当前位置: 首页 > 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同样能够满足消息队列的三大需求,而且它还支持消费组形式的消息读取。

相关文章:

  • 亚远景-ASPICE在汽车软件全生命周期管理中的作用
  • AI Agent 的架构与技术体系分析
  • 在当系统未连接上wifi的时候,直接不显示wifi列表 ,这个判断导致?
  • 贪心选择 (Greedy Choice)
  • Vue2数组响应式问题:Object.defineProperty不能监听数组吗
  • 论文略读:RegMix: Data Mixture as Regression for Language Model Pre-training
  • 杉山将(Sugiyama Masa)《图解机器学习》
  • 2023蓝桥杯C/C++ B组国赛
  • swagger通过配置将enum自动添加到字段说明中
  • Neo4j批量数据导入完全指南:高效处理大规模数据
  • Java多线程实现之同步方法详解
  • 创客匠人助力家庭教育IP破局:从0到1打造创始人个人品牌全攻略
  • Windows11下搭建Black Magic Probe (BMP) 编译环境
  • ESP32-s3 的I2C可以同时接LCD显示屏、IP5356M吗
  • c++ std::invoke
  • Docker Compose完整教程
  • 【Chipyard】 conda 环境安装与使用
  • 黑马python(四)
  • 正则表达式:开启文本处理的魔法之门
  • Git不能更新以及提交代码,提示链接超时,本地凭证无问题
  • 南京做网站的公司排名/百度seo服务公司
  • 网站建设分金手指排名二六/西安网约车平台
  • 建设的比较好的档案馆网站/软件开发公司简介
  • 东莞网站建设seo优化/app推广工作是做什么的
  • 有免费建站的网站吗/泰安seo培训
  • 个人网站的基本风格有哪些/收录好的网站有哪些