Redis RPUSHX 命令详细教程
RPUSHX 将一个或多个元素追加到列表的表尾(右侧),与 RPUSH 的唯一区别是:只有当 Key 已存在且持有列表时才执行推入 ,Key 不存在时不创建、不报错,静默返回 0。它从 Redis 2.2.0 起提供,4.0.0 起支持一次推入多个元素,适合"只更新、不新建"的保护性写入场景。
资料合集:https://pan.quark.cn/s/10e98d308913、https://pan.quark.cn/s/f56bc69c5338
一、概览与语法
redis
RPUSHX key element [element ...]
| 项目 | 说明 |
|---|---|
| 数据类型 | List |
| 支持版本 | Redis 2.2.0 起;4.0.0 起接受多个 element |
| key | 目标列表 Key,必须已存在 |
| element | 一个或多个要追加的元素,按书写顺序依次追加到表尾 |
| 返回值 | 推入后的列表长度;Key 不存在时返回 0 |
| 时间复杂度 | 每个元素 O(1),一次推 N 个为 O(N) |
| ACL | @write、@list、@fast |
| 命令标记 | write、fast、denyoom |
官方说明:当 key 已存在且持有列表时,RPUSHX 将所有指定值插入到存储于 key 的列表尾部;与 RPUSH 相反,当 key 尚不存在时不执行任何操作。$TRAE_REF
二、基础示例
以下命令在测试实例的 redis-cli 中执行。文中结果是预期说明,未实际连接 Redis 运行。示例沿用官方文档的数据与结果。
redis
DEL tutorial:{rpushx}:mylist tutorial:{rpushx}:myotherlist
RPUSH tutorial:{rpushx}:mylist "World"
RPUSHX tutorial:{rpushx}:mylist "Hello"
RPUSHX tutorial:{rpushx}:myotherlist "Hello"
LRANGE tutorial:{rpushx}:mylist 0 -1
EXISTS tutorial:{rpushx}:myotherlist
预期结果:RPUSH 返回 1;对已存在的 mylist 追加返回 2;对不存在的 myotherlist 追加返回 0;LRANGE 返回 "World"、"Hello"(参数顺序即列表顺序);EXISTS 返回 0,确认 myotherlist 未被创建。
三、与 RPUSH 的核心差异
| 对比项 | RPUSH | RPUSHX |
|---|---|---|
| Key 存在且为列表 | 追加并返回新长度 | 追加并返回新长度 |
| Key 不存在 | 先创建空列表再追加 | 不创建,返回 0 |
| 副作用 | 可能凭空产生新 Key | 无,绝不新建 Key |
| 支持多元素 | 2.4.0 起 | 4.0.0 起 |
这个差异决定了两者的适用面:RPUSH 适合初始化与常规写入;RPUSHX 把"判断存在"和"追加"合并成一次原子操作,避免"先 EXISTS 检查再 RPUSH"这种两步流程在并发下产生的竞态。
四、多元素推入顺序
与 RPUSH 一致,多个元素按参数顺序依次追加到表尾,参数的书写顺序即最终列表顺序:
redis
DEL tutorial:{rpushx}:order
RPUSH tutorial:{rpushx}:order seed
RPUSHX tutorial:{rpushx}:order a b c
LRANGE tutorial:{rpushx}:order 0 -1
预期结果:RPUSHX 返回 4;LRANGE 返回 "seed"、"a"、"b"、"c"。版本注意:Redis 4.0 之前的 RPUSHX 每次只接受一个元素。
五、Key 不存在与类型错误
RPUSHX 返回 0 只有一种正常情形:Key 当前不存在(含刚被弹出最后一个元素而删除的情况)。列表类型不会以"空列表"形式存在,元素弹完 Key 即被删除。
| 场景 | 行为 |
|---|---|
| Key 不存在 | 不创建,返回 0 |
| Key 是其他类型 | 报 WRONGTYPE |
| Key 已存在且为列表 | 追加,返回新长度 |
| Key 设置过 TTL | 追加不清除过期时间,到期仍会被删除 |
redis
SET tutorial:{rpushx}:str text
RPUSHX tutorial:{rpushx}:str x
预期结果:对 String 类型执行报 WRONGTYPE,类型检查先于任何写入。
六、与相近命令的区别
| 命令 | 推入位置 | Key 不存在时 | 支持多元素 |
|---|---|---|---|
| RPUSHX | 表尾(右侧) | 不创建,返回 0 | 4.0.0 起 |
| RPUSH | 表尾(右侧) | 创建列表后推入 | 2.4.0 起 |
| LPUSHX | 表头(左侧) | 不创建,返回 0 | 4.0.0 起 |
| LPUSH | 表头(左侧) | 创建列表后推入 | 2.4.0 起 |
| LINSERT | 指定 pivot 前后 | 不创建,返回 0 | 不支持 |
四个命令的分工很清晰:普通命令负责"有则推、无则建",X 命令负责"有则推、无则放弃"。RPUSHX 与 LPUSHX 的区别仅在推入端:前者追加到表尾,后者插到表头。
七、边界情况与错误处理
| 场景 | 行为 |
|---|---|
| 未提供任何 element | 参数数量不足,报语法错误 |
| element 为空字符串 | 允许,正常推入 |
| Key 不存在 | 返回 0,静默成功 |
| Key 类型不是 List | 报 WRONGTYPE |
| 返回值语义 | 0 表示未推入(Key 不存在),正数表示推入后的长度 |
redis
RPUSHX tutorial:{rpushx}:err
预期结果:缺少元素参数,报 ERR wrong number of arguments for 'rpushx' command。
业务代码需要注意:RPUSHX 返回 0 不是错误,但通常意味着"列表尚未初始化",调用方应据此决定是否先执行初始化,而不是把它当成失败重试。
八、客户端示例
前提为已安装 redis-py 并准备好本地测试实例。redis-py 的 rpushx(name, *values) 返回推入后的长度,Key 不存在时返回 0。
python
import redis
r = redis.Redis(host="localhost", port=6379, decode_responses=True)
key = "tutorial:{rpushx}:py"
try:
r.delete(key)
print(r.rpushx(key, "a", "b", "c")) # 0,Key 不存在不创建
r.rpush(key, "seed")
print(r.rpushx(key, "a", "b", "c")) # 4
print(r.lrange(key, 0, -1)) # ['seed', 'a', 'b', 'c']
finally:
r.delete(key)
r.close()
Java(Jedis)示例,rpushx 同样支持可变参数:
java
try (Jedis jedis = new Jedis("localhost", 6379)) {
String key = "tutorial:{rpushx}:java";
jedis.del(key);
System.out.println(jedis.rpushx(key, "a"));
// 0,Key 不存在不创建
jedis.rpush(key, "seed");
System.out.println(jedis.rpushx(key, "a", "b", "c"));
// 4
System.out.println(jedis.lrange(key, 0, -1));
// [seed, a, b, c]
jedis.del(key);
}
九、原子性与使用注意事项
检查与写入合一 :EXISTS key 加 RPUSH key x 是两步流程,中间可能被其他客户端删除 Key 或改变类型;RPUSHX 把"必须已存在"作为前置条件在服务端一次性校验,是天然原子的。
TTL 不受影响:对已设置过期时间的 Key 执行 RPUSHX 不会清除 TTL。若业务期望"写入后重新计时",需要显式重设 EXPIRE。
Pipeline 与事务:RPUSHX 可正常用于管道与 MULTI/EXEC,返回值语义不变;批量追加用管道减少网络往返。
性能:每个元素 O(1),只操作表尾,与列表长度无关。
十、典型业务场景
保护性追加:时序日志、审计流水、事件流等由专门的初始化逻辑(写入元数据、设置 TTL)创建 Key 后,后续写入全部走 RPUSHX,防止下游异常调用凭空造出没有 TTL、没有元数据的孤儿 Key。
会话期流水:用户会话存在才继续记录操作流水,会话过期后自然停止追加,无需额外判断。
并发初始化保护:多个服务实例同时写同一个列表时,用 RPUSHX 保证"谁负责创建、谁才能写入"的语义清晰。
追加已有集合的明细:主记录存在才追加其明细项,避免产生孤立明细。
十一、练习、排错与总结
练习:新建 tutorial:{rpushx}:ex1 与 tutorial:{rpushx}:ex2;对不存在的 ex1 执行 RPUSHX ex1 a,确认返回 0 且 EXISTS 为 0;执行 RPUSH ex1 seed 后执行 RPUSHX ex1 b c,确认返回 3 且 LRANGE 得到 seed,b,c;用 SET 把 ex1 改成 String 后再 RPUSHX,确认报 WRONGTYPE;最后对 ex2 重复验证 Key 不存在时的静默行为。
排错要点:RPUSHX 返回 0 时先怀疑 Key 未初始化,用 EXISTS、TYPE 确认;本想初始化列表时不要误用 RPUSHX,改用 RPUSH;报 WRONGTYPE 时检查 Key 是否被 String 等类型占用;老版本(4.0 前)报语法错误时检查是否使用了多元素写法;追加顺序与预期不符时确认元素是否被追加到表尾(RPUSHX 参数顺序即列表顺序)。清理使用 DEL tutorial:{rpushx}:mylist tutorial:{rpushx}:myotherlist tutorial:{rpushx}:order tutorial:{rpushx}:str tutorial:{rpushx}:py tutorial:{rpushx}:java tutorial:{rpushx}:ex1 tutorial:{rpushx}:ex2。速记:2.2 起支持、4.0 起可推多个、只在 Key 已存在时追加到表尾、Key 不存在返回 0 且不创建、返回值为推入后的长度、不清除已有 TTL、与 RPUSH 的区别就在"是否创建"、X 系列命令都是"有则推无则弃"。