Redis命令:RPOPLPUSH

Redis RPOPLPUSH 命令详细教程

RPOPLPUSH 原子地把源列表表尾(右侧)的元素弹出,并推入目标列表表头(左侧),同时返回被搬运的元素。它自 Redis 1.2.0 起提供,是早期实现"可靠队列"与"列表轮询"的核心命令。自 6.2.0 起 RPOPLPUSH 被标记为弃用,官方建议迁移到功能更完整的 LMOVE;新项目应直接使用 LMOVE,仅在兼容旧代码时保留 RPOPLPUSH。

资料合集:https://pan.quark.cn/s/10e98d308913、https://pan.quark.cn/s/f56bc69c5338

一、概览与语法

redis 复制代码
RPOPLPUSH source destination
项目 说明
数据类型 List
支持版本 Redis 1.2.0 起;6.2.0 起弃用,建议改用 LMOVE
source 源列表 Key,从其表尾(右侧)弹出元素
destination 目标列表 Key,把元素推入其表头(左侧),不存在时自动创建
返回值 Bulk 字符串,被弹出并推入的元素;source 不存在或为空时返回空值,不创建 source
时间复杂度 O(1)
ACL @write、@list、@slow
命令标记 write、denyoom

官方说明:原子地返回并移除存储于 source 的列表的最后一个元素,并把该元素推入存储于 destination 的列表的第一个位置。若 source 不存在或为空,则不执行任何操作并返回空值。$TRAE_REF

从方向上看,RPOPLPUSH source destination 完全等价于 LMOVE source destination RIGHT LEFT,两者只是名称与历史不同。

二、基础示例

以下命令在测试实例的 redis-cli 中执行。文中结果是预期说明,未实际连接 Redis 运行。示例沿用官方文档的数据与结果。

redis 复制代码
DEL tutorial:{rpoplpush}:mylist tutorial:{rpoplpush}:myotherlist
RPUSH tutorial:{rpoplpush}:mylist "one"
RPUSH tutorial:{rpoplpush}:mylist "two"
RPUSH tutorial:{rpoplpush}:mylist "three"
RPOPLPUSH tutorial:{rpoplpush}:mylist tutorial:{rpoplpush}:myotherlist
LRANGE tutorial:{rpoplpush}:mylist 0 -1
LRANGE tutorial:{rpoplpush}:myotherlist 0 -1

预期结果:三条 RPUSH 分别返回 1、2、3;RPOPLPUSH 返回 "three"(mylist 的表尾元素被弹出);LRANGE 显示 mylist 只剩 "one"、"two",myotherlist 为 "three"。myotherlist 此前不存在,被自动创建。

三、与 LMOVE 的关系及弃用说明

对比项 RPOPLPUSH LMOVE
提供版本 1.2.0 起 6.2.0 起
弹出端 固定 RIGHT(表尾) 可选 LEFT/RIGHT
推入端 固定 LEFT(表头) 可选 LEFT/RIGHT
当前状态 6.2.0 起弃用,仅保留兼容 官方推荐替代命令
等价关系 等于 LMOVE src dst RIGHT LEFT 更通用

迁移方式很直接:把 RPOPLPUSH src dst 改写为 LMOVE src dst RIGHT LEFT,语义与返回值完全一致。若需要其他方向组合(如头取头放),则只能用 LMOVE。本文后续示例继续使用 RPOPLPUSH 以便对照旧代码,实际新代码请优先写 LMOVE。

四、同 Key 时的列表旋转

当 source 与 destination 是同一个 Key 时,RPOPLPUSH 把表尾元素搬到表头,实现列表的循环旋转:

redis 复制代码
DEL tutorial:{rpoplpush}:rot
RPUSH tutorial:{rpoplpush}:rot "a" "b" "c"
RPOPLPUSH tutorial:{rpoplpush}:rot tutorial:{rpoplpush}:rot
LRANGE tutorial:{rpoplpush}:rot 0 -1

预期结果:RPUSH 返回 3;RPOPLPUSH 返回 "c";LRANGE 返回 "c"、"a"、"b"。表尾的 "c" 被移到表头,列表长度保持不变,这即是"循环列表"模式的实现方式,可用于轮询调度多个客户端依次取不同元素。

五、可靠队列模式

RPOPLPUSH 最重要的用途是构建比 LPOP/RPOP 更可靠的任务队列:消费者不直接丢弃元素,而是把待处理任务转移到一张"处理中"列表里;处理成功后再从该列表删除,处理失败或崩溃时任务仍留在处理中列表,可被重新投递。这是官方文档记载的经典用法之一。

redis 复制代码
DEL tutorial:{rpoplpush}:queue tutorial:{rpoplpush}:processing
LPUSH tutorial:{rpoplpush}:queue "job1" "job2" "job3"
RPOPLPUSH tutorial:{rpoplpush}:queue tutorial:{rpoplpush}:processing
LREM tutorial:{rpoplpush}:processing 1 "job1"
LRANGE tutorial:{rpoplpush}:queue 0 -1
LRANGE tutorial:{rpoplpush}:processing 0 -1

预期结果:LPUSH 返回 3。LPUSH 按参数顺序把 job1、job2、job3 依次推到表头,列表变为 job3、job2、job1,最早入队的 "job1" 落在表尾;RPOPLPUSH 从表尾弹出 "job1" 并推入 processing 表头,返回 "job1";LREM 返回 1,表示 "job1" 处理完成后被移出 processing;最后 LRANGE 显示 queue 为 "job3"、"job2",processing 为空数组。若消费者在处理 "job1" 时崩溃,元素会一直留在 processing 列表,便于恢复时重新处理。

这里采用 LPUSH 入队 + RPOPLPUSH 出队,两者方向相反,构成先进先出的可靠队列。

需要注意的是,LPOP/RPOP 一旦弹出元素,元素就从 Redis 消失,消费者崩溃即永久丢失;RPOPLPUSH 的处理中列表模式正是为解决这一点而设计。若要阻塞等待,还存在 BRPOPLPUSH,但其同样已被 BLMOVE 取代。

六、返回值与空结果

情形 返回
source 存在且有元素 被弹出并推入 destination 的元素(Bulk 字符串)
source 不存在 空值,且不创建 source,destination 也不受影响
source 为空列表 不会出现(列表弹空即删 Key),表现为 source 不存在的情形

返回空值不代表出错,只表示当前没有可搬运的元素。业务消费端应据此进入休眠或退避,而不是重试报错。

redis 复制代码
DEL tutorial:{rpoplpush}:empty tutorial:{rpoplpush}:dst
RPOPLPUSH tutorial:{rpoplpush}:empty tutorial:{rpoplpush}:dst
LRANGE tutorial:{rpoplpush}:dst 0 -1

预期结果:RPOPLPUSH 返回空值(source 不存在);LRANGE 返回空数组,确认 destination 并未被创建------只有真正搬运到元素时才会创建目标 Key。

七、边界情况与错误处理

场景 行为
source 不存在或为空 返回空值,不创建 source
destination 不存在 自动创建并推入
source 或 destination 类型不是 List 报 WRONGTYPE
source 与 destination 相同 列表旋转,长度不变
缺少 source 或 destination 参数 报语法错误
集群中两 Key 不同槽 报 CROSSSLOT
redis 复制代码
SET tutorial:{rpoplpush}:str text
RPUSH tutorial:{rpoplpush}:mylist2 "x"
RPOPLPUSH tutorial:{rpoplpush}:str tutorial:{rpoplpush}:mylist2

预期结果:SET 返回 OK;RPUSH 返回 1;最后一条因 source 是 String 类型报 WRONGTYPE,类型检查先于任何搬运,mylist2 不受影响。

八、与相近命令的区别

命令 是否搬运到另一列表 方向 阻塞 状态
RPOPLPUSH 是 固定 RIGHT→LEFT 否 6.2.0 起弃用
BRPOPLPUSH 是 固定 RIGHT→LEFT 是(带 timeout) 6.2.0 起弃用
LMOVE 是 四方向可选 否 官方推荐
BLMOVE 是 四方向可选 是 官方推荐
RPOP/LPOP 否 弹出后元素即消失 否 常用
LREM 否(按值删除) --- 否 常用

核心区别在于"是否转移到另一列表":RPOP/LPOP 弹出即丢弃,可靠性依赖消费者自身;RPOPLPUSH/LMOVE 把元素暂存到处理中列表,提供了失败重投递的可能。

九、客户端示例

前提为已安装 redis-py 并准备好本地测试实例。redis-py 的 rpoplpush(src, dst) 返回被搬运的元素或 None。

python 复制代码
import redis

r = redis.Redis(host="localhost", port=6379, decode_responses=True)
src = "tutorial:{rpoplpush}:py_src"
dst = "tutorial:{rpoplpush}:py_dst"
try:
    r.delete(src, dst)
    r.rpush(src, "one", "two", "three")

    print(r.rpoplpush(src, dst))    # three
    print(r.lrange(src, 0, -1))     # ['one', 'two']
    print(r.lrange(dst, 0, -1))     # ['three']
    r.delete(src)
    print(r.rpoplpush(src, dst))    # None,source 不存在
finally:
    r.delete(src, dst)
    r.close()

Java(Jedis)示例,rpoplpush(src, dst) 返回 String,source 为空时返回 null:

java 复制代码
try (Jedis jedis = new Jedis("localhost", 6379)) {
    String src = "tutorial:{rpoplpush}:java_src";
    String dst = "tutorial:{rpoplpush}:java_dst";
    jedis.del(src, dst);
    jedis.rpush(src, "one", "two", "three");

    System.out.println(jedis.rpoplpush(src, dst));   // three
    System.out.println(jedis.lrange(src, 0, -1));    // [one, two]
    System.out.println(jedis.lrange(dst, 0, -1));    // [three]
    jedis.del(src, dst);
}

十、原子性与集群注意事项

原子性:RPOPLPUSH 是单命令,"弹出加推入"不可分割,不存在元素已经离开 source 却还没进入 destination 的中间状态;多个消费者并发调用时,同一元素不会被两个消费者同时拿到,这正是可靠队列成立的基础。

集群语义 :在 Redis Cluster 中,source 与 destination 必须落在同一个哈希槽,否则报 CROSSSLOT。使用相同的哈希标签即可保证,例如 tutorial:{rpoplpush}:queue 与 tutorial:{rpoplpush}:processing 会落到同一槽。这是使用该命令时最容易忽略的限制。

其他:复杂度 O(1),与列表长度无关;在 MULTI/EXEC 事务、Lua 脚本与管道中正常工作;需要"没有元素就等待"时用 BLMOVE。

十一、典型业务场景

可靠任务队列:生产者 RPUSH 入队,消费者 RPOPLPUSH 把任务转移到 processing 列表,处理成功后 LREM 删除。异常宕机后,processing 列表里的残留任务可被人工或定时任务重新投放,避免任务丢失。

循环轮询调度 :把多个候选资源(如多个节点、多份配置)放入同一列表,用 RPOPLPUSH key key 每次取表尾并放回表头,实现无状态的轮流分配。

多队列串联搬运:把低优先级队列的元素批量搬运到高优先级队列,或在流量低谷时做队列整理。

消息多播:把一条消息依次推入多个消费者的私有列表,再结合 RPOPLPUSH 做各自的处理状态跟踪。

十二、练习、排错与总结

练习:用 tutorial:{rpoplpush}:ex_src 与 tutorial:{rpoplpush}:ex_dst 演练搬运流程:RPUSH 写入 a b c,执行一次 RPOPLPUSH 确认返回 "c" 且两列表内容符合预期;重复执行直到 source 弹空并返回空值,用 EXISTS 确认 source 已删除、destination 被创建;再对同一 Key 执行 RPOPLPUSH k k 观察旋转效果。

排错要点:返回空值时先用 EXISTS 确认 source 是否存在或已弹空;报 WRONGTYPE 时用 TYPE 检查两个 Key 的类型;集群中报 CROSSSLOT 时给两个 Key 加相同哈希标签;destination 意外为空说明搬运未发生,检查 source 是否真的有元素;发现元素丢失时排查消费者是否为 RPOP 直接弹出,建议迁移到处理中列表模式。清理使用 DEL tutorial:{rpoplpush}:mylist tutorial:{rpoplpush}:myotherlist tutorial:{rpoplpush}:rot tutorial:{rpoplpush}:queue tutorial:{rpoplpush}:processing tutorial:{rpoplpush}:empty tutorial:{rpoplpush}:dst tutorial:{rpoplpush}:str tutorial:{rpoplpush}:mylist2 tutorial:{rpoplpush}:py_src tutorial:{rpoplpush}:py_dst tutorial:{rpoplpush}:java_src tutorial:{rpoplpush}:java_dst tutorial:{rpoplpush}:ex_src tutorial:{rpoplpush}:ex_dst。速记:1.2 起支持、6.2 起弃用改用 LMOVE、表尾弹出推入目标表头、等价于 LMOVE RIGHT LEFT、返回被搬运的元素、source 空返回空值且不创建 source、destination 自动创建、同 Key 即旋转、可靠队列用处理中列表、集群必须同槽。

相关推荐
Tsuki_tl1 小时前
Redis 单线程 + 多编码,到底怎么配合的?
数据库·redis·哈希表·redis数据类型·编码方式·单线程模型·redis底层
霸道流氓气质1 小时前
Redisson BloomFilter:分布式位图实现、误判率调优与缓存穿透三级防护的 Spring Boot 生产级实战
spring boot·分布式·缓存
March.s2 小时前
Redis 核心知识与高可用架构实践
数据库·redis·架构
Knight_AL12 小时前
本地服务连接 SaaS 测试环境实践:Docker 部署 Redis 与 RocketMQ 完成接口联调
redis·docker·rocketmq
夕除13 小时前
redis--服务器优化
redis
xiaoqiMikko14 小时前
Redis 实战(一):内存满了 —— 拒写、淘汰、被系统杀掉,以及删了 key 内存为什么不降(6.2.4 实测)
redis·后端
wefg116 小时前
【Redis】安装与启动
数据库·redis·缓存
大圣编蚕16 小时前
常见的 Redis 面试题(高频整理)
数据库·redis·缓存