列表类型
列表类型list存放的一个有序的字符串列表,内部是使用双向链表实现的,可以向两端添加元素,并且获取接近两端的元素速度越快,链表的代价是通过索引访问元素比较慢。这种特点,可以作为队列来使用。
命令
1. 向两端添加元素
lpush key value[value ...]
rpush key value[value ...]
2. 从链表两端弹出元素
lpop key
rpop key
结合上面的四个命令可以使用列表类型来模拟栈和队列的操作,如果想要把列表作为栈(先进后出),则搭配lpush,lpop(rpush,rpop),如果想要将列表作为队列(先进先出),则搭配lpush,rpop(rpush,lpop)操作。
并且,当列表类型中的所有元素全部弹出后,这个键会清除
3. 获取列表中元素的个数 llen key (当键不存在,返回0)
4. 获取列表片段 lrange key start stop
返回索引从start到stop间的所有元素,起始索引为0,并且索引支持负索引,表示从右边开始计算,-1表示最右边第一个元素,-2表示最右边第二个元素
lrange key 0 -1 获取的就是所有的元素
注意:1. 如果start索引位置比stop的索引位置靠后,会返回空
2. 如果stop大于实际的索引范围,则会返回列表最右边的元素
5. 删除列表中指定的值
lrem key count value
lrem命令删除列表中前count个值为value的元素,返回值是实际删除的元素个数,根据count值的不同,lrem命令的执行方式略有不同。
1. count>0 从列表左边删除前count个值为value的元素
2. count<0 从列表右边删除前count个值为value的元素
3. count =0 删除所有value的元素
6. 获取/设置指定索引的元素值(如果将列表类型作为数组使用,lindex命令是必不可少的)
lindex key index / lset key index value
lindex命令返回指定索引的元素,索引从0开始
lindex numbers 0
如果index是负数表示从右边开始计算的索引,最右边元素的索引是-1
lindex numbers -1
lset 将索引为index的元素赋值为value
2. 只保留列表指定片段
ltrim key start end 删除指定索引范围外的所有元素,指定范围的方法和lrange命令相同。
ltrim命令常和lpush命令一起使用来限制列表中元素的数量,比如记录日志时候,只希望保留最近的100条记录,则每次加入新元素的时调用一次ltrim命令.
lpush logs $newlog
ltrim logs 0 99
7. 向列表中插入元素
linsert key before| after pivot value
linsert命令首先在列表中从左到右查找pivot元素,然后根据before或after决定放在前面还是后面
8. 将元素从一个列表转移到另一个列表
rpoplpush source destination (原子操作)
实践
1. 存储文章id列表
使用列表类型posts:list记录文章id列表,使用lpush命令将新文章id加入到这个列表中,另外删除也要将id删除掉 lrem posts:list 1 要删除的文章ID, lrange命令来分页文章列表
另外使用列表类型键存储文章ID有两个问题
1> 文章的发布时间不易修改,修改文章的发布时间不仅要修改post:文章ID中的time字段,还需要按照实际的发布时间重新排列posts:list中的元素顺序
2> 局限于底层实现链表的特性,当列表中的元素非常多是访问中间的元素效率不高。
2. 存放评论列表
总结:
1. 列表类型作为栈和队列使用 2. 列表类型作为数组使用
集合类型
集合类型的常用操作是向集合中加入或删除元素,判断某个元素是否存在等,由于集合类型在redis内部使用散列表实现的,这些操作的时间复杂度都是O(1),最方便的是多个集合类型键之间可以进行并集,交集和差集运算。
集合类型 | 列表类型 | |
存储内容 | 至多2的32次方减1个字符串 | 至多2的32次方减1个字符串 |
有序性 | 否 | 是 |
唯一性 | 是 | 否 |
命令
1. 增加/删除元素
sadd key member [member ...] 如果键不存在,自动创建,并且在一个集合中不能有相同的元素,如果集合中已经存在,就会忽略这个元素
srem key member [member ...] 删除一个或多个元素,返回删除的个数
2. 获取集合中所有元素
smembers key
3. 判断元素是否在集合中
sismember key member 当值存在时返回1,值不存在或键不存在返回0
4. 集合间运算
sdiff key1 [key2 ...] 差集 取得是属于key1的集合部分
sinter key[key ...] 交集
sunion key[key ...] 并集
实践
1. 存储文章标签
使用集合类型键存储标签适合需要单独增加或删除标签的场景
2. 通过标签搜索文章
具体做法是为每一个标签使用一个名tag:标签名称:posts的集合类型存放文章id列表
命令拾遗
1. 获取集合中元素的个数 scard key
2. 进行集合运算并将结果存储
sdiffstore destination key [key ...]
sinterstore destination key [key ...]
sunionstore destination key [key ...]
3. 随机获得集合中的元素
srandmember key [count] 没有count,默认随机从集合中获取一个元素
1> count为正数时,从集合中获取count个不重复的元素
2> count为负数时,从集合中获取
4. 从集合中弹出一个元素 spop key 从集合中随机选择一个元素弹出
有序集合类型
有序集合类型的特点从他的名字可以看到,和集合类型的区别就是有序
在集合类型的基础上有序集合为集合中的每个元素都关联了一个分数,这使得不仅完成插入、删除和判断元素是否存在等集合类型支持的操作,还能够获得分数最高或最低的前N个元素,获得指定分数范围内的元素等有关操作。
有序集合类型在某些方面和列表类型有些相似。
1> 都是有序
2> 都可以获得某一范围的元素
但是二者有很大的区别,使得他们的应用场景不同
1> 列表类型是通过链表实现的,获取靠近两端的数据速度极快,而当元素增多后,访问中间数据的速度会较慢,所以它更加适合实现比如"新鲜事"或"日志"这样很少访问中间元素的应用。
2> 有序集合类型是使用散列表和跳跃表实现的,所以即使读取位于中间部分的数据也很快
3> 列表中不能简单低调整某个元素的位置,但是有序集合可以(通过更改这个元素的份上)
4> 有序集合笔列表类型更耗费内存
命令
1. 增加元素 ZADD key score member [score member...]
ZADD命令用来向有序集合中加入一个元素和改元素的分数,如果该元素已经存在则会用新的分数替换原有的分数。ZADD命令返回值是新加入到集合中元素的个数(不包含已经存在的元素)
有序集合模拟计分板
记录Tom,Peter,David三名运动员的分数分别是89,67,100
ZADD scoreboard 89 Tom 67 Peter 100 David
更新Peter分数 ZADD socreboard 87 Peter
分数不仅可以是整数,还支持双精度浮点数
2. 获取元素的分数 ZSCORE key member
3. 获取排名在某个范围的元素列表 ZRANGE/ZREVRANGE
ZRANGE key start stop [withscores]
ZREVRANGE key start stop [withscores]
ZRANGE命令按照分数从小到大的顺序返回索引从start到stop之间的所有元素(包括两端的元素),ZRANGE和LRANGE命令相似,如索引都是从0开始,负数代表从后向前查找(-1表示最后一个元素)
如果需要同时获取元素的分数的话可以在ZRANGE命令的尾部加上WITHSCORES参数,这时返回的数据格式从
"元素1,元素2..." 变为了"元素1,分数1,元素2,分数2..."
ZRANGE命令如果返回的分数相同,按照字典顺序(0<9<A<Z<a<z)顺序来排列,中文排序则是取决于中文的编码格式
ZRERANGE 命令则是按照分数从大到小的顺序给出结果的。
4. 获得指定分数范围的元素 ZRANGEBYSCORE/ZRERANGEBYSCORE
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
改命令按照元素分数从小到大返回分数在min和max间的元素 如果要返回分数 加上 withscores
limit offset count 和sql类似, 返回偏移量开始的count个元素
note:
1. 如果希望分数范围不包含端点值,可以在分数前加上 (,例如: ZRANGEBYSCORE scoreboard 80 (100 返回分数 包含80 不包含100 之间的元素
2. -inf ,+inf 表示负无穷大和正无穷大。返回大于80不包含80的分数 ZRANGEBYSCORE scoreboard (80 +inf
如果想要获取分数小于等于100的前3个元素,该怎么做呢:
ZREVRANGEBYSCORE命令,不仅max 和 min顺序是相反的,而且 返回的结果是从大到小的顺序返回的
ZREVRANGEBYSCORE scoreboard 100 limit 0 3
5. 增加某个元素的分数 ZINCREBY KEY INCRMENT MEMBER (INCREMENT为正数表示增加,负数表示减少)
返回的结果是增加/减少后的分数
如果指定的元素不存在,redis会在执行命令前建立并将分数置0在执行操作。
实践
1.实现按点击量排序
要按照点击量排序,就必须在额外使用一个有序集合类型的键来实现。在这个键中以文章的ID作为元素,以改文章的点击量作为元素的分数。
将改键命令为posts:page.view,每次用户访问一篇文章时,博客程序就通过ZINCRBY posts:page.view 1 文章id更新访问量。如果需要按照点击量的顺序显示文章列表,zrange命令来实现
2. 改进按时间排序
原来每次发布新文章时,都将文章的ID加入到名为posts:list的列表类型的键中来获取按照时间顺序排列的文章列表。由于改动列表类型元素的顺序麻烦,博客系统都支持更改文章的时间
可以使用有序集合类型来替换列表类型。 自然的元素仍然是文章id,分数取得是发布的时间UNIX时间。借助ZREVRANGEBYSCORE命令可以获取时间范围的文章id列表。
命令拾遗
1. 获取集合中元素的数量 ZCARD KEY
2. 获取指定分数范围内的元素个数 ZCOUNT key min max (min,max同样也支持-inf,+inf,(的特性 )
3. 删除一个或多个元素 ZREM key member [member ...] 返回删除成功的元素数量
4. 按照排名范围删除元素 ZREMRANGEBYRANK key start stop
ZREMRANGEBYRANK命令按照分数从小到大的顺序(索引0表示最小的值)删除指定排名范围内的所有元素
,并返回删除的元素数量
5. 按照分数范围删除元素 ZREMRANGEBYSCORE key min max
min,max 支持 -inf, +inf ,( 特性不在赘述
6. 获取元素的排名
ZRANK key member 按照分数从小到大的顺序获取指定元素的排名(从0开始,分数最小的为0)
ZREVRANK key member 反之
7.计算有序集合的交集
ZINTERSTORE destination numkeys key[key ... ] weights weight [weight...] [ARRREGATE SUM|MIN|MAX]
destination键中元素的分数是有AGGREGATE参数决定的。
1> AGGREGATE 是SUM(也就是默认值),destination键中元素的分数是每个参与计算集合中元素分数的和
ZADD board 1 a 2 b / ZADD board 10 a 20 b
ZINTERSTORE test 2 borad1 board2 AGGREGATE SUM,这样ZRANGE test 0 -1 withscores
结果: 11 a 22 b
weights key1权重的值 key2的值 设置权重,也就是参与运算前,分数先要乘上权重。
ZINTERSTORE test 2 borad1 board2 weights 1 0.1 AGGREGATE SUM,这样ZRANGE test 0 -1 withscores
结果: 2 a 4 b