Redis 数据类型:List 列表的深度解析与应用

前言

Redis 中的 List 类型是一种灵活的数据结构,其功能类似于编程语言中的数组或顺序表,但其内部实现更接近于双端队列 (deque)。这使得在列表的头部和尾部进行元素的插入和删除操作都非常高效。

List 类型的主要特性包括:

  • 有序性:列表中的元素是按照插入顺序排列的。

  • 可重复性:与 Set 不同,List 允许存储重复的元素。

  • 高效的头尾操作:通过 lpush、rpush、lpop 和 rpop 等命令,可以快速地在列表两端添加或移除元素,因此常被用作栈或队列。

Redis 的 List 类型是实现消息队列等模式的基石,在 Redis 早期版本中,它是构建消息队列的主要方式,后来 Redis 也引入了更为专业的 Stream 类型。

列表list相当于数组或者顺序表

image.png
redis的下标支持负数下标,getrange

右侧插入叫做rpush,右侧删除叫做rpop
左侧插入叫做lpush,左侧删除叫做lpop

list内部的结构(编码方式),并非是一个简单的数组,而是更接近于“双端队列(deque)”

  • 列表中的元素是有序的

  • 列表中区分获取和删除的区别
    lindex能获取元素的值
    lrem也能返回被删除的元素的值

  • 列表中的元素是允许重复的
    想hash这样的类型,field是不能重复的

当前list,头和尾都能高效的插入删除元素,就可以把这个list当成一个栈、队列来使用

redis有一个典型的应用场景,就是作为消息队列,最早的时候,就是通过list类型,后来redis又提供了一个stream类型

lpush、lrange

lpush key element [element ...]

lpush一次可以插入一个元素,也可以插入多个元素
按照顺序进行头插元素
1、2、3、4
插入完成之后,4是最前面的

时间复杂度是O(1)

返回的是list的长度

如果key已经存在,并且key对应的value类型不是list的话,此时lpush就会报错
redis中所有的数据类型的操作都是这样的,必须保证类型匹配
image.png
第一次返回长度为4,第二次为8

插入完成之后使用lrange查看我们list的内容

lrange key start stop

后面我们需要指定一段区间的,也是闭区间的,下标也是支持负数的

使用命令就能查看整个list的内容了

lrange key  0 -1

image.png

如果给到的下标超出了范围会出现什么情况呢?

redis的做法,是直接尽可能的获取到给定区间的元素,如果给定区间非法,比如超出下标,尽可能获取到内容
image.png
image.png

lpushx、rpush、rpushx

lpushx:如果key存在时,将一个或者多个元素从左侧放入(头插)到list中。不存在,直接返回

lpushx key element [element...]

时间复杂度是O(1)
返回值是插入后list的长度
image.png

rpush将一个或者多个元素从右侧插入到list中

rpush key element [element...]

image.png

rpushx:如果key存在时,将一个或者多个元素从右侧放入(尾插)到list中。不存在,直接返回

rpushx key element [element...]

lpop、rpop

lpop:返回值是取出的元素或者nil,时间复杂度是O(1)

lpop key

image.png

rpop:从尾部删除数据,返回的即是删除的元素或者nil

rpop key [count]

可以发现这里是一个count的,说明我们可以指定删除数据的个数的
lpop是不支持后面加上个数的,但是rpop是支持的
image.png
image.png

lindex、linsert、llen

index:给定下标,获取到对应的元素
时间复杂度是O(N),N指的是list中元素个数
如果下标非法的话,会返回一个nil空值

lindex key index

下标是从0开始的,并且支持负数下标
image.png

linsert:在指定位置插入元素
时间复杂度是O(N) N表示列表的长度
返回值:插入后的list长度

linsert key <before|after> pivot element

before|after这两个选项是说明是插入到元素之前还是元素之后,这两个选项二选一即可

pivot就是插入元素的基准,是插入到这个元素的左侧还是右侧

element就是插入的元素

image.png
image.png

但是如果这个基准值如果存在多个的话如何
下面这里只插入到了第一个4的前面
image.png
linsert进行插入的时候,要根据基准值,找到对应的位置,从左往右找,找到第一个符合基准值的位置即可

llen:获取list长度
时间复杂度O(1)

llen key

image.png

lerm

rem是remove的缩写

lrem key count element

count表示元素的个数
element表示的是删除元素的值
时间复杂度是O(N+M)
N是list的长度,M是多少个元素要被删除,就是count的大小

  • 如果count大于0的话,我们在删除这个element的时候是从左往右进行元素的查找的
    如果此时的list是1 2 3 4 1 2 3 4 1 2 3 4 的话,我们lrem key 2 1,找到1,删除两次,所以就从左往右查找1,删除两个1即可
    image.pngimage.png

  • 如果count<0的话,我们在删除这个element的时候是从右往左进行元素的查找的
    如果此时的list是1 2 3 4 1 2 3 4 1 2 3 4 的话,我们lrem key -2 1,找到1,删除两次,所以就从右往左查找1,删除两个1即可
    image.pngimage.png

  • 如果count=0的话,我们得删除所有对应的元素,
    如果此时的list是1 2 3 4 1 2 3 4 1 2 3 4 的话,我们lrem key 0 1,找到1,将list中所有的1删除了
    image.png
    image.png

ltrim 、lset

ltrim这个也是删除list中元素的命令,相较于lrem更加粗暴,
指定一个范围,范围内的保留,范围外的删除
时间复杂度是O(N)

ltrim key start stop

保留start和stop之间区间内的元素(区间外面两边的元素就直接被删除了)
比如说这里将中间的3 4 5 6 都保存了,其他的都删除了
image.png

lset:根据下标修改元素
时间复杂度O(N)

lset key index element

image.png
如果下标超过了范围的话,他是会报错提醒的
image.png

lindex可以很好的处理下标越界的情况,直接返回nil

lset来说则会报错的

blpop、brpop

blpop和brpop是lpop和rpop的阻塞版本,和对应非阻塞版本的作用基本一致

  • 在列表中有元素的情况下,阻塞和⾮阻塞表现是⼀致的。但如果列表中没有元素,⾮阻塞版本会理解返回 nil,但阻塞版本会根据 timeout,阻塞⼀段时间,期间 Redis 可以执⾏其他命令,但要求执⾏该命令的客⼾端会表现为阻塞状态
  • 命令中如果设置了多个键,那么会从左向右进⾏遍历键,⼀旦有⼀个键对应的列表中可以弹出元素,命令⽴即返回。
  • 如果多个客⼾端同时多⼀个键执⾏ pop,则最先执⾏命令的客⼾端会得到弹出的元素。

阻塞:当前的线程不走了,代码不继续执行了
会在满足一定的条件下,被唤醒

这里的两个命令的前缀b就是block的意思

image.png
redis中的list也相当于阻塞队列一样
线程安全,则只支持“队列为空”的情况,不考虑“队列满”

如果list中存在元素,blpop和brpop就和lpop以及rpop作用相同
如果list为空的话,blpop和brpop就会产生阻塞,直到队列不空为止

使用blpop和brpop的时候,可以显示设置阻塞时间的,阻塞期间redis是可以执行其他的命令的

blpop和brpop并不会对redis服务器产生影响,只是看起来耗时

blpop和brpop都是可以去尝试获取多个key的列表的元素的
多个Key对应多个list的,哪个有元素了,就会返回哪个元素

如果多个客户端同时多一个键执行pop,则最先执行命令的客户端会得到弹出的元素

image.png

image.png
image.png

blpop key [key…]timeout
可以指定一个key或者是多个key
每个key都对应任何一个非空,blpop都能把这里的元素给获取到,立即返回

如果这些list为空的话,此时就需要阻塞等待了,等待其他客户端往这些list中插入元素了

次数还能指定超时时间,单位是秒

1)针对一个非空list进行操作
image.png
image.png

返回的结果相当于是一个pair

一方面告诉我们当前的数据来自于哪个key,另一个方面会告诉我们取到的数据是啥

2)针对一个空链表进行操作

image.png

3)针对多个Key进行操作
image.png

brpop效果和blpop完全于一样

这两个阻塞命令主要用途就是来作为消息队列

命令总结

好的,这是根据您提供的图片生成的表格:

操作类型命令时间复杂度
添加rpush key value [value ...]O(k), k 是元素个数
lpush key value [value ...]O(k), k 是元素个数
`linsert key beforeafter pivot value`O(n), n 是 pivot 距离头尾的距离
查找lrange key start endO(s+n), s 是 start 偏移量, n 是 start 到 end 的范围
lindex key indexO(n), n 是索引的偏移量
llen keyO(1)
删除lpop keyO(1)
rpop keyO(1)
lrem key count valueO(k), k 是元素个数
ltrim key start endO(k), k 是元素个数
修改lset key index valueO(n), n 是索引的偏移量
阻塞操作blpop brpopO(1)

list类型的应用场景

1、用list作为数组这样的结构来存储多个元素

2、生产消费模型
image.png

3、redis分频道阻塞消息队列模型
image.png
image.png
image.png

总结

本文详细介绍了 Redis 的 List(列表)数据类型,它是一个有序且允许元素重复的集合,底层实现为双端队列,保证了在头尾两端进行操作的效率。

我们探讨了 List 的核心操作命令,可以分为以下几类:

  1. 添加操作:lpush/rpush 用于从左侧或右侧插入一个或多个元素,linsert 可在指定元素前后插入新元素。

  2. 查找操作:lrange 用于获取指定范围内的元素,lindex 获取指定下标的元素,而 llen 则返回列表的长度,时间复杂度为 O(1)。

  3. 删除操作:lpop/rpop 分别从左侧或右侧移除一个元素,lrem 可以根据值删除指定数量的元素,ltrim 则通过保留指定范围内的元素来裁剪列表。

  4. 修改操作:lset 用于更新指定下标的元素值。

  5. 阻塞操作:blpop/brpop 是 lpop/rpop 的阻塞版本。当列表为空时,它们会阻塞客户端连接,直到有新元素可供弹出或超时,这使它们非常适合实现消息队列。

最后,我们介绍了 List 类型的典型应用场景,如作为普通数组存储数据,以及在生产者-消费者模型中充当高效、可靠的消息队列。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Undoom

感谢啦

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值