redis入门指南第二版读书笔记
作者:李子骅
读者:张剑
Redis是以高性能著称的内存数据库,通常用来做缓存服务器。通常人们把它当作memcached的替代品。
同时围绕Redis也形成了很多产品工具链,Redis的命令也成了一种协议,有些模仿者使用同样的命令但是在某些方面有所侧重。
通过replication/persistentce和client-side sharding等特性,用户可以很方便地将Redis扩展成数百GB数据每秒处理上百次请求的系统。
Redis的5种数据结构
我们把Redis归类为内存NOSQL Key—Value数据库。
Redis可以存储key与五种不同类型的value。
Value的五种类型分别是String,List,Set,HashSet,Zset。
特别说明:
String 中存放的是一个元素,可改变或追加其内容(json对象可以压缩为二进制字符串保存)
List Set 中存放的是一组元素,List以双向链表方式存储元素,Set是无序不可重复的元素集合
Hash 中存放的是一组key-value元素,可看成Map的集合
Sorted Set 中存放的是一组元素,不同的是每个元素都会关联一个double类型的score
1.简单类型 String
get,set,del
2.链表类型 List 可重复 有序
lpush和rpush 加入到list的头和尾
lpop和rpop 从list的头和尾出栈
lindex 根据指定位置获取item
lrange 获取某个范围内的items
eg:
lrange list-key 0 -1 取出list-key中的所有values
利用List可以有几种变化,比如可以当作堆栈先进后出,也可以当作队列先进先出
lpush和lpop结合使用就是堆栈
lpush和rpop结合使用就是队列
3.Set类型 Set 不可重复 无序
sadd 添加元素
smembers 返回所有元素
sismember 查看元素是否在set中
srem 如果存在,则删除该元素
说明:
以上3种类型存放的都是元素
4.Hash 类型 HashSet 存放 key-value 组
hset 在Hash表中存放key-value
hget 在Hash表中根据指定key获取value
hgetall 返回Hash表中的所有key-value组
hdel 如果存在,根据key删除value
eg:
可以一次性设置多个key-value组
hset hash_set_demo1 sub-key1 value1 sub-key2 value2
hget hash_set_demo1 sub-key1
hgetall hash_set_demo1
hdel hash_set_demo1 sub-key2
5.Sorted Sets类型 zset 排序的Set
zset-key zset
element score
Jack 80
Cooper 100
zadd 添加一个元素到zset中
zrange 从zset中获取排序之后的多个元素
zrangebyscore 获得按分数排序后的元素
zrem 如果存在,则从zset中删除元素
eg:
zadd zset-key 60 Jack 80 Cooper 100 David
127.0.0.1:6379> zrange zset-key 0 -1 withscores
1) "Jack"
2) "60"
3) "Cooper"
4) "80"
5) "David"
6) "100"
127.0.0.1:6379> zrange zset-key 0 -1
1) "Jack"
2) "Cooper"
3) "David"
127.0.0.1:6379> zrangebyscore zset-key 85 100 withscores
1) "David"
2) "100"
127.0.0.1:6379> zrem zset-key Jack
(integer) 1
127.0.0.1:6379> zrem zset-key Cooper
(integer) 0
127.0.0.1:6379> zrange zset-key 0 -1 withscores
1) "David"
2) "100"
Redis的核心内容
1. 5种数据类型的核心命令
String类型
存放3种类型的value
字节(数组)值
整数
浮点数
说明:
整数有自增和自减操作
incr/decr 整数自加1(自减1)
incrby/decrby key byNum 整数自增(自减)指定值byNum
incrbyfloat key amount 浮点数自增指定值amount
List类型 栈和队列类型,允许两头进行出栈和入栈
lpush和rpush 加入到list的头和尾
lpop和rpop 从list的头和尾出栈
lindex 根据指定位置获取item
lrange key start end 获取某个范围内的items
ltrim key start end 剔除list中索引不在start ~ end 之间的元素
Set类型 存放无序不可重复的元素
sadd key item 添加元素
srem key item 删除元素
sismember key item 查看item是否在set中
scard key 返回set中元素数量
smembers 返回set中所有元素
srandmember key [count] 从set中返回一个或count个随机元素
spop key 从set中删除并返回一个随机元素
smove source-key dest-key item 将item元素从source-key 移到dest-key
两个set类型的操作
sdiff key1 [key2...] 返回在key1中而不再其他key中的元素
sdiffstore
sinter key1 [key2...] 返回所有key都有的元素
sinterstore
sunion key1 [key2...] 返回多个key中的并集
sunionstore
Hash类型 存放若干key-value,看做存放map的集合
hmget hash key [key...] 从hash中根据多个key获取对应的value
hmset hash key value [key value ...] 在hash中设置多个key及其对应的value
hdel hash key [key...] 从hash中根据key删除
hlen hash key 返回hash中的map元素数量
hexists hash key 对应的key是否存在hash中
hkeys hash 获取hash中所有的key
hvals hash 获取hash中所有的value
hgetall hash 获取hash中所有的key-value键值对
hincrby hash key 如果key中存储的value是int,则value+1
Sorted Set类型 zset 存放排序的元素
zadd key-name score member [score member ...] 增加一个元素,并给元素一个对应的score
zrem key-name member [member ...] 删除一个或多个元素
zcard key-name 返回zset中元素数量
zincrby key-name incrment member
zcount key-name min max 返回score在min与max之间的元素,保护member 与其 对应的 value
zrank key-name member 返回member元素在zset中的位置
zscore key-name 返回zset中所有元素数量
zrange key-name start stop 返回score在min与max之间的元素
2. 事务
和一般的关系数据库类似,redis也支持事务。
但是redis的事务还是有所区别:
a.不能回滚,
b.即使其中一个命令有了运行错误(非语法错误),也不会影响到同一事务中其他命令的执行。
c.事务中的每个命令的执行结果都是最后一起返回的,所以无法将前一条命令的结果作为下一条命令的参数
结合WATCH命令
WATCH 命令可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行。监控一直持续到 EXEC 命令
redis> MULTI
OK
redis> SADD "user:1:following" 2
QUEUED
redis> SADD "user:2:followers" 1
QUEUED
redis> EXEC
1) (integer) 1
2) (integer) 1
3.过期时间
过期时间经常在访问频率控制和实现缓存中使用
EXPIRE session:29e3d 900
4.消息通知
利用消息通知,可以实现任务队列,优先级队列,发布/订阅模式
这里就用到了list的阻塞弹出命令,不过下面没有提到在消息通知和任务队列里面常用的弹出推入命令(RPOPLPUSH)。
a.任务队列
BRPOP命令和RPOP命令相似,唯一的区别是当列表中没有元素时BRPOP命令会一直阻塞住连接,直到有新元素加入。
示例代码:
loop
# 如果任务队列中没有新任务,BRPOP 命令会一直阻塞,不会执行 execute()。
$task = BRPOP queue, 0
# 返回值是一个数组(见下介绍),数组第二个元素是我们需要的任务。
execute($task[1])
b.优先级队列
BRPOP 命令可以同时接收多个键,其完整的命令格式为 BLPOP key [key …] timeout,如 BLPOP queue:1 queue:2 0。意义是同时检测多个键,如果所有键都没有元素则阻塞,如果其中有一个键有元素则会从该键中弹出元素。
示例代码:
loop
$task =BRPOP queue:confirmation.email,
queue:notification.email, 0
execute($task[1])
这时一旦发送确认邮件的任务被加入到 queue:confirmation.email 队列中,无论queue: notification.email还有多少任务,消费者都会优先完成发送确认邮件的任务。
c.发布/订阅模式
通过两个命令实现发布/订阅模式
PUBLISH
SUBSCRIBE
5.管道pipeline
管道和事务类似,不过管道的主要目的是为了减少来回通信次数,也就节省了来回的通信时间。
Redis 的底层通信协议对管道(pipelining)提供了支持。通过管道可以一次性发送多条命令并在执行完后一次性将结果返回,当一组命令中每条命令都不依赖于之前命令的执行结果时就可以将这组命令一起通过管道发出。管道通过减少客户端与 Redis 的通信次数来实现降低往返时延累计值的目的
6.排序
ALPHA参数
LIMIT参数
DESC/ASC参数
by参数
get参数
下面是sort在各种数据结构的使用实例
$array_tag_ruby_posts = array(
'2', '11', '6', '12'
// , '26', '28'
);
$redis->del('tag:ruby:posts');
$redis->lpush('tag:ruby:posts', $array_tag_ruby_posts);
echo '----------sort1--------';
echo '
';
print_r($redis->lrange('tag:ruby:posts', 0, -1));
$option1_sort = array(
// 'by' => 'weight_*',
// 'get' => array('value_*', '#'),
'sort' => 'desc',
// 'alpha' => true,
'limit' => array(1, 3),
'store' => 'result');
$redis->sort('tag:ruby:posts', $option1_sort);
echo '
';
//print_r($redis->type('result'));
print_r($redis->lrange('result', 0, -1));
echo '
----------sort2--------
';
$array_sortbylist = array(2, 1, 3);
$redis->del('sortbylist');
$redis->lpush('sortbylist', $array_sortbylist);
print_r($redis->lrange('sortbylist', 0, -1));
echo '
';
$redis->set('itemscore:1', 50);
$redis->set('itemscore:2', 100);
$redis->set('itemscore:3', -10);
$option2_sort = array(
'by' => 'itemscore:*',
// 'get' => array('value_*', '#'),
'sort' => 'desc',
// 'alpha' => true,
// 'limit' => array(1, 3),
'store' => 'result2'
);
$redis->sort('sortbylist', $option2_sort);
print_r($redis->lrange('result2', 0, -1));
echo '
----------sort3--------
';
$redis->del(array('post:2', 'post:6', 'post:11', 'post:12', 'post:26', 'post:28'));
$post2 = array(
'title' => 'Windows 8 app designs',
'time' => '1352620100'
);
$post6 = array(
'title' => 'RethinkDB - An open-source distributed database built with love',
'time' => '1352620000'
);
$post11 = array(
'title' => 'The Nature of Ruby',
'time' => '1352619200'
);
$post12 = array(
'title' => 'Uses for cURL',
'time' => '1352619600'
);
$redis->hmset('post:2',$post2);
$redis->hmset('post:6',$post6);
$redis->hmset('post:11',$post11);
$redis->hmset('post:12',$post12);
$option3_sort = array(
'by' => 'post:*->time',
'get' => array('post:*->title','post:*->time', '#'),
'sort' => 'desc',
// 'alpha' => true,
// 'limit' => array(1, 3),
'store' => 'result3'
);
$redis->sort('tag:ruby:posts',$option3_sort);
print_r($redis->lrange('result3',0,-1));
7.节省空间
a.命名简化
b.内部编码优化
本书里面没有的内容
1.Redis HyperLogLog起始版本:2.8.9
Redis HyperLogLog是用来做基数统计的算法。优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。
在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。
注:因为HyperLogLog只会根据输入元素来计算基数,而不会存储输入元素本身,因此不会返回输入的各个元素。
基数是什么? 对于["abc", "abc", "2", "3"],基数是["abc", "2", "3"],个数是3.
HyperLogLog通过下面三个命令:
pfadd key ele [ele2 ...]:添加指定元素到HyperLogLog中,
pfcount key: 返回给定HyperLogLog的基数估算值
pfmerge destkey srckey [srckey2....]:讲多个HyperLogLog合并到一个第一个HyperLogLog中