Redis中List(列表)类型的常用命令


一、 List类型简要介绍

列表类型是用来存储多个有序的字符串,如图 2-19 所示,a、b、c、d、e 五个元素从左到右组成了一个有序的列表,列表中的每个字符串称为元素(element),一个列表最多可以存储 2 32 2^{32} 232-1个元素。在 Redis 中,可以对列表两端插入(push)和弹出(pop),还可以获取指定范围的元素列表、获取指定索引下标的元素等(如图 2-19 和图 2-20 所示)。列表是一种比较灵活的数据结构,它可以充当栈和队列的角色,在实际开发上有很多应用场景。

在这里插入图片描述
在这里插入图片描述

列表类型的特点:

  • 第一、列表中的元素是有序的,这意味着可以通过索引下标获取某个元素或者某个范围的元素列表,例如要获取图 2-20 的第 5 个元素,可以执行 lindex user:1:messages 4 或者倒数第 1 个元素,lindex user:1:messages -1 就可以得到元素 e。
  • 第二、区分获取和删除的区别,例如图 2-20 中的 lrem 1 b 是从列表中把从左数遇到的前 1 个 b 元素删除,这个操作会导致列表的长度从 5 变成 4;但是执行 lindex 4 只会获取元素,但列表长度是不会变化的。
  • 第三、列表中的元素是允许重复的,例如图 2-21 中的列表中是包含了两个 a 元素的。

在这里插入图片描述

二、常用命令

lpush

LPUSH

  • 将一个或者多个元素从左侧放入(头插)到 list 中。
  • 语法:LPUSH key element [element ...]
  • 命令有效版本:1.0.0 之后
  • 时间复杂度:只插入一个元素为 O(1), 插入多个元素为 O(N), N 为插入元素个数.
  • 返回值:插入后 list 的长度。
  • 示例:

在这里插入图片描述

lpushx

LPUSHX

  • 在 key 存在时,将一个或者多个元素从左侧放入(头插)到 list 中。不存在,直接返回
  • 语法:LPUSHX key element [element ...]
  • 命令有效版本:2.0.0 之后
  • 时间复杂度:只插入一个元素为 O(1), 插入多个元素为 O(N), N 为插入元素个数.
  • 返回值:插入后 list 的长度。
  • 示例:

在这里插入图片描述

rpush

RPUSH

  • 将一个或者多个元素从右侧放入(尾插)到 list 中。
  • 语法: RPUSH key element 1 [element ...]
  • 命令有效版本:1.0.0 之后
  • 时间复杂度:只插入一个元素为 O(1), 插入多个元素为 O(N), N 为插入元素个数.
  • 返回值:插入后 list 的长度。
  • 示例:

在这里插入图片描述

rpushx

RPUSHX

  • 在 key 存在时,将一个或者多个元素从右侧放入(尾插)到 list 中。
  • 语法: RPUSHX key element [element ...]
  • 命令有效版本:2.0.0 之后
  • 时间复杂度:只插入一个元素为 O(1), 插入多个元素为 O(N), N 为插入元素个数.
  • 返回值:插入后 list 的长度。
  • 示例:

在这里插入图片描述

lrange

LRANGE

  • 获取从 start 到 end 区间的所有元素,左闭右闭,下标从0开始。
  • 语法: LRANGE key start stop
  • 命令有效版本:1.0.0 之后
  • 时间复杂度:O(N)
  • 返回值:指定区间的元素。
  • 示例:

在这里插入图片描述

lpop

LPOP

  • 从 list 左侧取出元素(即头删),count表示删除的个数,不写默认头删1个元素。
  • 语法: LPOP key [count]
  • 命令有效版本:1.0.0 之后
  • 时间复杂度:O(1)
  • 返回值:取出的元素或者 nil。
  • 示例:

在这里插入图片描述

rpop

RPOP

  • 从 list 右侧取出元素(即尾删)。
  • 语法:RPOP key
  • 命令有效版本:1.0.0 之后
  • 时间复杂度:O(1)
  • 返回值:取出的元素或者 nil。
  • 示例:

在这里插入图片描述

lindex

LINDEX

  • 获取从左数第 index 位置的元素。
  • 语法:LINDEX key index
  • 命令有效版本:1.0.0 之后
  • 时间复杂度:O(N)
  • 返回值:取出的元素或者 nil。
  • 示例:

在这里插入图片描述

linsert

LINSERT

  • 在特定位置插入元素。
  • 语法:LINSERT key <BEFORE | AFTER> 1 pivot element
  • 命令有效版本:2.2.0 之后
  • 时间复杂度:O(N)
  • 返回值:插入后的 list 长度。
  • 示例:

在这里插入图片描述

len

LLEN

  • 获取 list 长度。
  • 语法: LLEN key
  • 命令有效版本:1.0.0 之后
  • 时间复杂度:O(1)
  • 返回值:list 的长度。
  • 示例:

在这里插入图片描述

阻塞版本命令前言

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

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

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

blpop

BLPOP

  • LPOP 的阻塞版本。
  • 语法:BLPOP key 1 [key ...] timeout
  • 命令有效版本:1.0.0 之后
  • 时间复杂度:O(1)
  • 返回值:取出的元素或者 nil。
  • 示例:

在这里插入图片描述

brpop

BRPOP

  • RPOP 的阻塞版本。
  • 语法: BRPOP key [key ...] timeout
  • 命令有效版本:1.0.0 之后
  • 时间复杂度:O(1)
  • 返回值:取出的元素或者 nil。
  • 示例:

在这里插入图片描述

lrem

  • 根据参数 count 的值,移除列表中与 value 相等的元素。
  • 语法:LREM key count value
  • 命令有效版本:多个版本(具体取决于Redis的版本历史,但广泛支持)
  • 时间复杂度:O(k),k 是被遍历的元素数量,直到找到足够的元素或者列表被遍历完。
  • 返回值:被移除的元素的数量。
  • 示例:LREM key1 2 "a" 从列表 key1 中移除最多 2 个等于 "a" 的元素。

在这里插入图片描述

ltrim

  • 对一个列表进行修剪(trim),只保留指定区间内的元素,不在指定区间内的元素都将被删除。
  • 语法:LTRIM key start stop
  • 命令有效版本:多个版本(具体取决于Redis的版本历史,但广泛支持)
  • 时间复杂度:O(k),k 是被移除的元素数量。
  • 返回值:总是返回 “OK”。
  • 示例:LTRIM key1 2 4 只保留列表 key1 中索引为 2、3 和 4 的元素,移除其他所有元素。

在这里插入图片描述

lset

  • 将列表 key 中索引为 index 的元素的值设置为 value
  • 语法:LSET key index value
  • 命令有效版本:多个版本(具体取决于Redis的版本历史,但广泛支持)
  • 时间复杂度:O(n),n 是索引的偏移量,但实际上Redis的实现通常使得这个操作非常快。
  • 返回值:如果操作成功,返回 “OK”。如果索引超出范围,返回错误。
  • 示例:

在这里插入图片描述

注意:对于lset的时间复杂度,虽然理论上它可能与索引的偏移量成线性关系,但实际上Redis的内部实现通常会使得这个操作非常高效,因为Redis的列表数据结构(通常基于双向链表或类似结构)允许在常数时间内进行索引访问(在知道索引位置的情况下)。然而,为了符合通常的复杂度描述习惯,这里仍然使用了O(n)来表示。在实际应用中,这个操作的性能通常不是问题。

三、 常用命令小结

操作类型命令时间复杂度
添加rpush key value [value …]O(k),k 是元素个数
添加lpush key value [value …]O(k),k 是元素个数
添加linsert key beforeafter pivot value
查找lrange key start endO(s+n),s 是 start 偏移量,n 是 start 到 end 的范围
查找lindex key indexO(n),n 是索引的偏移量
查找llen keyO(1)
删除lpop keyO(1)
删除rpop keyO(1)
删除lrem key count valueO(k),k 是元素个数
删除ltrim key start endO(k),k 是元素个数
修改lset key index valueO(n),n 是索引的偏移量
阻塞操作blpopO(1)
阻塞操作brpopO(1)

四、 内部编码方式

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

  • ziplist(压缩列表):当列表的元素个数小于 list-max-ziplist-entries 配置(默认 512 个),同时列表中每个元素的长度都小于 list-max-ziplist-value 配置(默认 64 字节)时,Redis 会选用ziplist 来作为列表的内部编码实现来减少内存消耗。
  • linkedlist(链表):当列表类型无法满足 ziplist 的条件时,Redis 会使用linkedlist 作为列表的内部实现。
  1. 当元素个数较少且没有大元素时,内部编码为 ziplist:

在这里插入图片描述
2. 当元素个数超过 512 时,内部编码为 linkedlist:

127.0.0.1:6379> rpush listkey e1 e2 e3 ... 省略 e512 e513
OK
127.0.0.1:6379> object encoding listkey
"linkedlist"

由于插入元素太多,这里就不演示了。

  1. 当某个元素的长度超过 64 字节时,内部编码为 linkedlist:

在这里插入图片描述

经过实际操作我们会发现实际上的类型都是quicklist,以下是有关quicklist的介绍。

quicklist的引入时间

  • Redis从3.2版本开始,对列表数据结构进行了改造,引入了quicklist这一新的数据结构来替换之前的ziplist和linkedlist。这一改动旨在解决链表(linkedlist)内存占用高、碎片化严重以及压缩列表(ziplist)在元素较多时查询效率低的问题。

quicklist的特点

  • quicklist是ziplist和linkedlist的混合体,它将linkedlist按段切分,每一段使用ziplist来紧凑存储,多个ziplist之间使用双向指针串接起来。
  • quicklist通过合理配置ziplist的大小和压缩深度,可以在保证操作效率的同时,最大限度地减少内存占用和碎片化。
  • quicklist的设计使得Redis在处理列表数据时,既能够高效地添加、删除元素(特别是在列表两端),又能够在元素数量较多时保持较好的查询性能。

在这里插入图片描述

五、 典型使用场景

消息队列

如图 2-22 所示,Redis 可以使用 lpush + brpop 命令组合实现经典的阻塞式生产者-消费者模型队列,生产者客户端使用 lpush 从列表左侧插入元素,多个消费者客户端使用 brpop 命令阻塞式地从队列中"争抢" 队首元素。通过多个客户端来保证消费的负载均衡和高可用性。

在这里插入图片描述

分频道的消息队列

如图 2-23 所示,Redis 同样使用 lpush + brpop 命令,但通过不同的键模拟频道的概念,不同的消费者可以通过 brpop 不同的键值,实现订阅不同频道的理念。

在这里插入图片描述

微博 Timeline

每个用户都有属于自己的 Timeline(微博列表),现需要分页展示文章列表。此时可以考虑使用列表,因为列表不但是有序的,同时支持按照索引范围获取元素。

  1. 每篇微博使用哈希结构存储,例如微博中 3 个属性:title、timestamp、content:
hmset mblog:1 title xx timestamp 1476536196 content xxxxx
...
hmset mblog:n title xx timestamp 1476536196 content xxxxx
  1. 向用户 Timeline 添加微博,user::mblogs 作为微博的键:
lpush user:1:mblogs mblog:1 mblog:3
...
lpush user:k:mblogs mblog:9
  1. 分页获取用户的 Timeline,例如获取用户 1 的前 10 篇微博:
keylist = lrange user:1:mblogs 0 9
for key in keylist {
	hgetall key
}

此方案在实际中可能存在两个问题:

  1. 1 + n 问题。即如果每次分页获取的微博个数较多,需要执行多次 hgetall 操作,此时可以考虑使用pipeline(流水线)模式批量提交命令,或者微博不采用哈希类型,而是使用序列化的字符串类型,使用 mget 获取。
  2. 分裂获取文章时,lrange 在列表两端表现较好,获取列表中间的元素表现较差,此时可以考虑将列表做拆分。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

争不过朝夕,又念着往昔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值