Redis LMOVE怎样实现可靠的任务转移队列
如果消费者直接使用 RPOP 取走任务,进程可能在“任务已经从队列删除、业务还没有处理完成”的窗口崩溃,任务就没有可恢复位置。更稳妥的做法是把 Redis 列表拆成“待处理”和“处理中”两个状态:消费者用 LMOVE 一次性把任务转入处理中列表,成功后再用 LREM 删除。
本文只围绕一个任务转移队列展开,给出 Redis 官方命令语义、FIFO 方向选择、阻塞领取、成功门禁和超时回收的组合方式。Redis 官方文档地址:https://redis.io/docs/latest/commands/lmove/
1. 先把队列拆成待处理和处理中
可靠队列至少需要两个列表键:
queue:pending:生产者刚写入、尚未被某个消费者领取的任务。queue:processing:已经交给消费者,但业务结果还没有确认成功的任务。
生产者用 LPUSH 从左侧写入,消费者用 LMOVE queue:pending queue:processing RIGHT LEFT 从右侧取最旧任务,再从处理中列表左侧放入。这样既保留 FIFO 顺序,也让任务在处理期间仍然留在 Redis 中。任务值最好使用全局唯一 ID,而不是一段无法区分重试次数的普通文本。

2. 用 LMOVE 原子领取任务
LMOVE source destination wherefrom whereto 会从源列表指定的一端弹出一个元素,并把它推入目标列表指定的一端;整个转移由 Redis 作为一个命令完成。它的时间复杂度是 O(1),从源列表不存在时返回空值,不会凭空创建处理中任务。
# 左侧入队、右侧取最旧任务,保证 FIFO 顺序
redis-cli LPUSH queue:pending task:1001 task:1002
# 原子地把最旧任务从待处理队列转入处理中队列
redis-cli LMOVE queue:pending queue:processing RIGHT LEFT
# 查看两个状态列表,便于由消费者决定下一步
redis-cli LRANGE queue:pending 0 -1
redis-cli LRANGE queue:processing 0 -1
上例中,task:1001 会先被领取。不要把 LEFT LEFT、RIGHT RIGHT 等方向随意替换:源列表取哪一端决定消费顺序,目标列表放哪一端决定处理中任务的排列方式,回收策略也要使用同一套约定。
3. 用 BLMOVE 减少空轮询
如果消费者持续执行非阻塞的 LMOVE,空队列时会形成大量无效请求。Redis 提供了阻塞版本 BLMOVE,参数顺序相同,只是在末尾增加超时时间;超时单位为秒,使用 0 表示一直等待。
# 队列为空时最多等待 5 秒,避免消费者持续空轮询
redis-cli BLMOVE queue:pending queue:processing RIGHT LEFT 5
# 返回空值时由应用层安排下一次等待或退出
# 这里的注释描述处理策略,不是 Redis 返回日志
阻塞领取只解决“没有任务时如何等待”,不负责确认业务是否成功。消费者拿到任务后仍要执行自己的超时、取消、重试和幂等逻辑;如果部署在 Redis Cluster 中,还应把两个列表键设计到同一 hash slot,避免多键命令遇到跨槽限制。
4. 用 LREM 设置成功门禁
领取成功不能等同于业务成功。建议把任务处理拆成三个明确阶段:领取、执行业务、确认完成。只有业务写入结果并且确认成功后,才从 queue:processing 删除任务。
LREM key 1 value 的 1 表示从列表中删除第一个匹配值。任务 ID 必须足够唯一,否则同一个值重复入队时,删除可能命中错误任务。对于扣库存、发通知、写订单等不可重复副作用,处理器还要使用任务 ID 做幂等键;LMOVE 能避免转移中丢失,但不会自动提供业务层的“只执行一次”。
5. 失败处理与超时回收
消费者如果在领取后崩溃,任务会留在 queue:processing,这正是可靠队列可以恢复的依据。列表本身只保存顺序,不适合直接判断每个任务领取了多久,因此应额外保存领取时间、重试次数和租约状态,例如使用哈希保存任务元数据,再由回收者周期性找出超时任务。
生产环境中不要只凭列表位置回收,而应先核对任务 ID、租约版本和当前状态;否则一个慢任务可能被误判为超时,造成两个消费者同时处理。超过最大重试次数的任务可以转入死信列表,并保留最后错误摘要供人工复盘。

6. 建立触发、权限和通知复盘回路
如果把 LMOVE 放进自动化工作流,可以按下面的回路组织消费者:
- 触发场景:生产者将带唯一 ID 的任务写入
queue:pending,消费者由 BLMOVE 等待。 - 权限配置:调用账号至少需要允许列表写操作;Redis 文档将 LMOVE 归入
@write、@listACL 分类,实际 ACL 仍按环境最小化授权。 - 流水线阶段:LMOVE 领取、执行业务、提交结果、LREM 清理,四个阶段分别记录耗时。
- 门禁规则:没有成功结果就不能删除 processing 中的任务;重试超过阈值就进入死信列表。
- 失败处理:回收器按租约和版本判断是否回队列,避免慢任务与回收任务并发产生重复副作用。
- 通知复盘:当 processing 最老任务年龄、重试次数或死信数量超过阈值时通知值班人员,并保留任务 ID 便于定位。
建议至少记录 LLEN queue:pending、LLEN queue:processing、最老 processing 任务年龄、每分钟领取数、成功删除数和回收数。只看 pending 长度不够:pending 下降而 processing 长时间不下降,通常说明消费者拿到了任务但没有完成确认。
常见问题
LMOVE 和 RPOPLPUSH 有什么区别?
在“从右侧取、向左侧放”的方向组合下,LMOVE 可以表达原来的 RPOPLPUSH 语义;新代码使用 LMOVE 更直观,也可以明确指定源端和目标端。
LMOVE 能保证任务只执行一次吗?
不能。它保证列表转移是原子的,但消费者在业务副作用完成前崩溃时,任务会被重新处理,所以业务处理器必须支持幂等。
什么时候用 BLMOVE?
消费者需要等待新任务、又不希望持续轮询空列表时使用 BLMOVE;如果需要同时等待多个队列,应根据客户端能力选择合适的阻塞列表命令。
为什么不能只保留一个列表?
单列表直接弹出后,消费者崩溃会留下无法定位的任务丢失窗口。单独的 processing 列表让回收者知道哪些任务已经领取但没有完成。
可靠的 Redis 任务转移队列不是把 LMOVE 当成“自动重试按钮”,而是把原子转移、处理中状态、成功删除、租约回收和幂等执行组合成一条可观测的工作流。
Redis XREADGROUP怎样读取消息但不写入 pending
- 上一篇
- Redis XREADGROUP怎样读取消息但不写入 pending
- 下一篇
- Redis BLMOVE阻塞式转移任务时怎样设置超时
-
- 数据库 · Redis | 30分钟前 |
- Redis HSCAN 用 MATCH 和 COUNT 分批筛选 Hash 字段
- 459浏览 收藏
-
- 数据库 · Redis | 51分钟前 | redis streams XREADGROUP PEL pending NOACK
- Redis XREADGROUP怎样读取消息但不写入 pending
- 498浏览 收藏
-
- 数据库 · Redis | 18小时前 | redis 访问控制 acl ACL SETUSER 命令权限 键模式
- Redis ACL 怎样限制客户端只能访问指定命令
- 255浏览 收藏
-
- 数据库 · Redis | 1天前 | Redis · 性能排查 · redis lru LFU CLIENT NO-TOUCH 键淘汰
- Redis CLIENT NO-TOUCH 怎样避免访问键更新 LRU/LFU
- 111浏览 收藏
-
- 数据库 · Redis | 1天前 | Redis · redis Redis Cluster SPUBLISH 分片发布订阅
- Redis SPUBLISH 怎样向分片频道发送消息
- 186浏览 收藏
-
- 数据库 · Redis | 1天前 | Redis · 性能排查 · redis 内存回收 MEMORY PURGE jemalloc
- Redis MEMORY PURGE 怎样让分配器回收脏页
- 372浏览 收藏
-
- 数据库 · Redis | 1天前 | Redis · 集群 · Redis Cluster Redis Functions FUNCTION LOAD FCALL
- Redis Functions 怎样在集群中预加载函数
- 440浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 879次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 875次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 957次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 939次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 766次使用
-
- Go与Redis实现分布式互斥锁和红锁
- 2022-12-22 117浏览
-
- 基于Golang实现延迟队列(DelayQueue)
- 2023-02-16 321浏览
-
- Go+Redis实现延迟队列实操
- 2023-02-23 426浏览
-
- 一文搞懂Go语言操作Redis的方法
- 2023-01-07 171浏览
-
- Golang分布式应用之Redis示例详解
- 2023-01-07 113浏览

