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

前言

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

List 类型的主要特性包括:

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

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

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

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

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

redis的下标支持负数下标,getrange

右侧插入叫做rpush,右侧删除叫做rpop

左侧插入叫做lpush,左侧删除叫做lpop

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

  • 列表中的元素是有序的

  • 列表中区分获取和删除的区别

    lindex能获取元素的值

    lrem也能返回被删除的元素的值

  • 列表中的元素是允许重复的

    想hash这样的类型,field是不能重复的

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

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

lpush、lrange

bash 复制代码
lpush key element [element ...]

lpush一次可以插入一个元素,也可以插入多个元素

按照顺序进行头插元素

1、2、3、4

插入完成之后,4是最前面的

时间复杂度是O(1)

返回的是list的长度

如果key已经存在,并且key对应的value类型不是list的话,此时lpush就会报错

redis中所有的数据类型的操作都是这样的,必须保证类型匹配

第一次返回长度为4,第二次为8

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

bash 复制代码
lrange key start stop

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

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

bash 复制代码
lrange key  0 -1

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

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

lpushx、rpush、rpushx

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

bash 复制代码
lpushx key element [element...]

时间复杂度是O(1)

返回值是插入后list的长度

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

bash 复制代码
rpush key element [element...]

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

bash 复制代码
rpushx key element [element...]

lpop、rpop

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

bash 复制代码
lpop key

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

bash 复制代码
rpop key [count]

可以发现这里是一个count的,说明我们可以指定删除数据的个数的

lpop是不支持后面加上个数的,但是rpop是支持的

lindex、linsert、llen

index:给定下标,获取到对应的元素

时间复杂度是O(N),N指的是list中元素个数

如果下标非法的话,会返回一个nil空值

bash 复制代码
lindex key index

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

linsert:在指定位置插入元素

时间复杂度是O(N) N表示列表的长度

返回值:插入后的list长度

bash 复制代码
linsert key <before|after> pivot element

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

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

element就是插入的元素


但是如果这个基准值如果存在多个的话如何

下面这里只插入到了第一个4的前面

linsert进行插入的时候,要根据基准值,找到对应的位置,从左往右找,找到第一个符合基准值的位置即可

llen:获取list长度

时间复杂度O(1)

bash 复制代码
llen key

lerm

rem是remove的缩写

bash 复制代码
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即可

  • 如果count<0的话,我们在删除这个element的时候是从右往左进行元素的查找的

    如果此时的list是1 2 3 4 1 2 3 4 1 2 3 4 的话,我们lrem key -2 1,找到1,删除两次,所以就从右往左查找1,删除两个1即可

  • 如果count=0的话,我们得删除所有对应的元素,

    如果此时的list是1 2 3 4 1 2 3 4 1 2 3 4 的话,我们lrem key 0 1,找到1,将list中所有的1删除了

ltrim 、lset

ltrim这个也是删除list中元素的命令,相较于lrem更加粗暴,

指定一个范围,范围内的保留,范围外的删除

时间复杂度是O(N)

bash 复制代码
ltrim key start stop

保留start和stop之间区间内的元素(区间外面两边的元素就直接被删除了)

比如说这里将中间的3 4 5 6 都保存了,其他的都删除了

lset:根据下标修改元素

时间复杂度O(N)

bash 复制代码
lset key index element

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

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

lset来说则会报错的

blpop、brpop

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

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

阻塞:当前的线程不走了,代码不继续执行了

会在满足一定的条件下,被唤醒

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

redis中的list也相当于阻塞队列一样

线程安全,则只支持"队列为空"的情况,不考虑"队列满"

如果list中存在元素,blpop和brpop就和lpop以及rpop作用相同

如果list为空的话,blpop和brpop就会产生阻塞,直到队列不空为止

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

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

blpop和brpop都是可以去尝试获取多个key的列表的元素的

多个Key对应多个list的,哪个有元素了,就会返回哪个元素

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


blpop key [key...]timeout

可以指定一个key或者是多个key

每个key都对应任何一个非空,blpop都能把这里的元素给获取到,立即返回

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

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

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

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

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

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

3)针对多个Key进行操作

brpop效果和blpop完全于一样

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

命令总结

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

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

list类型的应用场景

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

2、生产消费模型

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


总结

本文详细介绍了 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 类型的典型应用场景,如作为普通数组存储数据,以及在生产者-消费者模型中充当高效、可靠的消息队列。

相关推荐
岁岁种桃花儿1 小时前
MySQL从入门到精通系列:InnoDB记录存储结构
数据库·mysql
jiunian_cn3 小时前
【Redis】hash数据类型相关指令
数据库·redis·哈希算法
冉冰学姐3 小时前
SSM在线影评网站平台82ap4(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面
数据库·ssm框架·在线影评平台·影片分类
知识分享小能手4 小时前
SQL Server 2019入门学习教程,从入门到精通,SQL Server 2019数据库的操作(2)
数据库·学习·sqlserver
踩坑小念5 小时前
秒杀场景下如何处理redis扣除状态不一致问题
数据库·redis·分布式·缓存·秒杀
萧曵 丶5 小时前
MySQL 语句书写顺序与执行顺序对比速记表
数据库·mysql
Wiktok6 小时前
MySQL的常用数据类型
数据库·mysql
曹牧6 小时前
Oracle 表闪回(Flashback Table)
数据库·oracle
浪客灿心7 小时前
list_stack_queue
数据结构·list
J_liaty7 小时前
Redis 超详细入门教程:从零基础到实战精通
数据库·redis·缓存