使用理解基本API
redis提供了5种数据结构,理解每次数据结构的特点在开发和运维中是很重要的.使用redis版本:3.0.7
本章内容如下:
redis
列表redis
集合redis
有序集合
1.1 列表
列表(list
)类型是用来存储多个字符串,如下图A,B,C,D
四个元素从左到右组成一个有序的集合.列表中的每个字符串被称为元素(element
),一个列表最多可以存储(2的32次方)-1
个元素.在redis
中,可以对列表两端插入(push
)和弹出(pop
),还可以获取指定范围的元素列表、获取指定所有下标的元素等.
列表类型有两个特点:
列表中的元素是有序的,这就意味着可以通过索引下标获取某个元素或者某个范围内的元素列表.
列表中的元素可以是重复的.
1.1.1 命令
1) 插入命令
(1) 从右边插入元素.
rpush key value [value...]
例:
向列表插入a,b,c
三个元素.
lrange key 0 -1
命令可以获取列表中所有的元素.
(2) 从左边插入元素.
使用方法与rpush
一样,从左侧插入.
lpush key value [value....]
例:
2) 查询命令
(1) 查询指定范围内的元素列表
lrange key start end
lrange
操作会获取列表指定索引范围所有的元素.索引下标有两个特点:第一,索引下标从左到右分别是0
到N-1
,但是从右到左分别是-1
到-N
.第二,lrange
中的end
选项包含了自身.
例:
(2) 获取列表指定索引下的元素
lindex key index
例:
(3) 获取列表长度
llen key
例:
3) 删除命令
(1) 从列表左侧或右侧弹出元素.
如下操作是将列表最左侧与右侧的元素弹出来.
(2) 删除指定元素
lrem key count value
lrem
命令会从列表中找到等于value
的元素进行删除,根据count
的不同分为三种:
count>0
,从列表中删除指定数量(count
)的元素.count<0
,从列表中删除count
绝对值数量的元素.count=0
,删除所有.
例:
指定元素数量:
所有元素:
(3) 按照索引范围修剪列表
ltrim key start end
例如,下面操作会保留列表第二个到第六个元素:
4)修改命令
修改指定索引下标的元素:
lset key index value
例:
修改第一个元素的值为c
.
4) 阻塞操作
阻塞式弹出:
blpop key [key...] timeout
brpop key [key...] timeout
blpop
与brpop
命令是lpop
和rpop
命令的阻塞版本,他除了弹出方向不同,使用方法基本相同,所以下面以brpop
命令进行说明,brpop
命令包含两个参数:
key [key...]
:多个列表的键.timeout
:阻塞时间(单位为秒
).
1)列表为空:如果timeout
等于3
,那么客户端等到三秒后返回,如果timeout=0
,那么客户端将一直阻塞,直到弹出成功.
2)列表不为空:客户端会立刻返回.
在使用阻塞弹出命令时,有两点需要注意.
第一点:如果是多个键,那么会从左到右遍历键,一旦有一个键能弹出元素客户端就会立刻返回.
第二点:如果多个客户端同时对一个键进行操作,那么最先执行命令的客户端可以获取到值.
1.1.2 内部编码
列表类型的内部编码有两种:
编码名 | 编码描述 |
---|---|
ziplist (压缩列表) |
当列表的元素个数大于list-max-ziplist-entries 配置(默认为512个),同时列表中每个元素的长度小于list-max-ziplist-value 配置(默认为64字节). |
linkedlist (链表) |
当列表的长度或值得大小不满足ziplist 的要求,redis 会采用linkedlist 为列表的内部实现编码. |
1.1.3 使用场景
消息队列:
redis
的lpush-brpop
命令组合即可实现阻塞队列,生产者客户端使用lpush
命令向列表插入元素.消费者客户端使用brpop
命令阻塞式的"抢"列表中的尾部元素.多个客户端保证消息的负载均衡与可用性.文章列表:每个用户都有属于自己的文章列表.此时可以考虑使用列表,因为列表不但是有序的,同时支持使用
lrange
按照索引范围获取多个元素.
伪代码:
var articles = redis.lrange('user:1:acticles',0,9);
articles.forEach(function(){
// 遍历操作
});
开发提示:列表的使用场景有很多如:
lpush
+lpop
=Stack
(栈)、lpush
+rpop
=queue
(队列)、lpush
+brpop
=message queue
(消息队列)、lpush
+ltrim
=Capped Collection
(有限集合)
1.2 集合
集合(set
)类型也是用来保存多个的字符串元素,但和列表不同的是:它的元素是无序且不可重复的,不能通过索引获取元素.如下图,集合user:1:follows
中包含着"his"、"it"、"sports"、"music"四个元素,一个集合最多可以存储(2的32次方-1)个元素.
1.2.1 命令
1) 集合内操作
(1) 添加元素
sadd key value [value...]
返回结果为添加成功的元素数量.
例:
(2) 删除元素
srem key value [value...]
返回结果为删除成功的元素数量.
例:
(3) 获取元素个数
scard key
例:
(4) 判断元素是否在集合中
sismember key value
如果元素存在于集合内则返回1
,反之返回0
.
例:
(5) 随机从集合中返回指定个数元素
srandmember key [count]
[count]
是可选参数,如果不写默认为:1
.
例:
(6) 从集合中随机弹出元素
spop key
spop操作可以从集合中随机弹出一个元素.
例:
使用spop
命令后,集合元素变为"1","3".
(7) 获取集合的所有元素
smembers key
获取集合所有元素,且返回结果是无序的.
2) 集合间操作
(1) 求多个集合的交集
sinter key [key...]
例:
(2) 求多个集合的并集
sunion key [key...]
例:
(3) 求多个集合的差集
sdiff key [key...]
例:
(4) 将交集、并集、差集的结果保存.
sinterstore storeKey key [key...]
sunionstore storeKey key [key...]
sdiffstore storeKey key [key...]
集合间的运算在元素比较多的情况下会比较耗时,所以redis
提供了上面三个命令(原命令+store
)将集合间交集、并集、差集的结果保存到storeKey
中,例如将user:1:follows
和user:2:follows
两个集合之间的交集结果保存到user:1_2:follows
中.
1.2.2 内部编码
集合类型的内部编码有两种:
编码名 | 编码描述 |
---|---|
intset (整数集合) |
当集合中的元素全是整数,且长度不超过set-max-intset-entries (默认为512 个)时,redis 会选用intset 作为内部编码. |
hashtable (哈希表) |
当集合无法满足intset 的条件时,redis 会使用hashtable 作为内部编码. |
1.2.3 使用场景
集合类型比较典型的使用场景是标签(tag
).例如一个用户可能对音乐感兴趣,另一个用户对新闻感兴趣,这些想去点就是标签.有了这些数据就可以获得喜欢同一个标签的人,以及用户的共同喜好的标签,这些数据对于用户体验来说比较重要.
下面使用集合类型实现标签功能的若干功能.例如:
(1) 给用户添加标签:
(2) 给标签添加用户:
(3) 删除用户的标签:
(4) 删除标签的用户:3
到4
最好在同一个事务下进行处理.
(5) 计算用户共同拥有的标签:
1.3 有序集合
有序集合相对于哈希、列表、集合来说会有一点陌生,但既然叫有序集合.那么它和集合必然是有着联系,它保留了集合不能重复元素的特性.但不同的是,有序集合是可排序的.但是他和列表使用索引下标进行排序依据不同的是,它给每个元素设置一个分数(score
)作为排序的依据.
列表、集合、有序结合的异同点
数据结构 | 是否允许重复元素 | 是否有序 | 有序实现方式 | 应用场景 |
---|---|---|---|---|
集合 | 否 | 否 | 无 | 标签、社交等. |
有序集合 | 否 | 是 | 分值 | 排行榜、社交等. |
列表 | 是 | 是 | 索引下标 | 时间轴、消息队列等. |
1.3.1 命令
1)集合内
(1) 添加成员
zadd key score member [score member ...]
下面操作向有序集合user:ranking
增加用户Rico
和他的分数60
分:
有关zadd
命令有两点需要注意:
-
Redis 3.2
为zadd
命令添加了nx
、xx
、ch
、incr
四个选项:nx
:member
必须不存在,才可以设置成功,用于添加.xx
:member
必须存在,才可以设置成功,用于添加.ch
:返回此次操作后,有序结合元素和分数发生变化的个数.incr
: 对score
进行添加操作,相当于后面介绍的zincrby
.
有序集合相比集合提供了排序字段,但是也产生了代价,
zadd
的时间复杂度是O(log(n))
,sadd
的时间复杂度为O(1)
.
(2) 获取成员个数
zcard key
例:
(3) 获取某个成员的分数
zscore key member
例:
(4) 获取成员排名
zrank key member
zrevrank key member
zrank
命令是从分数从低到高返回排名,zrevrank
反之,排名从0
开始.例如下面:
(5) 删除成员
zrem key member [member...]
下面操作成员ann
从有序集合user:ranking
中删除.
(6) 增加成员分数
zincrby key score member
下面操作给Rico
成员添加9
分,分数变为69
分.
(7) 获取制定范围的元素
zrange key start end [withscores]
zrevrange key start end [withscores]
有序集合是按照分值排名的,zrange
是由低到高返回,zrevrange
反之,查询全部:zrange user:ranking 0 -1
,加上withscores
参数显示分数.
例:
获取前三名.
127.0.0.1:6379> zrange user:ranking 0 2
1) "codger"
2) "hank"
3) "ann"
127.0.0.1:6379> zrevrange user:ranking 0 2
1) "tom"
2) "Rico"
3) "ann"
(8) 返回指定分数范围的成员
zrangebyscore key min max [withscores] [limit offset count]
zrevrangebyscore key min max [withscores] [limit offset count]
例:
返回分数在0
到100
的成员.
127.0.0.1:6379> zrangebyscore user:ranking 0 100
1) "codger"
2) "hank"
3) "ann"
4) "Rico"
5) "tom"
返回分数在0
到无限大的成员.
127.0.0.1:6379> zrangebyscore user:ranking 0 +inf
1) "codger"
2) "hank"
3) "ann"
4) "Rico"
5) "tom"
同时min
和max
还支持开区间(小括号)和闭区间(中括号),-inf
和+inf
分别代表无限小和无限大:
127.0.0.1:6379> zrangebyscore user:ranking (10 +inf withscores
1) "hank"
2) "15"
3) "ann"
4) "30"
5) "Rico"
6) "69"
7) "tom"
8) "80"
(9) 返回指定分数范围成员个数
zcount key min max
下面返回分数为10
到15
的成员:
127.0.0.1:6379> zcount user:ranking (10 15
(integer) 1
(10) 删除指定排名内的升序元素
zremrangebyrank key start end
删除第start
到第end
名的成员:
127.0.0.1:6379> zremrangebyrank user:ranking 0 1
(integer) 2
(11) 删除指定分数范围的成员
zremrangebyscore key min max
删除分数从min
到max
的成员:
127.0.0.1:6379> zremrangebyscore user:ranking 0 30
(integer) 1
2) 集合间的操作
(1) 交集
zinterstore storeKey keyNum key [key ...] [weights weight [weight...]] [aggregate sum|min|max]
参数说明:
storeKey
:交集计算结果保存到这个键下.keyNum
:需要做交集的键的个数.key[key ...]
:需要做交集的键.weights weight [weight...]
:每个键的权重,在做交集计算时,每个键中的每个member
的分值会和这个权重相乘,每个键的权重默认为1
.aggregate sum|min|sum
:计算成员交集后,分值可以按照sum
(和)、min
(最小值)、max
(最大值)做汇总.默认值为sum
.
127.0.0.1:6379> zrange user:ranking:2 0 -1 withscores
1) "Rico"
2) "138"
3) "tom"
4) "160"
127.0.0.1:6379> zinterstore user:ranking:1_2 2 user:ranking user:ranking:2 aggregate min
(integer) 2
127.0.0.1:6379> zrange user:ranking:1_2 0 -1
1) "Rico"
2) "tom"
127.0.0.1:6379> zrange user:ranking:1_2 0 -1 withscores
1) "Rico"
2) "69"
3) "tom"
4) "80"
(2) 并集
zunionstore storeKey keyNum key [key...] [weights weight [weight...]] [aggregate sum|min|max]
该命令的所有参数和zinterstore
是一致的,只不过做的是并集计算.
例:
127.0.0.1:6379> zunionstore user:ranking:1_2 2 user:ranking user:ranking:2 aggregate min
(integer) 3
127.0.0.1:6379> zrange user:ranking:1_2 0 -1 withscores
1) "Rico"
2) "69"
3) "codger"
4) "90"
5) "tom"
6) "160"
1.3.2 内部编码
编码名称 | 编码描述 |
---|---|
ziplist (压缩列表) |
当有序集合的元素小于zset-max-ziplist-entries 配置(默认是128个),同时每个元素的值都小于zset-max-ziplist-value (默认是64字节)时,Redis 会用ziplist 来作为有序集合的内部编码实现,ziplist 可以有效的减少内存的使用 |
skiplist (跳跃表) |
当ziplist 的条件不满足时,有序集合将使用skiplist 作为内部编码的实现,来解决此时ziplist 造成的读写效率下降的问题. |
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。