持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第5天,点击查看活动详情
列表里可以存储多个且有序的字符串,每个字符串称之为元素,在redis中每个列表最多可以存储2³²-1个元素。
列表是一个比较灵活的数据结构,可以充当栈和队列的角色,实际开发中有很多应用场景。
首先我们先看一看它的命令:
| 操作 | 命令 |
|---|---|
| 添加 | rpush lpush linsert |
| 查找 | lrange lindex llen |
| 删除 | lpop rpop lren ltrim |
| 修改 | lset |
| 阻塞 | blpop brpop |
详细分析:
1 命令
1.1 添加
在列表这个数据结构中,添加元素时可以从左边添加也可以从右边添加,也可以向某个指定位置添加
1.1.1 从右边插入元素
rpush key value可以向列表右侧添加元素
127.0.0.1:6379> rpush name a b c d
(integer) 4
此时可以使用lrange key 0 -1从左到右输出元素
127.0.0.1:6379> lrange name 0 -1
1) "a"
2) "b"
3) "c"
4) "d"
有关这个输出命令之后会说到
1.1.2 从左边插入元素
lpush key value可以向列表左侧添加元素
127.0.0.1:6379> lpush name:1 a b c d
(integer) 4
127.0.0.1:6379> lrange name:1 0 -1
1) "d"
2) "c"
3) "b"
4) "a"
这个命令和rpush基本相同只是从左侧插入了
1.1.3 向某个位置前或者后添加元素
linsert key before|after pivot value这个命令可以在元素pivot 前或后插入value值
下边这个命令就是在元素b前边添加元素luke
127.0.0.1:6379> linsert name before b luke
(integer) 5
127.0.0.1:6379> lrange name 0 -1
1) "a"
2) "luke"
3) "b"
4) "c"
5) "d"
可是要是pivot有多个会怎样呢?
127.0.0.1:6379> rpush name:3 a b c d b
(integer) 5
127.0.0.1:6379> linsert name:3 before b luke
(integer) 6
127.0.0.1:6379> lrange name:3 0 -1
1) "a"
2) "luke"
3) "b"
4) "c"
5) "d"
6) "b"
可以看到只给第一个元素b前添加了元素luke。
1.2 查找
当查找的时候,可以从左到右也可以从右到左,在redis中,从左向右查找的时候,第一个元素的下标是0,最后一个是N-1。从右向左查找的时候,是从-1到-N。也就是说第一个元素是0,最后一个是-1。
1.2.1 获取指定范围内的元素列表
上边我们使用了lrange key start end命令去获取list的值
127.0.0.1:6379> lrange name 0 -1
1) "a"
2) "luke"
3) "b"
4) "c"
5) "d"
这里0是指第一个元素,-1指最后一个元素,即这个命令是获取所有元素。
1.2.2 获取指定位置的元素
我们也可以获取指定下标的元素:lindex key index
例如我们从name中获取第二个元素,则获取到的值为b
127.0.0.1:6379> rpush name a b c d
(integer) 4
127.0.0.1:6379> lindex name 1
"b"
1.2.3 获取列表长度
llen key可以获取列表内共有多少个元素,即列表的长度
127.0.0.1:6379> llen name
(integer) 4
1.3 删除
1.3.1 从左侧弹出元素
这个弹出指的就是,取出第一个元素,并且其余元素的下标改变,之前的1位置变为0。
lpop key可以从列表左侧弹出一个元素,并且只能弹出第一个元素。
127.0.0.1:6379> lpop name
"a"
之前的name列表将变为
127.0.0.1:6379> lrange name 0 -1
1) "b"
2) "c"
3) "d"
1.3.2 从列表右侧弹出
从右侧弹出是rpop key吗,与左侧弹出相同,只是弹出的是最右侧的一个元素。
1.3.3 删除指定元素
lrem key count value会遍历整个列表,删除value值
根据count值的不同会有三种情况
- count > 0 从左到右删除count个value
- count < 0 从右到左删除count绝对值个value
- count = 0 删除所有与value值相同的元素
我们给name左边添加两个c
127.0.0.1:6379> lpush name c c
(integer) 5
127.0.0.1:6379> lrange name 0 -1
1) "c"
2) "c"
3) "b"
4) "c"
5) "d"
然后删除右边两个c
127.0.0.1:6379> lrem name -2 c
(integer) 2
127.0.0.1:6379> lrange name 0 -1
1) "c"
2) "b"
3) "d"
1.3.4 按照索引范围修剪列表
其实就是获取到指定位置的字符串,重新赋值给key
命令是:ltrim key start end
我们进行如下操作,
127.0.0.1:6379> rpush name a b c d
(integer) 4
127.0.0.1:6379> ltrim name 1 2
OK
127.0.0.1:6379> lrange name 0 -1
1) "b"
2) "c"
可以看到,name中的元素只剩原本下标1-2的值,b和c了
1.4 修改
修改就比较简单了,修改指定下标的元素,也就是修改指定位置的元素
命令是lset key index value,意思是修改index位置的值为value
127.0.0.1:6379> rpush name a b c d
(integer) 4
127.0.0.1:6379> lset name 1 luke
OK
127.0.0.1:6379> lrange name 0 -1
1) "a"
2) "luke"
3) "c"
4) "d"
1.5 阻塞
阻塞主要是阻塞式弹出,阻塞的意义在于,当没有获取到值的时候,会阻塞等待,无法执行其他命令
blpop key timeout从左侧阻塞式弹出
brpop key timeout从右侧阻塞式弹出
也就是说,客户端会等待timeout的时间获取结果,若是没有值,会等待timeout的时间,若是在这个时间内有值添加进来,则直接返回。若是没有值,则等待timeout的时间,然后返回nil。当timeout的值为0的时候,会一直阻塞等待值的添加。
分类说说
1.5.1 列表一直为空
就像下边的例子,当列表一直为空的时候,会等待3秒然后返回nil
127.0.0.1:6379> flushall
OK
127.0.0.1:6379> brpop name 3
(nil)
(3.08s)
1.5.2 列表不为空的时候会立即返回
127.0.0.1:6379> rpush name a b c d
(integer) 4
127.0.0.1:6379> brpop name 3
1) "name"
2) "d"
在brpop的时候,若是遍历了多个键,只要有一个key的value内有值则会直接返回。而且当多个客户端获取同一个key的元素的时候,只有第一个发起命令的客户端可以获取到值。
2 内部编码
列表类型的内部编码有两种
- ziplist
- 元素个数需要小于配置
list-max-ziplist-entries,默认是512个 - 每个元素的大小需要小于配置
list-max-ziplist-value,默认是64字节
- 元素个数需要小于配置
- linkedlist
- 不满足ziplist的条件时,会使用linkedlist的内部编码
这个和哈希类型的内部编码类似,都是由配置和元素个数和元素大小决定内部编码。ziplist的优点是占用空间更小。
3 使用场景
3.1 消息队列
消息队列遵循先入先出
lpush + brpop 则可以实现消息队列
从阻塞弹出就可以看出这个可以实现消息队列,消费者阻塞获取值,当生产者插入元素之后,立即返回并消费消息。
3.2 数据分页
因为列表不仅是有序的,而且还可以获取指定范围内的元素,这就很完美的契合了分页查询。
3.3 栈
lpush + lpop则可以实现栈
栈则和消息队列相反,是先入后出。
list类型的使用场景还是蛮多的,大家在工作中还有哪些更巧妙的使用场景呢?