Redis开发与运维. 2.4 列表

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介:

2.4 列表

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

 

图2-18 列表两端插入和弹出操作

 

图2-19 子列表获取、删除等操作

列表类型有两个特点:第一、列表中的元素是有序的,这就意味着可以通过索引下标获取某个元素或者某个范围内的元素列表,例如要获取图2-19的第5个元素,可以执行lindex user:1:message 4(索引从0算起)就可以得到元素e。第二、列表中的元素可以是重复的,例如图2-20所示列表中包含了两个字符串a。

 

图2-20 列表中可以包含重复元素

这两个特点在后面介绍集合和有序集合后,会显得更加突出,因此在考虑是否使用该数据结构前,首先需要弄清楚列表数据结构的特点。

2.4.1 命令

下面将按照对列表的5种操作类型对命令进行介绍,命令如表2-4所示。

1.?添加操作

(1)从右边插入元素

rpush key value [value ...]

下面代码从右向左插入元素c、b、a:

127.0.0. 1:6379> rpush listkey c b a

(integer) 3

lrange 0 -1命令可以从左到右获取列表的所有元素:

127.0.0.1:6379> lrange listkey 0 -1

1) "c"

2) "b"

3) "a"

(2)从左边插入元素

lpush key value [value ...]

使用方法和rpush相同,只不过从左侧插入,这里不再赘述。

(3)向某个元素前或者后插入元素

linsert key before|after pivot value

linsert命令会从列表中找到等于pivot的元素,在其前(before)或者后(after)插入一个新的元素value,例如下面操作会在列表的元素b前插入java:

127.0.0.1:6379> linsert listkey before b java

(integer) 4

返回结果为4,代表当前命令的长度,当前列表变为:

127.0.0.1:6379> lrange listkey 0 -1

1) "c"

2) "java"

3) "b"

4) "a"

2.?查找

(1)获取指定范围内的元素列表

lrange key start end

lrange操作会获取列表指定索引范围所有的元素。索引下标有两个特点:第一,索引下标从左到右分别是0到N-1,但是从右到左分别是-1到-N。第二,lrange中的end选项包含了自身,这个和很多编程语言不包含end不太相同,例如想获取列表的第2到第4个元素,可以执行如下操作:

127.0.0.1:6379> lrange listkey 1 3

1) "java"

2) "b"

3) "a"

(2)获取列表指定索引下标的元素

lindex key index

例如当前列表最后一个元素为a:

127.0.0.1:6379> lindex listkey -1

"a"

(3)获取列表长度

llen key

例如,下面示例当前列表长度为4:

127.0.0.1:6379> llen listkey

(integer) 4

3.?删除

(1)从列表左侧弹出元素

lpop key

如下操作将列表最左侧的元素c会被弹出,弹出后列表变为java、b、a:

127.0.0.1:6379>t lpop listkey

"c"

127.0.0.1:6379> lrange listkey 0 -1

1) "java"

2) "b"

3) "a"

(2) 从列表右侧弹出

rpop key

它的使用方法和lpop是一样的,只不过从列表右侧弹出,这里不再赘述。

(3)删除指定元素

lrem key count value

lrem命令会从列表中找到等于value的元素进行删除,根据count的不同分为三种情况:

count>0,从左到右,删除最多count个元素。

count<0,从右到左,删除最多count绝对值个元素。

count=0,删除所有。

例如向列表从左向右插入5个a,那么当前列表变为“a a a a a java b a”,下面操作将从列表左边开始删除4个为a的元素:

127.0.0.1:6379> lrem listkey 4 a

(integer) 4

127.0.0.1:6379> lrange listkey 0 -1

1) "a"

2) "java"

3) "b"

4) "a"

(4)按照索引范围修剪列表

ltrim key start end

例如,下面操作会只保留列表listkey第2个到第4个元素:

127.0.0.1:6379> ltrim listkey 1 3

OK

127.0.0.1:6379> lrange listkey 0 -1

1) "java"

2) "b"

3) "a"

4.?修改

修改指定索引下标的元素:

lset key index newValue

下面操作会将列表listkey中的第3个元素设置为python:

127.0.0.1:6379> lset listkey 2 python

OK

127.0.0.1:6379> lrange listkey 0 -1

1) "java"

2) "b"

3) "python"

5.?阻塞操作

阻塞式弹出如下:

blpop key [key ...] timeout

brpop key [key ...] timeout

blpop和brpop是lpop和rpop的阻塞版本,它们除了弹出方向不同,使用方法基本相同,所以下面以brpop命令进行说明,brpop命令包含两个参数:

key [key ...]:多个列表的键。

timeout:阻塞时间(单位:秒)。

1)列表为空:如果timeout=3,那么客户端要等到3秒后返回,如果timeout=0,那么客户端一直阻塞等下去:

127.0.0.1:6379> brpop list:test 3

(nil)

(3.10s)

127.0.0.1:6379> brpop list:test 0

...阻塞...

如果此期间添加了数据element1,客户端立即返回:

127.0.0.1:6379> brpop list:test 3

1) "list:test"

2) "element1"

(2.06s)

2)列表不为空:客户端会立即返回。

127.0.0.1:6379> brpop list:test 0

1) "list:test"

2) "element1"

在使用brpop时,有两点需要注意。

第一点,如果是多个键,那么brpop会从左至右遍历键,一旦有一个键能弹出元素,客户端立即返回:

127.0.0.1:6379> brpop list:1 list:2 list:3 0

..阻塞..

此时另一个客户端分别向list:2和list:3插入元素:

client-lpush> lpush list:2 element2

(integer) 1

client-lpush> lpush list:3 element3

(integer) 1

客户端会立即返回list:2中的element2,因为list:2最先有可以弹出的元素:

127.0.0.1:6379> brpop list:1 list:2 list:3 0

1) "list:2"

2) "element2_1"

第二点,如果多个客户端对同一个键执行brpop,那么最先执行brpop命令的客户端可以获取到弹出的值。

客户端1:

client-1> brpop list:test 0

...阻塞...

客户端2:

client-2> brpop list:test 0

...阻塞...

客户端3:

client-3> brpop list:test 0

...阻塞...

此时另一个客户端lpush一个元素到list:test列表中:

client-lpush> lpush list:test element

(integer) 1

那么客户端1最会获取到元素,因为客户端1最先执行brpop,而客户端2和客户端3继续阻塞:

client> brpop list:test 0

1) "list:test"

2) "element"

有关列表的基础命令已经介绍完了,表2-5是这些命令的时间复杂度,开发人员可以参考此表选择适合的命令。

表2-5 列表命令时间复杂度

操作类型         命  令         时间复杂度

添加         rpush key value [value ...]        O(k),k是元素个数

         lpush key value [value ...]         O(k),k是元素个数

         linsert key before|after pivot value         O(n),n是pivot距离列表头或尾的距离

查找         lrange key start end         O(s+n),s是start偏移量,n是start到end

的范围

         lindex key index        O(n),n是索引的偏移量

         llen key     O(1)

删除         lpop key   O(1)

         rpop key   O(1)

         lrem count value      O(n),n是列表长度

         ltrim key start end   O(n),n是要裁剪的元素总数

修改         lset key index value O(n),n是索引的偏移量

阻塞操作         blpop brpop      O(1)

 

2.4.2 内部编码

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

ziplist(压缩列表):当列表的元素个数小于list-max-ziplist-entries配置(默认512个),同时列表中每个元素的值都小于list-max-ziplist-value配置时(默认64字节),Redis会选用ziplist来作为列表的内部实现来减少内存的使用。

linkedlist(链表):当列表类型无法满足ziplist的条件时,Redis会使用linkedlist作为列表的内部实现。

下面的示例演示了列表类型的内部编码,以及相应的变化。

1)当元素个数较少且没有大元素时,内部编码为ziplist:

127.0.0.1:6379> rpush listkey e1 e2 e3

(integer) 3

127.0.0.1:6379> object encoding listkey

"ziplist"

2.1)当元素个数超过512个,内部编码变为linkedlist:

127.0.0.1:6379> rpush listkey e4 e5 ...忽略... e512 e513

(integer) 513

127.0.0.1:6379> object encoding listkey

"linkedlist"

2.2)或者当某个元素超过64字节,内部编码也会变为linkedlist:

127.0.0.1:6379> rpush listkey "one string is bigger than 64 byte...............

    ................."

(integer) 4

127.0.0.1:6379> object encoding listkey

"linkedlist"

Redis 3.2版本提供了quicklist内部编码,简单地说它是以一个ziplist为节点的linkedlist,它结合了ziplist和linkedlist两者的优势,为列表类型提供了一种更为优秀的内部编码实现,它的设计原理可以参考Redis的另一个作者Matt Stancliff的博客:https://matt.sh/redis-quicklist。

有关列表类型的内存优化技巧将在8.3节详细介绍。

2.4.3 使用场景

1.?消息队列

如图2-21所示,Redis的lpush+brpop命令组合即可实现阻塞队列,生产者客户端使用lrpush从列表左侧插入元素,多个消费者客户端使用brpop命令阻塞式的“抢”列表尾部的元素,多个客户端保证了消费的负载均衡和高可用性。

2.?文章列表

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

 

图2-21 Redis消息队列模型

1)每篇文章使用哈希结构存储,例如每篇文章有3个属性title、timestamp、content:

hmset acticle:1 title xx timestamp 1476536196 content xxxx

...

hmset acticle:k title yy timestamp 1476512536 content yyyy

...

2)向用户文章列表添加文章,user:{id}:articles作为用户文章列表的键:

lpush user:1:acticles article:1 article3

...

lpush user:k:acticles article:5

...

3)分页获取用户文章列表,例如下面伪代码获取用户id=1的前10篇文章:

articles = lrange user:1:articles 0 9

for article in {articles}

    hgetall {article}

使用列表类型保存和获取文章列表会存在两个问题。第一,如果每次分页获取的文章个数较多,需要执行多次hgetall操作,此时可以考虑使用Pipeline(第3章会介绍)批量获取,或者考虑将文章数据序列化为字符串类型,使用mget批量获取。第二,分页获取文章列表时,lrange命令在列表两端性能较好,但是如果列表较大,获取列表中间范围的元素性能会变差,此时可以考虑将列表做二级拆分,或者使用Redis 3.2的quicklist内部编码实现,它结合ziplist和linkedlist的特点,获取列表中间范围的元素时也可以高效完成。

实际上列表的使用场景很多,在选择时可以参考以下口诀:

lpush + lpop = Stack(栈)

lpush + rpop = Queue(队列)

lpsh + ltrim = Capped Collection(有限集合)

lpush + brpop = Message Queue(消息队列)

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
打赏
0
0
0
0
1408
分享
相关文章
AI大模型运维开发探索第四篇:智能体分阶段演进路线
本文探讨了智能体工程的演进历程,从最初的思维链(智能体1.0)到实例化智能体(智能体2.0),再到结构化智能体(智能体3.0),最终展望了自演进智能体(智能体4.0)。文章详细分析了各阶段遇到的问题及解决策略,如工具调用可靠性、推理能力提升等,并引入了大模型中间件的概念以优化业务平台与工具间的协调。此外,文中还提到了RunnableHub开源项目,为读者提供了实际落地的参考方案。通过不断迭代,智能体逐渐具备更强的适应性和解决问题的能力,展现了未来AI发展的潜力。
|
1月前
|
基于SpringBoot的Redis开发实战教程
Redis在Spring Boot中的应用非常广泛,其高性能和灵活性使其成为构建高效分布式系统的理想选择。通过深入理解本文的内容,您可以更好地利用Redis的特性,为应用程序提供高效的缓存和消息处理能力。
163 79
Redis应用—2.在列表数据里的应用
本文介绍了基于数据库和缓存双写的分享贴功能设计,包括:基于数据库 + 缓存双写的分享贴功能、查询分享贴列表缓存时的延迟构建、分页列表惰性缓存方案、用户分享贴列表数据按页缓存实现精准过期控制、用户分享贴列表的分页缓存异步更新、数据库与缓存的分页数据一致性方案、热门用户分享贴列表的分页缓存失效时消除并发线程串行等待锁的影响。总结:该设计通过合理的缓存策略和异步处理机制,有效提升了系统性能,降低了内存占用,并确保了数据的一致性和高可用性。
Redis应用—2.在列表数据里的应用
Spring运维之boot项目多环境(yaml 多文件 proerties)及分组管理与开发控制
通过以上措施,可以保证Spring Boot项目的配置管理在专业水准上,并且易于维护和管理,符合搜索引擎收录标准。
186 2
Redis 列表(List)
10月更文挑战第16天
80 2
|
6月前
|
【运维基础知识】掌握VI编辑器:提升你的Java开发效率
本文详细介绍了VI编辑器的常用命令,包括模式切换、文本编辑、搜索替换及退出操作,帮助Java开发者提高在Linux环境下的编码效率。掌握这些命令,将使你在开发过程中更加得心应手。
75 2
|
5月前
|
Redis命令:列表模糊删除详解
通过本文的介绍,我们详细探讨了如何在Redis中实现列表的模糊删除。虽然Redis没有直接提供模糊删除命令,但可以通过组合使用 `LRANGE`和 `LREM`命令,并在客户端代码中进行模糊匹配,来实现这一功能。希望本文能帮助你在实际应用中更有效地操作Redis列表。
198 0
Redis常见面试题:ZSet底层数据结构,SDS、压缩列表ZipList、跳表SkipList
String类型底层数据结构,List类型全面解析,ZSet底层数据结构;简单动态字符串SDS、压缩列表ZipList、哈希表、跳表SkipList、整数数组IntSet
|
6月前
|
实时计算Flink版在稳定性、性能、开发运维、安全能力等等跟其他引擎及自建Flink集群比较。
实时计算Flink版在稳定性、性能、开发运维和安全能力等方面表现出色。其自研的高性能状态存储引擎GeminiStateBackend显著提升了作业稳定性,状态管理优化使性能提升40%以上。核心性能较开源Flink提升2-3倍,资源利用率提高100%。提供一站式开发管理、自动化运维和丰富的监控告警功能,支持多语言开发和智能调优。安全方面,具备访问控制、高可用保障和全链路容错能力,确保企业级应用的安全与稳定。
113 0
如何使用 C++ 开发 Redis 模块
如何使用 C++ 开发 Redis 模块

热门文章

最新文章

下一篇
oss创建bucket