Redis - list 列表

前言

列表类似于 Java 中的数组或者顺序表,在 Redis 中,可以对列表两端插⼊(push)和弹出(pop),还可以获取指定范围的元素列表、 获取指定索引下标的元素等。列表是⼀种⽐较灵活的数据结构,它可以充当栈和队列的⻆⾊,在实际开发上有很多应⽤场景。

注意:list 内部的编码方式并不是一个简单的数组,更接近于双端队列

列表两端插入和弹出操作

约定最左侧下标为 0 ,redis 的下标支持负数下标,最右侧是 -1

列表类型的特点

a.列表中的元素是有序的

" 有序 " 的含义要根据上下文区分,有的时候谈到有序指的是升序降序,而这里谈到的有序是指顺序很关键,列表有下标,如果把两个下标之间的值进行交换,得到的列表和之前的列表就不相同

b.列表中的元素是允许重复的

c.可以当作栈和队列来使用

因为列表的头和尾都可以高效的插入和删除元素,所以可以做出相应的限制,当作栈和队列来使用

命令

LPUSH 将⼀个或者多个元素从左侧放入(头插)到 list 中

语法

java 复制代码
LPUSH key element [element ...]

时间复杂度:只插⼊⼀个元素为 O(1), 插⼊多个元素为 O(N), N 为插⼊元素个数.

返回值:插⼊后 list 的⻓度

通过上图的操作,key1 对应的列表中的数据为 4 3 2 1

注意:如果 key 已经存在,并且 key 对应的 value 类型不是 list,那么就会报错(redis 中各种数据类型的操作都是类似的效果)

LPUSHX 在 key 存在时,将⼀个或者多个元素从左侧放入(头插)到 list 中

语法

java 复制代码
LPUSHX key element [element ...]

时间复杂度:只插⼊⼀个元素为 O(1), 插⼊多个元素为 O(N), N 为插⼊元素个数.

返回值:插⼊后 list 的⻓度。

上图中通过 lpushx 插入数据到列表中得到的返回值是 0 就说明了没有成功插入数据,因为该列表当前不存在

RPUSH 将⼀个或者多个元素从右侧放⼊(尾插)到 list 中

语法

java 复制代码
RPUSH key element [element ...]

时间复杂度:只插⼊⼀个元素为 O(1), 插⼊多个元素为 O(N), N 为插⼊元素个数

返回值:插⼊后 list 的⻓度

通过上图的操作,key 对应的列表中的数据为 1 2 3 4 5

RPUSHX 在 key 存在时,将⼀个或者多个元素从右侧放入(尾插)到 list 中

语法

java 复制代码
RPUSHX key element [element ...]

时间复杂度:只插⼊⼀个元素为 O(1), 插⼊多个元素为 O(N), N 为插⼊元素个数.

返回值:插⼊后 list 的⻓度

LRANGE 获取指定区间的所有元素

获取从 start 到 stop 区间的所有元素,左闭右闭。下标可以使用负数

语法

java 复制代码
LRANGE key start stop

时间复杂度:O(N)

返回值:指定区间的元素

-1 代表最后一个元素,所以获取 0 ~ -1 的元素相当于获取列表中所有的元素

谈到下标,往往会关注超出范围的情况,在 Redis 中,如果给定的区间非法,比如超出下标,会尽可能的获取能获取到的内容,程序的容错能力很强

如图,要获取 0 ~ 100 区间内的数据,而列表中只有 5 个数据,很明显 100 超出了列表的下标,但 Redis 并没有报错,而是尽可能的获取了0 ~ 100 中能够获取到的数据

LPOP 从 list 左侧取出元素(即头删)

语法

java 复制代码
LPOP key

时间复杂度:O(1)

返回值:取出的元素或者 nil

RPOP 从 list 右侧取出元素(即尾删)

语法

java 复制代码
RPOP key

时间复杂度:O(1)

返回值:取出的元素或者 nil

LINDEX 获取从左数 index 位置的元素

语法

java 复制代码
LINDEX key index

时间复杂度:O(N)

应该会有读者疑惑,通过下标获取元素时间复杂度不应该是 O(1) 吗,实际上 Redis 的 list(列表)类型的内部编码不是一个普通的顺序表,所以不能按照普通的顺序表来考虑该列表

返回值:取出的元素或者 nil

LINSERT 在特定位置插入元素

语法

java 复制代码
LINSERT key <BEFORE | AFTER> pivot element

pivot 代表要插入数据的基准值,element 代表插入的数据

时间复杂度:O(N)

返回值:插⼊后的 list ⻓度

万一基准值存在多个怎么办?数据要如何插入?

如上图,当基准值 2 存在多个时,选取从左到右的第一个 2 作为基准值

LLEN 获取 list 长度

语法

java 复制代码
 LLEN key

时间复杂度:O(1)

获取 list 的长度并不需要遍历列表中的内容,因为 redis 为列表维护了一个变量来存储列表的长度,当要获取列表长度时,直接去获取该变量即可

返回值:list 的⻓度

BLPOP 从 list 左侧取出元素(即头删)

LPOP 的阻塞版本

• 在列表中有元素的情况下,阻塞和⾮阻塞表现是⼀致的。但如果列表中没有元素,⾮阻塞版本会直接返回 nil,但阻塞版本会根据 timeout,阻塞⼀段时间,期间 Redis 可以执⾏其他命令,但要求执⾏该命令的客户端会表现为阻塞状态

• 命令中如果设置了多个键,那么会从左向右进⾏遍历键,⼀旦有⼀个键对应的列表中可以弹出元 素,命令⽴即返回。

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

语法

java 复制代码
BLPOP key [key ...] timeout

列表 key 可以有多个,表示可以同时监控多个列表,哪个列表有数据就取哪个列表

timeout 是阻塞的时间,阻塞时间过了以后还没有获取到数据就返回 nil

时间复杂度:O(1)

返回值:取出的元素或者 nil

BRPOP 从 list 右侧取出元素(即尾删)

RPOP 的阻塞版本(详细内容参考对 BLPOP 的介绍)

语法

java 复制代码
BRPOP key [key ...] timeout

时间复杂度:O(1)

返回值:取出的元素或者 nil

内部编码

以前列表类型的内部编码有两种:

• ziplist(压缩列表):当列表的元素个数⼩于 list-max-ziplist-entries 配置(默认 512 个),同时 列表中每个元素的⻓度都⼩于 list-max-ziplist-value 配置(默认 64 字节)时,Redis 会选⽤ ziplist 来作为列表的内部编码实现来减少内存消耗

• linkedlist(链表):当列表类型⽆法满⾜ ziplist 的条件时,Redis 会使⽤ linkedlist 作为列表的内 部实现

因为 ziplist 编码方式虽然节省空间,但是当元素个数较多或元素较长时会大大影响存取数据的效率,所以就需要改变编码方式为 linkedlist

注意:上述的两种编码方式我特意加上了以前,因为现在 list(列表)的内部编码方式作出了改进,现在的内部编码方式是 quicklist ,quicklist 是 ziplist(压缩列表)和 linkedlist(链表)的结合,整体还是个链表,但链表的每个节点是一个压缩列表

通过这种方式可以节省空间,并且压缩列表不会太长,不会明显的影响读取数据的效率

使用场景

消息队列

Redis 可以使⽤ lpush + brpop 命令组合实现经典的阻塞式 ⽣产者-消费者 模型队列, ⽣产者客户端使⽤ lpush 从列表左侧插⼊元素,多个消费者客户端使⽤ brpop 命令阻塞式地从队列中 "争抢" 队⾸元素。通过多个客户端来保证消费的负载均衡和⾼可⽤性

有读者可能会疑惑,消息队列的存取元素不是都应该实现阻塞吗?但 lpush + brpop 命令组合明显只实现取元素的阻塞,实际上对于 Redis 的数据类型是几乎不会出现存满的情况的,首先如果 Redis 的一个数据类型中存了太多的元素,那么对其进行操作时很容易发生阻塞,所以程序员不会让 Redis 的一个数据类型中存了太多的元素,而且 list 中最多存多少元素是可以配置的,所以通常情况下只会出现列表中没有数据的情况,不会出现存满的情况。

Redis 阻塞消息队列模型

哪个消费者先执行 brpop 命令,哪个消费者就先获得列表中的数据

Redis 分频道阻塞消息队列模型

消费者可以同时监视多个列表,获取数据

相关推荐
Blossom i1 分钟前
大数据预处理与采集实验一:使用Python操作MySQL数据库
数据库·mysql
雾时之林1 小时前
Linux--软件管理、源码包安装
linux·服务器·数据库
thesky1234561 小时前
智能体面试准备(二十六):Agent 成本工程——模型路由、缓存、预算控制与大小模型分工
缓存·llmops·智能体·模型路由·成本工程·预算控制·小模型分工
老纪的技术唠嗑局2 小时前
超级干货分享:中小企业使用 OceanBase 实践经验汇总
数据库
CLOUD ACE3 小时前
谷歌云代理|零售商Target 如何利用 Spanner Graph 提升零售发现体验并将数据库维护成本降低 50%
数据库·零售
AI大模型-小雄3 小时前
Codex写分页接口为什么越翻越慢?用Cursor Pagination解决重复与漏数据
大数据·数据库·elasticsearch·搜索引擎·chatgpt·后端开发·codex
少晓年4 小时前
从 MySQL 迁移到人大金仓 KingbaseES:完整指南与实践
数据库·mysql
SelectDB4 小时前
Apache Doris AI RAG 实战:从基础 RAG 到知识图谱增强的技术能力与选型
数据库
SelectDB4 小时前
电商企业 PostgreSQL 迁移 Apache Doris:80TB 分析数据统一平台技术能力与实践
数据库
SelectDB4 小时前
阶跃星辰 Agent 可观测:Apache Doris / SelectDB 的技术能力与实践
数据库