当前位置: 首页 > news >正文

【Redis】常用数据结构之List篇:从常用命令到典型使用场景


目录

1.前言

插播一条消息~

2.正文

2.1List概念特点

2.2常用命令

2.2.1lpush

2.2.2lpushx

2.2.3rpush & rpushx

2.2.4lrange

2.2.5lpop & rpop

2.2.6lindex

2.2.7linsert

2.2.8llen

2.2.9lrem

2.2.10ltrim

2.3阻塞版本命令

2.3.1blpop & brpop

2.4内部编码

2.5使用场景

2.5.1消息队列

2.5.2分频道阻塞消息队列

2.5.3微博Timeline实现

3.小结


1.前言

Redis 作为一款高性能的内存数据库,以其低延迟高并发支持特性,在实时数据处理场景中占据重要地位。其基于内存的存储架构与高效的 I/O 模型,能够满足毫秒级响应需求,广泛应用于缓存系统、消息队列、实时分析等核心业务场景。在 Redis 提供的多样化数据结构体系中,包括 String、Hash、List、Set、Sorted Set 等,List 结构因其独特的双端操作能力和灵活的内存管理机制,成为高频使用的数据结构之一。

本文将系统探讨 Redis List 的核心特性、完整命令体系、底层存储实现以及典型实践场景,为读者构建从理论到应用的完整认知框架,助力开发者在实际业务中高效运用这一数据结构解决问题。


插播一条消息~

🔍十年经验淬炼 · 系统化AI学习平台推荐

系统化学习AI平台https://www.captainbed.cn/scy/

  • 📚 完整知识体系:从数学基础 → 工业级项目(人脸识别/自动驾驶/GANs),内容由浅入深
  • 💻 实战为王:每小节配套可运行代码案例(提供完整源码)
  • 🎯 零基础友好:用生活案例讲解算法,无需担心数学/编程基础

🚀 特别适合

  • 想系统补强AI知识的开发者
  • 转型人工智能领域的从业者
  • 需要项目经验的学生

2.正文

2.1List概念特点

Redis List是有序可重复的字符串集合,底层基于双端链表实现,支持在两端高效插入/删除元素,因此可同时作为栈(LIFO)队列(FIFO) 使用:

  • 栈模式:通过LPUSH(左侧入栈)和LPOP(左侧出栈)实现
  • 队列模式:通过LPUSH(左侧入队)和RPOP(右侧出队)实现

生活类比:可将List比作"双向传送带",既可以从左侧放入物品(LPUSH),也可以从右侧放入(RPUSH);取出时同样支持两侧操作,且物品顺序严格按照放入顺序排列,允许出现相同物品。

2.2常用命令

2.2.1lpush

概念:从列表左侧插入一个或多个元素,类似"栈顶入栈"操作。

语法

LPUSH key element [element ...]

时间复杂度:O(1)(无论插入多少元素,链表头插无需遍历)

返回值:插入后列表的长度(整数)

示例

# 向空列表插入元素
LPUSH stack a b  # 返回3,列表变为[a,b](注:实际存储顺序为b,a,因左侧插入)
LRANGE stack 0 -1  # 返回1) "b" 2) "a"

2.2.2lpushx

概念:仅当列表存在时才从左侧插入元素(X=eXists)。

语法

LPUSHX key element [element ...]

关键区别:与LPUSH的唯一差异是,若key不存在则不创建列表,直接返回0。

示例

LPUSHX newlist x  # 返回0(newlist不存在)
LPUSH list1 a     # 创建list1并返回1
LPUSHX list1 b    # 返回2,列表变为[b,a]

2.2.3rpush & rpushx

RPUSH(右侧插入)与LPUSH对称,从列表尾部插入元素:

RPUSH queue task1 task2  # 返回2,列表为[task1,task2]

RPUSHX(条件右侧插入)与LPUSHX逻辑一致,仅当列表存在时执行:

RPUSHX log "2023-10-01"  # 若log存在则返回新长度,否则返回0

2.2.4lrange

概念:获取列表指定索引范围的元素,支持正/负索引。

语法

LRANGE key start stop

参数说明

  • start:起始索引(0表示第一个元素)
  • stop:结束索引(-1表示最后一个元素)

示例

LRANGE mylist 0 2   # 获取前3个元素
LRANGE mylist -3 -1 # 获取最后3个元素

边界处理:当stop超出实际索引时,自动返回至列表末尾。例如列表长度为5,LRANGE key 0 10将返回所有5个元素。


2.2.5lpop & rpop

LPOP(左侧弹出):移除并返回列表第一个元素

LPOP stack  # 返回"b",列表变为[a]

RPOP(右侧弹出):移除并返回列表最后一个元素

RPOP queue  # 返回"task2",列表变为[task1]

2.2.6lindex

概念:通过索引获取元素(类似数组下标访问)。

语法

LINDEX key index

时间复杂度:O(N)(需遍历至指定索引)

示例

LINDEX mylist 2   # 获取第3个元素
LINDEX mylist -1  # 获取最后一个元素

性能提示:避免频繁访问大索引元素(如index>1000),建议用LRANGE批量获取。


2.2.7linsert

概念:在指定基准元素的前后插入新元素。

语法

LINSERT key BEFORE|AFTER pivot value

多基准值处理:仅匹配从左到右第一个基准元素

返回值

  • 成功:插入后列表长度
  • 失败:pivot不存在返回-1,key不存在返回0

示例

# 原列表:a,b,c,b
LINSERT mylist AFTER b x  # 返回5,结果:a,b,x,c,b(仅第一个b后插入)

2.2.8llen

概念:获取列表长度,时间复杂度O(1)(Redis内部维护length计数器)。

语法

LLEN key

示例

LLEN user:1:follows  # 返回5(用户1关注了5人)

2.2.9lrem

概念:删除指定数量的匹配元素,通过count控制删除方向和数量。

语法

LREM key count element

count参数规则

  • 正数:从左侧删除count个匹配元素
  • 负数:从右侧删除|count|个匹配元素
  • 0:删除所有匹配元素

示例

LREM mylist 2 a     # 从左删除2个a
LREM mylist -1 b    # 从右删除1个b
LREM mylist 0 c     # 删除所有c

2.2.10ltrim

概念:保留指定范围元素,删除范围外元素(裁剪列表)。

语法

LTRIM key start stop

典型应用:限制列表长度(如保留最近100条日志)


2.3阻塞版本命令

2.3.1blpop & brpop

BLPOP(阻塞左侧弹出):

BLPOP queue1 queue2 5  # 按顺序监听queue1、queue2,5秒超时

BRPOP(阻塞右侧弹出):

BRPOP notifications 0  # 永久阻塞监听通知队列

应用场景:实时消息处理,如聊天系统的消息推送。

核心差异总结:阻塞命令通过内置阻塞机制实现资源高效利用,适合需实时响应且避免轮询开销的场景;非阻塞命令逻辑简单直接,更适用于对实时性要求低的单次元素获取或清理操作。实际应用中需根据业务对响应速度和资源效率的需求选择合适命令。

对比维度阻塞命令(BLPOP/BRPOP)非阻塞命令(LPOP/RPOP)
触发条件当列表为空时,命令会阻塞连接直至超时或有新元素插入当列表为空时,立即返回 nil 结果
返回值返回包含 [key, 元素值] 的数组(因支持多 key 输入)直接返回弹出的单个元素值(空列表返回 nil
参数要求需指定超时时间(timeout),且支持同时传入多个 key仅需指定目标列表 key,无其他参数
资源消耗阻塞期间无轮询操作,CPU 资源占用低若需模拟阻塞效果需客户端轮询调用,可能导致 CPU 资源浪费
应用场景实时消息队列消费、分布式锁实现等需即时响应的场景简单元素弹出、数据清理等非实时的单次操作

2.4内部编码

Redis List采用两种内部编码,自动根据列表特性切换:

编码类型存储结构适用场景优缺点
ziplist连续内存块元素少(<512个)且小(<64字节)省内存,插入慢
linkedlist双端链表大列表或大元素插入快,内存占用高

  • ziplist:采用连续内存块设计,头部包含 zlbytes(总字节数)、zltail(尾偏移)、zllen(元素数)三个元数据字段,随后紧跟实际元素数组。这种结构通过消除指针开销实现了极高的内存利用率,但在中间位置插入/删除元素时需移动后续所有数据,性能随元素数量增长线性下降。

  • linkedlist:基于双端链表实现,每个节点独立存储且包含 prev/next 指针以维护节点间关系,头节点与尾节点的引用使首尾操作可在 O(1) 时间完成。尽管节点分散存储导致内存碎片化且指针开销较大,但任意位置的节点修改(增/删)仅需调整相邻节点指针,性能稳定。

两种编码的设计差异体现了 Redis 在“空间效率”与“时间效率”之间的权衡:ziplist 适合小规模、紧凑数据场景,linkedlist 则为大规模数据提供了更优的操作性能。


2.5使用场景

2.5.1消息队列

Redis List 数据结构因其高效的插入/删除特性,可作为轻量消息队列的实现方案,适用于对消息可靠性要求不高的简易场景。以“简易订单消息系统”为例,其核心原理基于 生产者-消费者模型:生产者通过 LPUSH 命令向队列左侧写入消息,消费者通过 BRPOP 命令从队列右侧阻塞式读取消息,形成“先进先出”(FIFO)的消息传递机制。这种模式下,消息从生产到消费的路径清晰,且 Redis 本身的高性能特性可支撑高并发场景下的消息流转。

代码示例

# 生产者
LPUSH order:queue "order_id:123"# 消费者
BRPOP order:queue 0  # 阻塞等待新订单

优化策略

  • 开启AOF持久化避免消息丢失
  • 用LTRIM限制队列长度:LTRIM order:queue 0 999(保留1000条)

2.5.2分频道阻塞消息队列

分频道阻塞消息队列是基于 Redis List 数据结构实现的高级消息队列模式,主要解决单一消息队列中多主题消息混杂导致的处理效率低下问题。其核心设计思想是通过频道隔离机制实现消息的分类存储与精准消费,在保留基础队列阻塞特性的同时,显著提升多主题场景下的消息处理灵活性与系统性能。

该模式的核心原理在于将不同主题的消息分配至独立的 List 数据结构中,每个主题(频道)对应一个唯一的 Redis Key。具体实现流程如下:

  • 生产者端:根据消息所属主题,选择对应的 List Key 执行 LPUSH 命令将消息入队,确保不同频道的消息物理隔离。
  • 消费者端:通过 BRPOP 命令同时监听多个频道对应的 List Key,该命令会按照参数顺序依次检查各 List 是否有消息,一旦某个频道存在待处理消息则立即返回并处理,实现多频道消息的阻塞式优先级消费。

代码示例

以新闻发布系统为例,生产者需将不同领域的新闻分发至对应频道,消费者则同时监听多个领域的新闻更新:

1. 生产者发布消息

向“体育”频道发布新闻 ID 为 1001 的消息:

LPUSH msg:channel:体育 "news:1001"  # 消息内容通常为业务标识,如新闻ID、订单号等

2. 消费者监听消息

同时监听“体育”和“科技”频道,阻塞等待新消息(超时时间设为 0 表示永久阻塞):

BRPOP msg:channel:体育 msg:channel:科技 0  
# 返回示例:1) "msg:channel:体育" 2) "news:1001"(先返回有消息的频道及消息内容)

2.5.3微博Timeline实现

微博用户动态流(Timeline)是 Redis List 数据结构的典型应用场景,其核心需求是高效存储用户发布的动态序列并支持分页查询。

基础实现方案

用户发布新微博时,需将动态 ID 按时间顺序存入 List,采用 LPUSH 命令保证最新动态位于列表头部;分页获取时通过 LRANGE 命令指定索引范围实现。

  • 发布动态:使用 LPUSH 将新动态 ID 加入用户 Timeline 列表
    LPUSH user:1001:timeline "post:9527"  # 用户 1001 发布动态 ID 为 9527 的微博
    
  • 分页查询:通过 LRANGE 获取指定范围的动态 ID(如第一页 10 条数据)
    LRANGE user:1001:timeline 0 9  # 获取索引 0-9 的 10 个动态 ID
    

性能瓶颈:1 + N 网络请求问题

基础方案存在显著性能缺陷:获取动态 ID 后,需通过 HGETALL 或 HMGET 逐个查询动态详情(如标题、内容、发布时间),导致 N 次网络往返。例如获取 10 条动态需执行 10 次 HMGET,网络开销随数据量线性增长。

1 + N 问题示例:假设单次网络请求耗时 2ms,获取 10 条动态需 1(LRANGE)+ 10(HMGET)= 11 次请求,总耗时约 22ms;若分页 size 为 20,则耗时增至 42ms,性能随分页规模急剧下降。

列表拆分:解决中间元素访问低效问题

Redis List 本质是链表结构,通过索引访问中间元素(如获取半年前的历史动态)时需遍历从头/尾至目标位置,时间复杂度为 O(n)。针对此问题,可采用以下两种拆分策略:

  • 按时间分月存储
    将用户 Timeline 按月份拆分,使用 user:{uid}:timeline:{yyyyMM} 作为 List 键名(如 user:1001:timeline:202310 存储 2023 年 10 月动态)。查询历史动态时,先根据时间计算目标月份的键名,再通过 LRANGE 获取该月数据。

适用场景:用户动态发布频率稳定,且查询多集中于近期或特定时间段(如“近 3 个月动态”)。

  • 按数量分块存储
    每 1000 条动态划分为一个 List 块,使用 user:{uid}:timeline:{chunkId} 命名(如 user:1001:timeline:0 存储前 1000 条,user:1001:timeline:1 存储第 1001-2000 条)。通过 LLEN 获取总条数后计算目标块 ID,再用 LRANGE 定位具体数据。

适用场景:用户动态发布频率波动大,或需支持“第 5000 条动态”等绝对位置查询。


3.小结

Redis List作为有序可重复的双端列表,其核心价值在于支持栈(LIFO)、队列(FIFO)及阻塞操作,能够灵活适配消息传递、时序数据存储等多种业务场景。

最佳实践指南

  • 控制列表长度:通过LTRIM命令定期修剪列表(如LTRIM key 0 999保留最近1000个元素),防止无限增长引发内存溢出。
  • 避免大索引访问LRANGE key N M中使用大索引(如N>10000)会触发全表扫描,建议通过业务设计(如分片存储)规避此类操作。
  • 阻塞命令替代轮询BRPOP/BLPOP相较于"RPOP+sleep"的轮询模式,可将空等待耗时从秒级降至毫秒级,显著提升资源利用率。
  • 结合Pipeline减少网络开销:批量执行插入、查询操作时,使用Pipeline将多次请求合并为单次TCP交互,网络延迟可降低60%~80%。

通过上述技术要点与实践策略的结合,能够充分发挥Redis List的结构优势,在保证性能的同时拓展其业务适用边界。


文章转载自:

http://6wnyDeIT.rfxyk.cn
http://sG8XKz2V.rfxyk.cn
http://yJZjotiZ.rfxyk.cn
http://GipLqMsG.rfxyk.cn
http://l04xTka8.rfxyk.cn
http://GeK7Y5ul.rfxyk.cn
http://t57iWdVg.rfxyk.cn
http://WNj0pzfq.rfxyk.cn
http://di3FWWxn.rfxyk.cn
http://NFAJTNS4.rfxyk.cn
http://F8nGKAO9.rfxyk.cn
http://W9xyCcUQ.rfxyk.cn
http://BvlS80LN.rfxyk.cn
http://28rHgxBm.rfxyk.cn
http://Z29NMGfg.rfxyk.cn
http://EEM6CYSO.rfxyk.cn
http://7D7nRrZw.rfxyk.cn
http://n1HP6InB.rfxyk.cn
http://rQEp9GrW.rfxyk.cn
http://F87I2nuy.rfxyk.cn
http://ngPi6Org.rfxyk.cn
http://WsHc6Y4D.rfxyk.cn
http://F3OiG0y1.rfxyk.cn
http://AyJfF2nP.rfxyk.cn
http://mIavgXIg.rfxyk.cn
http://FkBPCmXN.rfxyk.cn
http://89f3yYIn.rfxyk.cn
http://JvlYmM6A.rfxyk.cn
http://XigbRHdq.rfxyk.cn
http://LJvCfeAv.rfxyk.cn
http://www.dtcms.com/a/377920.html

相关文章:

  • 掌握单元测试的利器:JUnit 注解从入门到精通
  • 【Vue2手录05】响应式原理与双向绑定 v-model
  • spring项目部署后为什么会生成 logback-spring.xml文件
  • Java 日期字符串万能解析工具类(支持多种日期格式智能转换)
  • 在VS2022的WPF仿真,为什么在XAML实时预览点击 ce.xaml页面控件,却不会自动跳转到具体代码,这样不方便我修改代码,
  • 【数组】区间和
  • Qt 基础编程核心知识点全解析:含 Hello World 实现、对象树、坐标系及开发工具使用
  • 解决推理能力瓶颈,用因果推理提升LLM智能决策
  • 【大前端】常用 Android 工具类整理
  • Gradle Task的理解和实战使用
  • 强大的鸿蒙HarmonyOS网络调试工具PageSpy 介绍及使用
  • C++/QT 1
  • 软件测试用例详解
  • 【ROS2】基础概念-进阶篇
  • 三甲地市级医院数据仓湖数智化建设路径与编程工具选型研究(上)
  • 利用Rancher平台搭建Swarm集群
  • BRepMesh_IncrementalMesh 重构生效问题
  • VRRP 多节点工作原理
  • 运行 Ux_Host_HUB_HID_MSC 通过 Hub 连接 U 盘读写不稳定问题分析 LAT1511
  • Oracle体系结构-控制文件(Control Files)
  • 0303 【软考高项】项目管理概述 - 组织系统(项目型组织、职能型组织、矩阵型组织)
  • Spark-SQL任务提交方式
  • 10、向量与矩阵基础 - 深度学习的数学语言
  • 开发避坑指南(45):Java Stream 求两个List的元素交集
  • React19 中的交互操作
  • 阿里云ECS vs 腾讯云CVM:2核4G服务器性能实测对比 (2025)
  • 网络编程;TCP多进程并发服务器;TCP多线程并发服务器;TCP网络聊天室和UDP网络聊天室;后面两个还没写出来;0911
  • STM32项目分享:基于stm32的室内环境监测装置设计与实现
  • 利用归并算法对链表进行排序
  • GPU 服务器压力测试核心工具全解析:gpu-burn、cpu-burn 与 CUDA Samples