redis实验环境
针对学习redis 我们可以为您提供一套完善的学习环境
点击此处跳转
章节叙述
本章节我们介绍一下List
类型,列表(List
) 类型是用来存储多个有序的字符串, 如图所示, a、b、 c、 d、 e五个元素从左到右组成了一个有序的列表, 列表中的每个字符串称为元素(element
) , 一个列表最多可以存储232-1个元素。 在Redis
中, 可以对列表两端插入(push
) 和弹出(pop
) , 还可以获取指定范围的元素列表、 获取指定索引下标的元素等。 列表是一种比较灵活的数据结构, 它可以充当栈和队列的角色, 在实际开发上有很多应用场景。
列表类型有两个特点:
- 列表中的元素是有序的;
- 列表中的元素是可以重复的;
常用命令
我们先按照对 列表的5种操作类型 ,对所有的api
进行了一个分类,然后按照分类进行介绍
操作类型 | 操作 |
---|---|
增加 | rpush 、lpush 、linsert |
查 | lrange 、lindex 、llen |
删除 | lpop 、rpop 、lrem 、ltrim |
修改 | lset |
阻塞操作 | blpop 、brpop |
-
1,添加操作
首先我们了解到,
Redis
中的List
是一个有序的列表,我们可以在列表的右侧,左侧,或者某个元素的位置上进行插入,相对的Redis
给我们提供了以上三个场景的命令,分别是rpush
、lpush
、linsert
三个命令,我们依次来演示一下-
1,
rpush
从右侧新增元素(支持单个或者多个),该命令会返回当前所有元素的个数,此时新建的元素在队列的末尾:rpush list1 value1 value2 value3
您可以通过
lrange list1 0 -1
指令来查询列表:lrange list1 0 -1
可以看到新插入的数据在列表的末尾。
-
2,
lpush
从左侧新增元素(支持单个或者多个),该命令会返回当前所有元素的个数,此时新增的元素在队列的开头位置:lpush list1 left1 left2
我们再来查询一下列表:
lrange list1 0 -1
可以发现新增的元素按照插入时间的倒叙在列表的开头位置。
-
3,
linsert
是在指定元素的前或者后插入元素,该命令会返回当前所有元素的个数。首先我们先在上述list1
的value1
前方插入一个元素linsert1
linsert list1 before value1 linsert1
再次查询列表:
lrange list1 0 -1
可以看到
linsert1
确实位于位于value1
的上方我们再向上述
list1
的value1
后方插入一个元素linsert1
linsert list1 after value1 linsert1
再查询一下:
lrange list1 0 -1
如您所见
value1
的下方也出现了一个linsert1
,这也只能的List
中的元素是可以重复的
-
-
2,查找
Redis
提供了多种检索List
列表的方法,同上述我们使用到的lrange
检索指定范围内的元素列表,还提供了获取指定索引元素的方法lindex
,以及查询List
列表长度的方法llen
,下述我们依次介绍一下-
1,
lrange
获取指定范围内的元素列表 这个方法我们在上边已经使用过了,我们来解析一下刚刚使用的命令
lrange list1 0 -1
其中list1
是查询的指定键,后面跟的两个参数分别是开始的索引start
,和结束的索引end
。Redis
中的索引下标有两个特:第一, 索引下标从左到右分别是0
到N-1
, 但是从右到左分别是-1
到-N
;第二,lrange
中的end
选项包含了自身, 这个和很多编程语言不包含end
不太相同。 例如想获取列表的第2到第4个元素, 可以执行如下操作:lrange list1 1 3
-
lindex
可以获取指定索引的元素这个方法简单,就不赘述了,我们来一个示例
lindex list1 1
-
llen
这个方法可以获取列表长度这个方法同我们前面课程介绍的
dbsize
一样获取的系统参数,并不是检索的内容,时间复杂度为0(1)llen list1
-
-
3,删除
同样的
Redis
也提供了多重删除List
中元素的方法分别是:lpop
从左侧弹出,rpop
从右侧弹出,lrem
删除指定元素,ltrim
删除指定范围内的元素。老规矩我们还是依次来介绍一下:-
1,
lpop
从列表的左侧弹出元素,可以指定弹出元素的个数,此结果会返回删除的元素值先查询当前的列表
lrange list1 0 -1
执行指令
lpop list1 1
再次查询列表:
lrange list1 0 -1
-
2,
rpop
从列表的右侧弹出元素,同样可以指定弹出元素的个数rpop list1 1
查询列表:
lrange list1 0 -1
可以发现在列表的末尾部分,少了一个
-
lrem
删除指定元素lrem
可以删除指定的元素,它的语法是这样的lrem key count value
这个命令会从列表中找到等于value的元素进行删除, 根据count的不同分为三种情况:- count>0, 从左到右, 删除最多count个元素。
- count<0, 从右到左, 删除最多count绝对值个元素
- count=0, 删除所有。
例如:新建一个列表中从左向右插入5个a和两个b。
lpush list2 a a a a a b b
通过查询可以得知那么当前列表变为“ b b a a a a a”:
lrange list2 0 -1
下面操作将从列表左边开始删除4个为a的元素:
lrem list2 4 a
随后我们再次查询列表,看看是否和我们猜想的一样变成了
"b b a"
lrange list2 0 -1
-
修改操作
Redis
提供了修改指定索引下标元素的方法lset list1 0 newval
-
阻塞操作
blpop key [key ...] timeout brpop key [key ...] timeout
阻塞式弹出有两个命令
blpop
和brpop
这个两个方法是lpop
和rpop
的阻塞版本,它们除了弹出方向不同之后,使用方法基本相同,下面以blpop
为例子 首先解释一下上述语法:这个方法就是当给定的列表内没有任何元素可供弹出的时候,连接将被
BLPOP
命令阻塞,直到等待超时或发现可弹出元素为止。- key [key …] 多个列表的键
- timeout 阻塞时间(单位秒)
我们来一个简单的例子实践一下
首先有两种情况(列表为空和列表不为空的情况)
我们先创建一个列表
lpush list3 val3
可以查看一下当前
list3
中有一个元素val3
lrange list3 0 -1
我们执行一下
BLPOP
命令让他阻塞10秒blpop list3 10
我们可以发现当列表不为空的时候,它会立即返回结果,并没有进入阻塞等待,返回值为,指定的
key
以及弹出的值。我现在再来看一下list3
的情况lrange list3 0 -1
可以发现当前的
list3
是空的,这时候我们再执行BLPOP
命令:blpop list3 10
可以看到它在阻塞了10秒之后返回了空,在这期间如果我们插入了元素,那么它会停止阻塞,立即返回,我们来试一下。
首先我们还是使它处理阻塞等待状态,这次时间稍微长点设置30秒
blpop list3 30
首先打开新的
terminal
窗口点击我打开使用指令给
list3
新建一个元素redis-cli lpush list3 new
随后切换为成我们原先的
terminal
窗口可以看到,命令并没有阻塞等待30秒自动返回,而是立即返回了我们刚刚新增的new
元素
-
内部编码
接下来我们来了解一下List
类型的内部编码。
-
ziplist
(压缩列表):当列表的元素个数小于list-max-ziplist-entries
配置(默认512个),同时列表中每个元素都小于list-max-ziplist-value
配置时(默认64字节),redis
会选取ziplist
来作为列表的内部实现来减少内存的使用。 -
linkedlist
(链表):当列表类型无法满足ziplist
的条件是,Redis
会使用linkedlist
作为列表的内部实现。Redis 3.2
版本提供了quicklist
内部编码,简单地说它是一个ziplist
为节点的linkedlist
,它结合了ziplist
和linkedlist
两者的优势,为列表类型提供了一种更加优秀的内部编码实现,它的设置原理可以参考Redis
的另一个作者Matt Stancliff
的博客: https://matt.sh/redis-quicklist
使用场景
-
消息队列
Redis
的lpush + brpop
命令组合即可实现阻塞队列,生产者客户端使用lrpush
从列表左侧插入元素,多个消费者客户端使用brpop
命令阻塞式的“抢”列表尾部的元素,多个客户端保证了消费的负载均衡和高可用性。 -
文章列表
对于热点数据的文章列表可以考虑使用
Redis
的List
类型存储,因为列表不但是有序的,同时支持按照索引范围获取元素。可能存在问题如下- 如果每次获取的文章个数较多,需要执行多次
hgetall
操作 ,此时可以考虑使用 pipeline (管道)批量获取 lrange
命令在列表两端性能较好,但是如果列表较大,获取列表中间范围的元素性能会变差,此时可以考虑将列表作二级拆分,或者使用Redis3.2
的quicklist
内部编码实现,它结合ziplist
和linkedlist
的特点,获取列表中间范围的元素时也可以高效完成。
- 如果每次获取的文章个数较多,需要执行多次
实际上列表的使用场景很多,下述是指令组合能达到的 类似数据类型特点
-
lpush + lpop
=Stack
(栈) -
lpush + rpop
=Queue
(队列) -
lpush + ltrim
=Capped Collection
(有限集合) -
lpush + brpop
=Message Queue
(消息队列)