list类型
类型介绍
列表类型 list 相当于 数组或者顺序表
list内部的编码方式更接近于 双端队列 ,支持头插 头删 尾插 尾删。
需要注意的是,Redis的下标支持负数下标。 比如数组大小为5,那么要访问下标为 -2 的值可以理解为访问 5 - 2 = 3
Redis中的数据是有序的,但是注意这里的有序不是指排序数组中的升序或者降序,而是指数据的顺序很关键。比如把元素位置调换之后,得到的新的list与原来的list是不等价的。
同样的一个词在不同的上下文中,它的意义可能是不一样的。
比如同步这个词在 线程之间和IO之间的意义就不同。
另外list还提供了其他的一些功能,如图:
关于lindex和lrem两个命令
在hash类型中,是不允许有重复数据的,也就是不能有重复的field。但是list可以有重复的值。
lpush / lrange
lpush
将⼀个或者多个元素从左侧放⼊(头插)到 list 中。
LPUSH key element [element ...]
lrange
获取从 start 到 end 区间的所有元素,左闭右闭。
LRANGE key start stop
谈到下标,那么往往就会关注超出范围的问题。
可以看到Redis的做法是尽可能的获取到指定区间的元素,如果给定区间不合法,比如超出下标,那么依然会尽可能的获取到对应的内容。
lrange这里的l不是指left 而是指 list。
扩展:一些超出下标范围的情况
对于C++ :会认为这是一个未定义的行为
对于Java:会抛异常
对于Redis:尽可能的获取到对应的内容。
lpushx / rpush / rpushx
lpushx
LPUSHX key element [element ...]
rpush
将⼀个或者多个元素从右侧放⼊(尾插)到 list 中。
这样依次插入的元素就是顺序的 ,lpush就是倒序的
RPUSH key element [element ...]
rpushx
在 key 存在时,将⼀个或者多个元素从右侧放⼊(尾插)到 list 中。
RPUSHX key element [element ...]
lpushx 和 rpushx 这里的x可以理解为exists。
lpop / rpop
lpop
从 list 左侧取出元素(即头删)。
LPOP key
rpop
从 list 右侧取出元素(即尾删)。
RPOP key
总结:
Redis的list其实就是一个双端队列,从两头插入/删除元素的效率都是 O(1):
搭配 lpush / lpop 可以当作一个队列使用
搭配 rpush/ rpop 可以当作一个栈使用
lindex / linsert / llen
lindex
获取从左数第 index 位置的元素。
LINDEX key index
linsert
在特定位置插⼊元素。
LINSERT key <BEFORE | AFTER> pivot element
因为list的结构不是数组,所以lindex和linsert都需要遍历找到位置,所以时间复杂度为O(N),这里的N指的就是list的长度,当list的长度不大时还好,如果list太长了,那么这两个命令的效率就低了。
llen
获取 list ⻓度。
LLEN key
另外之前说过,list中是允许有重复值的,那么在insert的时候,如果选择的基准值有重复的,那么是如何插入的呢?
可以看见,当基准值有重复值时,Redis会从左向右遍历,找到以第一个符合基准值的位置为主。
lrem
批量删除等于 value的值。
当count > 0时:从左往右删除 count个元素。
当count < 0时:从右往左删除 count 个元素 。
当count = 0时:删除所有符合要求的元素。
rem 其实时 remove的缩写。
图中演示的就是从左往右删除 2 个 值 = 1的元素。
ltrim / lset
ltrim
删除 除了 [left,right]区间之外的所有元素
ltrim key start stop
时间复杂度 O(N)
lset
根据下标来修改元素
lset key index element
时间复杂度O(N)
另外这里如果下标越界了,是会直接报错的,跟lindex那里会返回一个nil是不同的。
blpop / brpop (阻塞版本命令)
blpop/brpop key timeout
时间复杂度 O(1)
返回值:取出来的元素或者 nil(超时了)
还可以针对多个key进行操作的,返回最先取出来的二元组。
命令总结:
list内部编码
Redis老版本的方式
现在的redis是用一个quicklist的结构。
在redis的配置文件种,这个就是配置list的ziplist的最大长度的,这里的-2表示的是等级。
redis配置文件的默认路径
cd /etc/redis
list的应用场景
作为数组
list作为数组,可以储存多个元素。
在mysql下存储表结构
在Redis下存储
查询起来还是mysql的功能要丰富一些。
作为消息队列
分频道的消息队列
比如我们刷抖音,一个抖音界面有很多元素组成,比如视频,弹幕,点赞和评论等等,那么这些数据没必要由一个通道来传输,可以将这些元素分为多个通道传输。这样的好处是可以解耦合。
微博timeline
1)每篇微博使⽤哈希结构存储,例如微博中 3 个属性:title、timestamp、content:
hmset mblog:1 title xx timestamp 1476536196 content xxxxx
...
hmset mblog:n title xx timestamp 1476536196 content xxxxx
2)向⽤⼾ Timeline 添加微博,user:<uid>:mblogs 作为微博的键:
lpush user:1:mblogs mblog:1 mblog:3
...
lpush user:k:mblogs mblog:9
这里插入list中的是哈希表的索引。
3)分⻚获取⽤⼾的 Timeline,例如获取⽤⼾ 1 的前 10 篇微博:
keylist = lrange user:1:mblogs 0 9
for key in keylist {
hgetall key
}
但是这里会存在一些问题: