当前位置: 首页 > news >正文

list类型

Redis中的列表跟其它语言的链表一样,只是操作不一样。列表类型是用来存储多个有序的字符串,列表中的每个字符串称为元素(element),⼀个列表最多可以存储个元素。列表中的元素是允许重复的,在 Redis 中,可以对列表两端插入(push)和弹出(pop),还可以获取指定范围的元素列表、获取指定索引下标的元素等。列表是⼀种比较灵活的数据结构,它可以充当栈和队列的角色,在实际开发上有很多应用场景。
请添加图片描述

非阻塞命令

LPUSH

Rpush 命令用于将一个或多个值插入到列表的尾部(最左边)。
如果列表不存在,一个空列表会被创建并执行 LPUSH 操作。 当列表存在但不是列表类型时,返回一个错误。
注意:在Redis 2.4版本以前的 LPUSH 命令,都只接受单个 value 值。
语法:LPUSH key element [element ...]
示例:

127.0.0.1:6379> lpush key1 1 2 3 4 5
(integer) 5
127.0.0.1:6379> lrange key1 0 -1
1) "5"
2) "4"
3) "3"
4) "2"
5) "1"

命令有效版本:1.0.0 之后。
时间复杂度:只插⼊⼀个元素为 O(1), 插入多个元素为 O(N), N 为插⼊元素个数。
返回值:插后入 list 的⻓度。

LPUSHX

Lpushx 将一个值插入到已存在的列表头部,列表不存在时操作无效。
语法:LPUSHX key element [element ...]
示例:

127.0.0.1:6379> lpushx key1 6
(integer) 6
127.0.0.1:6379> lpushx key2 1
(integer) 0
127.0.0.1:6379> lrange key1 0 -1
1) "6"
2) "5"
3) "4"
4) "3"
5) "2"
6) "1"
127.0.0.1:6379> lrange key2 0 -1
(empty array)

命令有效版本:2.0.0 之后
时间复杂度:只插⼊⼀个元素为 O(1), 插入多个元素为 O(N), N 为插⼊元素个数.
返回值:插⼊后 list 的⻓度。

RPUSH

Rpush 命令用于将一个或多个值插入到列表的尾部(最右边)。
如果列表不存在,一个空列表会被创建并执行 RPUSH 操作。 当列表存在但不是列表类型时,返回一个错误。
注意:在 Redis 2.4 版本以前的 RPUSH 命令,都只接受单个 value 值。
示例:

127.0.0.1:6379> rpush key2 1 2 3 4 5
(integer) 5
127.0.0.1:6379> lrange key2 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"

命令有效版本:1.0.0 之后。
时间复杂度:只插入⼀个元素为 O(1), 插⼊多个元素为 O(N), N 为插⼊元素个数.
返回值:插⼊后 list 的长度。

RPUSHX

Rpushx 将一个值插入到已存在的列表尾部,列表不存在时操作无效。
语法:RPUSHX key element [element ...]
示例:

127.0.0.1:6379> rpushx key2 10
(integer) 6
127.0.0.1:6379> rpushx key3 1
(integer) 0
127.0.0.1:6379> lrange key2 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "10"
127.0.0.1:6379> lrange key3 0 -1
(empty array)

命令有效版本:2.0.0 之后
时间复杂度:只插⼊⼀个元素为 O(1), 插⼊多个元素为 O(N), N 为插⼊元素个数.
返回值:插⼊后 list 的长度。

LRANGE

Lrange 返回列表中指定区间内的元素,区间以偏移量 START 和 END 指定。 其中 0 表示列表的第一个元素, 1 表示列表的第二个元素,以此类推。 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。
语法:LRANGE KEY_NAME START END
示例:

127.0.0.1:6379> lrange key2 0 5
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "10"127.0.0.1:6379> lrange key2 -6 -1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "10"

命令有效版本:1.0.0 之后。
时间复杂度:O(N)。
返回值:指定区间的元素。

LPOP

从 list 左侧取出元素(即头删)
语法:LPOP key
示例:

127.0.0.1:6379> lpop key2
"1"
127.0.0.1:6379> lpop key2
"2"
127.0.0.1:6379> lrange key2 0 -1
1) "3"
2) "4"
3) "5"
4) "10"

命令有效版本:1.0.0 之后。
时间复杂度:O(1)。
返回值:取出的元素或者 nil。

RPOP

从 list 右侧取出元素(即尾删)。
语法:RPOP key
示例:

127.0.0.1:6379> rpop key2
"10"
127.0.0.1:6379> rpop key2
"5"
127.0.0.1:6379> lrange key2 0 -1
1) "3"
2) "4"

命令有效版本:1.0.0 之后。
时间复杂度:O(1)。
返回值:取出的元素或者 nil。

LINDEX

获取从左数第 index 位置的元素。
语法:LINDEX KEY_NAME INDEX_POSITION
示例:

127.0.0.1:6379> lpush key1 5 4 3 2 1
(integer) 5
127.0.0.1:6379> lrange key1 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
127.0.0.1:6379> lindex key1 3
"4"

命令有效版本:1.0.0 之后。
时间复杂度:O(N)。
返回值:取出的元素或者 nil。

LINSERT

Linsert 命令用于在列表的元素前或者后插入元素。当指定元素不存在于列表中时,不执行任何操作。
当列表不存在时,被视为空列表,不执行任何操作。
如果 key 不是列表类型,返回一个错误。
语法:LINSERT key <BEFORE | AFTER> pivot element
pivot是指要插入的元素(element)
示例:

127.0.0.1:6379> linsert key1 before 2 100   // 在2的前面加上100
(integer) 6
127.0.0.1:6379> lrange key1 0 -1
1) "1"
2) "100"
3) "2"
4) "3"
5) "4"
6) "5"127.0.0.1:6379> linsert key1 after 2 300   // 在2的前面加上300
(integer) 7
127.0.0.1:6379> lrange key1 0 -1
1) "1"
2) "100"
3) "2"
4) "300"
5) "3"
6) "4"
7) "5"

命令有效版本:2.2.0 之后。
时间复杂度:O(N)。
返回值:插⼊后的 list 长度。

LLEN

Llen 命令用于返回列表的长度。 如果列表 key 不存在,则 key 被解释为一个空列表,返回 0 。 如果 key 不是列表类型,返回一个错误。
语法:LLEN KEY_NAME
示例:

127.0.0.1:6379> lrange key1 0 -1
1) "1"
2) "100"
3) "2"
4) "300"
5) "3"
6) "4"
7) "5"
127.0.0.1:6379> llen key1
(integer) 7
127.0.0.1:6379> llen key2  // key2不存在
(integer) 0
127.0.0.1:6379> set key2 3  // key3是一个字符串
OK
127.0.0.1:6379> llen key2   // 不是列表会报错
(error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> type key2
string

命令有效版本:1.0.0 之后。
时间复杂度:O(1)。
返回值:list 的⻓度。

LREM

Lrem 根据参数 COUNT 的值,移除列表中与参数 VALUE 相等的元素。
COUNT 的值可以是以下几种:

  • count > 0 : 从表头开始向表尾搜索,移除与 VALUE 相等的元素,数量为 COUNT 。
  • count < 0 : 从表尾开始向表头搜索,移除与 VALUE 相等的元素,数量为 COUNT 的绝对值。
  • count = 0 : 移除表中所有与 VALUE 相等的值。

语法:LREM key count VALUE
示例:

127.0.0.1:6379> lpush key3 1 1 1 2 2 3
(integer) 6
127.0.0.1:6379> lrem key3 1 2  // 删除1个2
(integer) 1
127.0.0.1:6379> lrange key3 0 -1
1) "3"
2) "2"
3) "1"
4) "1"
5) "1"
127.0.0.1:6379> lrem key3 3 1    // 删除3个1
(integer) 3
127.0.0.1:6379> lrange key3 0 -1
1) "3"
2) "2"
127.0.0.1:6379> lpush key3 3 3 2 2 4
(integer) 7
127.0.0.1:6379> lrange key3 0 -1
1) "4"
2) "2"
3) "2"
4) "3"
5) "3"
6) "3"
7) "2"
127.0.0.1:6379> lrem key3 0 2  // 删除所有的2
(integer) 3
127.0.0.1:6379> lrange key3 0 -1
1) "4"
2) "3"
3) "3"
4) "3"

可用版本>= 1.0.0
返回值:被移除元素的数量。 列表不存在时返回 0 。

LTRIM

Ltrim 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。
下标 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。 也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。
语法:LTRIM KEY_NAME START STOP
示例:

127.0.0.1:6379> lpush key 4 1 2 3 3 4 5
(integer) 7
127.0.0.1:6379> lrange key 0 -1
1) "5"
2) "4"
3) "3"
4) "3"
5) "2"
6) "1"
7) "4"
127.0.0.1:6379> ltrim key 2 5
OK
127.0.0.1:6379> lrange key 0 -1
1) "3"
2) "3"
3) "2"
4) "1"

可用版本>= 1.0.0
返回值:命令执行成功时,返回 ok 。

LSET

Lset 通过索引来设置元素的值。
当索引参数超出范围,或对一个空列表进行 LSET 时,返回一个错误。
语法:LSET KEY_NAME INDEX VALUE
示例:

127.0.0.1:6379> lrange key 0 -1
1) "3"
2) "3"
3) "2"
4) "1"
127.0.0.1:6379> lset key 2 10   // 下标为2的元素改为10
OK
127.0.0.1:6379> lrange key 0 -1
1) "3"
2) "3"
3) "10"
4) "1"
127.0.0.1:6379> lset key4 1 2
(error) ERR no such key

可用版本>= 1.0.0
返回值:命令执行成功时,返回 ok,否则返回错误信息。

阻塞命令

之前的命令都是非阻塞的,也就是说你只要输完命令就可以直接得到结果。Redis是单线程的,要是有一个命令需要耗费的时间很长的话,那么输入其它命令就会被阻塞住,只能等前一个命令执行完毕。
在学习Linux的时候,生产消费者模型里面有阻塞队列,使用队列来作为中间的交易场所,我们所期望的这个队列有两个特性,第一个是线程安全;第二个是阻塞,如果队列为空,尝试出队列,就会产生阻塞,直到队列不为空,阻塞解除。如果队列是满的话,尝试入队列,也会产生阻塞,直达队列不为满,阻塞解除。
Redis中的list也相当于阻塞队列一样,线程安全是通过单线程模型支持的。阻塞,则只支持“队列为空”的情况,不考虑“队列满”。
命令如果设置了多个键,那么会从左向右进行遍历键,一旦有一个键对应的列表中可以弹出元素,命令立即返回。
blpopbrpop都是可以同时去尝试获取多个key的列表的元素的,多个key对应多个list,这多个list哪个有元素了,就会返回哪个元素。

BLPOP

Blpop 命令移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
语法:BLPOP LIST1 LIST2 .. LISTN TIMEOUT
示例:
请添加图片描述

命令有效版本:2.0.0 之后。
时间复杂度:O(1)。
返回值:如果列表为空,返回一个 nil 。 否则,返回一个含有两个元素的列表,第一个元素是被弹出元素所属的 key ,第二个元素是被弹出元素的值。

BRPOP

Brpop 命令移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
语法:BRPOP LIST1 LIST2 .. LISTN TIMEOUT
示例:与BLPOP类似,只是会尾删

命令有效版本:1.0.0 之后
时间复杂度:O(1)
返回值:假如在指定时间内没有任何元素被弹出,则返回一个 nil 和等待时长。 反之,返回一个含有两个元素的列表,第一个元素是被弹出元素所属的 key ,第二个元素是被弹出元素的值。

应用场景

消息队列

消息队列在存取消息时,必须满足三个要求,分别是消息保序、处理重复的消息和保证消息的可靠性
RedisListStream两种数据类型,就可以满足消息队列的这三个需求。我们先了解一下基于List的消息队列的实现方法。
1. 如何满足消息保序需求?
List 本身就是按照先进先出的顺序对数据进行存取的,所以,如果使用List 作为消息队列保存消息的话,就已经能满足消息保序的需求了。
List 可以使用LPUSH + RPOP或者反过来RPUSH + LPOP命令实现消息队列。
请添加图片描述

  • 生产者使用LPSUH key value[value...]将消息插入到队列的头部,如果key不存在则会创建一个空的队列再插入消息。
  • 消费者使用RPOP key依次读取队列的消息,先进先出。

不过,在消费者读取数据时,有一个潜在的性能风险。
在生产者往List 中写入数据时,List 并不会主动地通知消费者有新消息写入,如果消费者想要及时处理消息,就需要在程序中不停的调用RPOP命令(比如使用一个while(1)循环)。如果有新消息写入,RPOP命令就会返回结果,否则,RPOP命令返回空值,再继续循环。
所以,即使没有新的消息写入list中时,消费者也要不停的调用RPOP命令,这就会导致消费者程序的CPU一直消耗在执行RPOP命令上,带来不必要的性能损失。
为了解决这个问题,Redis提供了BRPOP命令。BRPOP命令也称为阻塞式读取,客户端在没有读到队列数据时,自动阻塞,直到有新的数据写入队列,再开始读取新数据。和消费者程序自己不停地调用RPOP命令相比,这种方式能节省CPU开销。
请添加图片描述

2. 如何处理重复的消息?
消费者要实现重复消息的判断,需要2个方面的要求:

  • 每个消息都有一个全局的ID。
  • 消费者要记录已经处理过的消息的ID。当收到一条消息后,消费者程序就可以对比收到的消息ID记录的已处理过的消息ID,来判断当前收到的消息有没有经过处理。如果已经处理过,那么消费者程序就不再进行处理了。

但是List 并不会为每个消息生成ID号,所以我们需要自己为每个消息生成一个全局唯一ID,生成之后,我们在用LPUSH命令把消息插入到List时,需要在消息中包含这个全局唯一ID。

例如,我们执行以下命令,就把一条全局ID为 111000101、库存量为999的消息插入了消息队列。

LPUSH mq "111000101:stock:99"
(integer) 1

3. 如何保证消息可靠性?
当消费者程序从List中读取一条消息后,List就不会留存这条消息了。所以,如果消费者程序在处理消息的过程中出现了故障或宕机,就会导致消息没有处理完成,那么,消费者程序再次启动后,就没法再次从List中读取消息了。
为了留存消息,List类型提供了BRPOPLPUSH命令,这个命令的作用是让消费者程序从一个List中读取消息,同时,Redis会把这个消息再插入到另一个List(可以叫做备份List)留存
这样一来,如果消费者程序读了消息但没有正常处理,等它重启后,就可以从备份List中重新读取消息并进行处理了。
总结:

  • 消息保存:使用:LPUSH + RPOP;
  • 阻塞读取:使用BRPOP;
  • 重复消息处理:生产者自行实现全局唯一ID;
  • 消息的可靠性:使用BRPOPLPUSH

List作为消息队列的缺陷

List不支持多个消费者消费同一条消息,因为一旦消费者拉取一条消息后,这条消息就从List中删除了,无法被其它消费者再次消费。
要实现一条消息可以被多个消费者消费,那么就要将多个消费者组成一个消费者组,使得多个消费者可以消费同一条消息,但是List类型并不支持消费组的实现
不过我们可以使用Stream数据类型来实现消费队列,Stream同样能够满足消息队列的三大需求,而且它还支持消费组形式的消息读取。

http://www.xdnf.cn/news/985951.html

相关文章:

  • SCADA|测试KingSCADA4.0信创版采集汇川PLC AC810数据
  • 开源夜莺支持MySQL数据源,更方便做业务指标监控了
  • xss分析
  • C2f模块 vs Darknet-53——YOLOv8检测效率的提升
  • 9.IP数据包分片计算
  • HNCTF2025 - Misc、Osint、Crypto WriteUp
  • 第三讲 基础运算之整数运算
  • 什么是数字化项目风险管理?如何实现项目风险管理数字化?
  • IIS 实现 HTTPS:OpenSSL证书生成与配置完整指南
  • 突然虚拟机磁盘只剩下几十K
  • [特殊字符] React 与 Vue 源码级对比:5大核心差异与实战选择指南
  • # include<heαd.h>和# include″heαd.h″的区别
  • 成都国际数字影像产业园孵化培育模式的探索与突破
  • 人机交互设计知识点总结
  • 驻波比(VSWR)详解
  • 判断字符串子序列
  • OpenAI o3-pro深度解析:87%降价背后的AI战略,AGI发展迈入新阶段!
  • 自动托盘搬运车是什么?它的工作逻辑如何实现物流自动化?
  • Python训练营打卡 Day51
  • 日本滨松R669光电倍增管Hamamatsu直径51 mm 直径端窗型扩展红多碱光阴极面光谱灵敏度特性:300 至 900 nm
  • AI重写工具导致‘文本湍流’特征|如何人工消除算法识别标记
  • 卷积神经网络(一)
  • 基于C++实现(控制台)小学算数自测系统
  • ateⅹⅰt()的用法
  • DD3118完整版参数规格书|DD3118 3.0读卡器控制方案|DD3118高速3.0读卡器芯片
  • 【数据采集+人形机器人】使用 Apple Vision Pro 对宇树(Unitree)G1 和 H1 人形机器人进行全身的遥操作控制
  • RAG 处理流程
  • Linux内核学习资料-deepseek
  • AIGC 基础篇 Python基础 05 元组,集合与字典
  • 45. Jump Game II