Redis:不只是缓存那么简单(五)

专栏:Redis 修行录

个人主页:手握风云

目录

[一、List 类型](#一、List 类型)

[1.1. 概述与特点](#1.1. 概述与特点)

[1.2. 常见操作命令](#1.2. 常见操作命令)

[1. LPUSH 和 RPUSH](#1. LPUSH 和 RPUSH)

[2. LPUSHX 和 RPUSHX](#2. LPUSHX 和 RPUSHX)

[3. LPOP 和 RPOP](#3. LPOP 和 RPOP)

[4. LRANGE](#4. LRANGE)

[5. LINDEX、LINSERT 和 LLEN](#5. LINDEX、LINSERT 和 LLEN)

[6. LREM 和 LTRIM](#6. LREM 和 LTRIM)

[7. LSET](#7. LSET)

[1.3. 阻塞版本命令](#1.3. 阻塞版本命令)

[1.4. 内部编码机制](#1.4. 内部编码机制)

[1.5. 典型应用场景](#1.5. 典型应用场景)


一、List 类型

1.1. 概述与特点

List 类型用于存储多个有序的字符串,列表中的每个字符串称为元素(element),一个列表最多可以存储 2^32 - 1 个元素。在 Redis 中,可以对列表的两端进行插入(push)和弹出(pop)操作,使其非常灵活,可以充当栈或队列的角色。

列表类型具有以下三个核心特点:

  1. 有序性:列表中的元素是有序的,这意味着可以通过索引下标(正数或负数)获取某个元素或某个范围的元素列表。
  2. 操作区分:获取和删除是不同的操作。例如,使用 lrem 会删除元素并改变列表长度;而使用 lindex 只是获取元素,列表长度不会变化。
  3. 允许重复:列表中的元素是允许重复存在的。

1.2. 常见操作命令

1. LPUSH 和 RPUSH

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

LPUSH 命令主要用于将一个或多个指定的元素从列表(List)的左侧(即头部)插入。该命令的基本语法为 LPUSH key element element ...,在执行时,如果指定的键(key)不存在,Redis 会在执行插入操作前自动创建一个新的空列表。从执行效率来看,插入单一元素的时间复杂度为 O(1),若在一条命令中传入多个欲插入的元素,其整体时间复杂度则为 O(N),这里的 N 代表插入元素的总数。命令执行完毕后,系统会返回一个整数,代表插入操作完成后列表的最新长度。

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

RPUSH 命令与 LPUSH 相对应,主要用于将一个或多个指定的元素从列表的右侧(即尾部)追加插入。它的基本语法是 RPUSH key element element ...,同样地,如果目标键不存在,Redis 会预先创建一个空列表再进行元素的推入。其时间复杂度与 LPUSH 完全一致,每追加一个元素耗时 O(1),同时追加 N 个元素的时间复杂度则为 O(N)。在命令成功执行后,它也会返回对应列表在追加新元素后的总长度。

bash 复制代码
LPUSH list1 1 2 3 4
RPUSH list1 6 5 4 3

2. LPUSHX 和 RPUSHX

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

LPUSHX 命令主要用于在指定的键(key)已经存在并且是一个列表(List)的情况下,将一个或多个指定的元素从列表的左侧(即头部)插入。与 LPUSH 命令不同的是,如果目标键在此之前并不存在,LPUSHX 不会预先创建空列表,而是不执行任何操作并直接返回。在执行效率方面,插入单一元素的时间复杂度为 O(1),如果一次性传入多个元素进行插入,其整体时间复杂度则为 O(N),N 代表插入元素的总数。命令执行完毕后,会返回一个整数,表示插入操作完成后列表的最新长度。

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

RPUSHX 命令则与 LPUSHX 的行为相对应,主要用于在指定的键已经存在且持有列表时,将一个或多个元素从列表的右侧(即尾部)追加插入。如果给定的键不存在,Redis 同样不会执行任何追加操作。该命令的时间复杂度与 LPUSHX 完全一致,即每追加一个元素耗时 O(1),同时追加 N 个元素耗时 O(N)。命令成功执行后,它也会返回对应的列表在追加新元素后的最新总长度。

bash 复制代码
LPUSHX list1 10 20
RPUSHX list1 30 40
LPUSHX list2 1 2 3

3. LPOP 和 RPOP

bash 复制代码
LPOP key [count]

LPOP 命令主要用于从列表(List)的左侧(即头部)取出一个或多个元素并将其删除。在执行时,如果不指定可选的 count 参数,默认只会从列表头部弹出一个元素;如果提供了 count 参数,则会返回多达指定数量的元素。其执行的时间复杂度为 O(N),其中 N 代表实际返回的元素数量;如果只弹出一个元素,运行极其高效,时间复杂度为 O(1)。命令执行完毕后,它会返回被取出的元素;如果指定的键不存在或列表已为空,则会返回 nil。同时,如果列表中的最后一个元素被成功弹出,Redis 会自动将这个空列表也一并删除。

bash 复制代码
RPOP key [count]

RPOP 命令则与 LPOP 的行为相对应,主要用于从列表的右侧(即尾部)取出一个或多个元素并将其删除。该命令同样支持通过可选的 count 参数来控制一次性从尾部弹出的元素上限。该命令的时间复杂度也与 LPOP 完全一致,即弹出 N 个元素的时间复杂度为 O(N),仅弹出单一元素时为 O(1)。命令成功执行后,它会返回被移除的元素内容;若列表为空或目标键不存在,则会返回 nil。同样地,当列表内的所有元素都被 RPOP 弹出完毕后,系统会自动删除该空列表。

bash 复制代码
LPOP list1
LPOP list1 2
RPOP list1
RPOP list1 3

4. LRANGE

LRANGE 命令主要用于获取 Redis 列表(List)中指定偏移量(索引)范围内的所有元素。该命令的基本语法为 LRANGE key start stop,其中 start 和 stop 均为基于零的整数索引,0 代表列表的第一个元素(即头部),1 代表第二个元素,依此类推。该命令同样支持负数索引,例如 -1 表示列表的最后一个元素(即尾部),-2 表示倒数第二个元素。需要特别注意的是,LRANGE 所提取的区间是左闭右闭的(inclusive),这意味着执行 LRANGE key 0 10 实际会返回 11 个元素。在处理超出范围的索引参数时,Redis 并不会抛出错误:如果传入的 start 值大于列表的实际末尾,命令会直接返回一个空列表;如果传入的 stop 值超出了列表的实际长度,Redis 会自动将其截断,按照列表的最后一个元素来处理。在执行效率方面,该命令的时间复杂度为 O(S+N),其中 S 是从列表头部或尾部(取较近的一端)到达 start 偏移量的距离,N 则是指定范围内包含的元素个数。

bash 复制代码
LPUSH list1 1 2 3
RPUSH list1 6 5 4

LRANGE list1 1 4
LRANGE list1 0 -3

LRANGE list1 3 0
LRANGE list1 6 8
LRANGE list1 2 8

5. LINDEX、LINSERT 和 LLEN

bash 复制代码
LINDEX key index

LINDEX 命令主要用于获取 Redis 列表(List)中指定索引位置的元素。在执行效率方面,时间复杂度为 O(N),其中 N 是到达指定索引所需遍历的元素数量,不过如果只是请求列表的第一个或最后一个元素,时间复杂度为 O(1)。如果给定的索引超出了列表的实际长度范围,该命令会返回 nil。

bash 复制代码
LINDEX list1 0
LINDEX list1 3
LINDEX list1 7
LINDEX list1 -2
LINDEX list1 -1
bash 复制代码
LINSERT key <BEFORE | AFTER> pivot element

LINSERT 命令主要用于在列表中特定的参考元素(pivot)之前或之后插入一个新元素。该命令的时间复杂度同样为 O(N),其中 N 代表在找到参考元素前需要遍历的元素数量;这意味着在列表头部附近插入效率接近 O(1),而在尾部附近插入耗时 O(N)。在执行时,如果目标键不存在,Redis 会将其视为空列表且不执行任何插入操作(返回 0);如果未能找到指定的参考元素,则返回 -1;如果成功插入,则返回插入后列表的新长度。

bash 复制代码
LINSERT list1 BEFORE 5 7
LINSERT list1 AFTER 5 6
LINSERT list2 BEFORE 1 10
LINSERT list1 AFTER 100 200
bash 复制代码
LLEN key

LLEN 命令主要用于获取指定列表当前的长度(即列表中包含的元素总数)。它的基本语法为 LLEN key。该命令的执行时间复杂度为 O(1),运行十分高效。在命令执行完毕后,如果指定的键存在,会返回列表的实际长度;如果指定的键不存在,Redis 会将其视为一个空列表并返回整数 0;但如果该键持有的值不是列表类型,则会报错。

6. LREM 和 LTRIM

bash 复制代码
LREM key count element

LREM 命令主要用于从列表(List)中删除指定数量的、与给定值匹配的元素。该命令的基本语法为 LREM key count element。参数 count 决定了删除的具体行为:如果 count > 0,系统会从列表头部(左侧)向尾部方向,移除数量为 count 的匹配元素;如果 count < 0,则从列表尾部(右侧)向头部方向移除绝对值数量的匹配元素;如果 count = 0,则会删除列表中所有与指定 element 相等的元素。在执行效率方面,其时间复杂度为 O(N+M),其中 N 是列表的总长度,M 是被成功移除的元素数量。命令执行完毕后,它会返回实际被移除的元素总数。需要注意的是,如果目标键不存在,Redis 会将其视为空列表处理并直接返回 0;同时,如果操作导致列表内的所有元素都被删除,Redis 会自动将这个空列表也一并移除。

bash 复制代码
LREM list1 4 5
LREM list1 -2 6
LREM list2 1 3
LREM list1 0 7
bash 复制代码
LTRIM key start stop

LTRIM 命令主要用于修剪(Trim)一个已存在的列表,使其仅保留指定索引范围内的元素,而位于该范围两端的所有元素都将被删除。其基本语法为 LTRIM key start stop,其中 start 和 stop 均为基于零的整数索引,且同样支持使用负数来表示从尾部起算的偏移量。在处理越界参数时,该命令表现得十分宽容且不会抛出错误:如果 start 大于列表的实际末部,或者 start 大于 stop,执行结果将是一个空列表(进而导致该键被自动删除);如果 stop 超出了列表的实际长度,Redis 会将其自动截断并视为列表的最后一个元素。该命令的执行时间复杂度为 O(N),这里的 N 代表被该操作移除的元素数量。在实际应用中,LTRIM 经常与 LPUSH 或 RPUSH 命令结合使用,以控制列表不会超过指定的固定长度(例如用于存储固定条数的日志),在这种特定场景下,由于每次基本只需移除一个元素,其操作效率实际上可视为 O(1)。命令执行成功后,它会返回一个简单的字符串回复 OK。

7. LSET

bash 复制代码
LSET key index element

LSET 命令主要用于根据索引修改 Redis 列表(List)中特定位置元素的值。在执行效率方面,其时间复杂度为 O(N),其中 N 代表列表的总长度;但如果只是修改列表的第一个元素(头部)或最后一个元素(尾部),操作会非常高效,时间复杂度仅为 O(1)。需要特别注意的是,如果传入的索引超出了列表的实际范围,该命令会返回一个错误(error)。命令成功执行完毕后,系统会返回一个简单的字符串回复 OK。

1.3. 阻塞版本命令

bash 复制代码
BLPOP key [key ...] timeout
BRPOP key [key ...] timeout

在 Redis 中,BLPOP 和 BRPOP 分别是列表类型命令 LPOP 和 RPOP 的阻塞版本,它们在列表内部有元素时的表现与对应的非阻塞版本是完全一致的。这两类命令的主要区别体现在列表为空的情境下:非阻塞版本会立刻返回 nil,而阻塞版本则会根据指定的 timeout 参数使客户端阻塞等待一段时间。在这段阻塞期间,Redis 服务端依然能够正常处理其他的命令,但发起该请求的客户端会表现为阻塞状态。

同时,BLPOP 和 BRPOP 命令在语法上支持设置多个键,执行时系统会从左向右依次遍历这些被监听的键,只要其中任何一个键对应的列表中可以弹出元素,命令就会立刻结束并返回结果。

此外,如果出现多个客户端同时对同一个键执行阻塞弹出的操作,那么当新元素被加入列表时,最先执行阻塞命令的客户端将会优先得到该弹出的元素。最后,这两个命令的时间复杂度均为 O(1),当操作成功时会返回取出的元素,若达到超时时间仍无元素可弹则会返回 nil。

bash 复制代码
RPUSH list2 a b c
BLPOP list1 list2 10
BLPOP list3 10

1.4. 内部编码机制

列表类型在 Redis 底层有两种内部编码实现:

  1. ziplist (压缩列表):当列表的元素个数小于 list-max-ziplist-entries 配置(默认 512 个),并且每个元素的长度都小于 list-max-ziplist-value 配置(默认 64 字节)时,Redis 会选用 ziplist 作为内部实现以减少内存消耗。
  2. linkedlist (链表):当列表类型无法满足 ziplist 的条件时(例如元素个数过多或存在大元素),Redis 会将内部实现转换为 linkedlist。

1.5. 典型应用场景

  1. 消息队列:可以使用 lpush 结合 brpop 命令组合实现经典的阻塞式生产者-消费者模型队列。多个消费者客户端可以阻塞式地从队列中争抢队首元素,从而保证消费的负载均衡和高可用性。
  2. 分频道消息队列:通过对不同的键使用 lpush 和 brpop 命令,可以模拟频道的概念,实现订阅不同频道的功能。
  3. 时间轴 / 文章列表 (Timeline):例如微博的时间线,可以使用列表存储文章记录,并使用 lrange 命令进行索引范围获取来实现分页展示功能。针对大列表的分页,文档也提醒在获取中间元素时性能较差,可以考虑将列表做拆分。
相关推荐
吳所畏惧5 小时前
宝塔面板Redis密码修改指南:SSH命令修改 vs 面板UI界面修改,哪个更靠谱?
运维·服务器·数据库·redis·缓存·ssh
Y38153266210 小时前
SERP API + Redis 缓存层:4 种方案对比与选型
数据库·redis·缓存
海兰12 小时前
【高速缓存】RedisVL 高级查询(全文搜索、混合搜索和 多向量搜索)
数据库·人工智能·redis·缓存
流星白龙1 天前
【Redis】4.基本全局命令
数据库·redis·缓存
流星白龙1 天前
【Redis】3.Redis安装与命令行客户端
数据库·redis·缓存
我不想名字重复1 天前
redis缓存和数据库数据保持一致
数据库·redis·缓存
时空无限1 天前
vllm 大模型启动缓存相关环境变量 export
linux·缓存·vllm
流星白龙2 天前
【Redis】6.计数其他命令
数据库·redis·缓存
正儿八经的少年2 天前
redis 的大 key 和热 key 详解
数据库·redis·缓存
X-⃢_⃢-X2 天前
十、Redis之布隆过滤器
数据库·redis·缓存