Redis LPOS 命令详细教程
LPOS 用于在列表中查找与给定值匹配的元素,并返回其索引位置。它支持从表头或表尾开始查找、指定从第几个匹配开始、一次返回多个匹配位置,以及限制比较的元素数量,是"按值定位"列表元素的只读命令。它从 Redis 6.0.6 起提供,在此之前只能用 LRANGE 自己遍历实现。
资料合集:https://pan.quark.cn/s/10e98d308913、https://pan.quark.cn/s/f56bc69c5338
一、概览与语法
redis
LPOS key element [RANK rank] [COUNT num-matches] [MAXLEN len]
| 项目 | 说明 |
|---|---|
| 数据类型 | List |
| 支持版本 | Redis 6.0.6 起 |
| key | 目标列表 Key |
| element | 要查找的元素,按字节精确匹配,允许空字符串 |
| RANK rank | 从第几个匹配开始返回;1 表示第一个匹配,负数表示从表尾倒数(-1 为最后一个匹配) |
| COUNT num-matches | 一次返回多个匹配位置;0 表示返回全部匹配 |
| MAXLEN len | 最多比较的元素数量;0 表示不限制 |
| 返回值 | 单结果模式返回匹配元素的 0 基索引,无匹配返回空值;带 COUNT 时返回索引数组,无匹配返回空数组 |
| 时间复杂度 | O(N),N 为需要比较的元素数(受 MAXLEN 影响) |
| ACL | @read、@list、@fast |
| 命令标记 | readonly、fast |
官方说明:LPOS 报告列表中匹配元素的索引,默认从列表表头开始扫描并返回第一个匹配;返回的索引始终以表头为 0 基准,即使从表尾方向查找也一样。$TRAE_REF
二、基础示例
以下命令在测试实例的 redis-cli 中执行。文中结果是预期说明,未实际连接 Redis 运行。示例沿用官方文档的数据与结果。
redis
DEL tutorial:{lpos}:mylist
RPUSH tutorial:{lpos}:mylist a b c 1 2 3 c c
LPOS tutorial:{lpos}:mylist c
预期结果:RPUSH 返回 8;LPOS 返回 (integer) 2,即第一个 "c" 的索引。列表索引从表头以 0 开始计数,a 是 0、b 是 1、c 是 2。
注意 LPOS 只查找不修改,执行后列表内容与长度不变,与 LREM 的"查找并删除"不同。
三、RANK 参数:从第几个匹配开始
列表中 "c" 出现了 3 次(索引 2、6、7)。RANK 用于指定以第几个匹配为返回目标:
redis
LPOS tutorial:{lpos}:mylist c RANK -1
LPOS tutorial:{lpos}:mylist c RANK 2
LPOS tutorial:{lpos}:mylist c RANK 100
预期结果:RANK -1 从表尾倒数,返回最后一个匹配 (integer) 7;RANK 2 返回第二个匹配 (integer) 6;RANK 100 超出实际匹配次数,返回空值。
RANK 为 0 是无效值,报错 ERR RANK can't be zero. Use 1 to start searching from the first matching element in the head and negative ranks to start searching from the tail。
四、COUNT 参数:批量返回匹配
COUNT 把单个索引变为索引数组,返回从起点开始的前 N 个匹配:
redis
LPOS tutorial:{lpos}:mylist c COUNT 2
LPOS tutorial:{lpos}:mylist c RANK -1 COUNT 2
LPOS tutorial:{lpos}:mylist c COUNT 0
LPOS tutorial:{lpos}:mylist c RANK 2 COUNT 3
预期结果:COUNT 2 返回 [2, 6];RANK -1 COUNT 2 从最后一个匹配开始继续向表头方向收集,返回 [7, 6];COUNT 0 返回全部匹配 [2, 6, 7];RANK 2 COUNT 3 从第二个匹配开始要 3 个但只剩 2 个,返回 [6, 7]。
要点:COUNT 模式下的顺序与搜索方向一致,负 RANK 时索引是倒序给出的;COUNT 0 表示"不限数量",而不是"返回 0 个"。
五、MAXLEN 参数:限制比较范围
MAXLEN 限制最多比较多少个元素,防止在大列表上全量扫描:
redis
LPOS tutorial:{lpos}:mylist c COUNT 0 MAXLEN 3
LPOS tutorial:{lpos}:mylist c RANK -1 COUNT 0 MAXLEN 3
预期结果:第一条只比较表头前 3 个元素 a,b,c,在索引 2 命中后停止,返回 [2];第二条从表尾方向比较 3 个元素 c,c,3,命中索引 7 和 6 后到达比较上限,返回 [7, 6]。
MAXLEN 计的是"比较次数"而不是"返回的匹配数",负 RANK 时从表尾倒数比较。MAXLEN 0 表示不限制。
六、返回值与空结果的两种形态
| 调用模式 | 有匹配 | 无匹配 |
|---|---|---|
| 不带 COUNT | 返回整数索引 | 返回空值(nil) |
| 带 COUNT | 返回索引数组 | 返回空数组 |
redis
LPOS tutorial:{lpos}:mylist no-such
LPOS tutorial:{lpos}:mylist no-such COUNT 0
LPOS tutorial:{lpos}:mylist no-such RANK 5
预期结果:第一条返回 (nil);第二条返回空数组 (empty list or set);第三条在单结果模式下 RANK 超出匹配次数,同样返回 (nil)。
客户端代码必须区分这两种形态:判断"元素是否存在"适合用单结果模式判断 nil;收集全部位置适合用 COUNT 模式判断空数组。
七、边界情况与错误处理
| 场景 | 行为 |
|---|---|
| Key 不存在 | 视为空列表,返回空值(或 COUNT 模式下的空数组) |
| Key 类型不是 List | 报 WRONGTYPE |
| element 为空字符串 | 允许,可与列表中的空字符串元素匹配 |
| RANK 为 0 | 报错 ERR RANK can't be zero... |
| COUNT 为负数 | 报错 ERR COUNT can't be negative |
| MAXLEN 为负数 | 报错 ERR MAXLEN can't be negative |
| 参数选项拼写错误 | 报 ERR syntax error |
| Redis 版本低于 6.0.6 | 报 unknown command 错误 |
redis
SET tutorial:{lpos}:str text
LPOS tutorial:{lpos}:str a
LPOS tutorial:{lpos}:mylist c RANK 0
预期结果:第一条在 String 类型上报 WRONGTYPE;第二条报 RANK 为 0 的错误。
八、与相近命令的区别
| 命令 | 方向 | 是否修改列表 | 说明 |
|---|---|---|---|
| LPOS | 按值查找,返回索引 | 否 | 6.0.6 起支持 |
| LINDEX | 按索引取元素 | 否 | 与 LPOS 互为反向:一个按值找位置,一个按位置取值 |
| LINSERT | 按值定位插入 | 是(+1) | 需要 pivot 已存在 |
| LSET | 按索引覆盖元素 | 否(长度不变) | 常与 LPOS 组合实现"查找并替换" |
| LREM | 按值删除 | 是(-N) | 可删除前 N 个匹配,无位置信息返回 |
组合用法:LPOS key target 拿到索引后 LSET key index new_value 即为精确替换;带 COUNT 的 LPOS 可先确认重复元素的全部位置,再逐个处理。LREM 只能按值从前/后删除指定数量,拿不到索引。
九、客户端示例
前提为已安装 redis-py 并准备好本地测试实例。redis-py 的 lpos 签名为 lpos(name, value, rank=None, count=None, maxlen=None),单结果返回 int 或 None,带 count 返回列表。
python
import redis
r = redis.Redis(host="localhost", port=6379, decode_responses=True)
key = "tutorial:{lpos}:py"
try:
r.delete(key)
r.rpush(key, "a", "b", "c", "1", "2", "3", "c", "c")
print(r.lpos(key, "c")) # 2
print(r.lpos(key, "c", rank=-1)) # 7
print(r.lpos(key, "c", count=0)) # [2, 6, 7]
print(r.lpos(key, "c", rank=2, count=3)) # [6, 7]
print(r.lpos(key, "c", count=0, maxlen=3)) # [2]
print(r.lpos(key, "no-such")) # None
print(r.lpos(key, "no-such", count=0)) # []
finally:
r.delete(key)
r.close()
Java(Jedis)示例,选项参数使用 redis.clients.jedis.params.LPosParams:
java
import redis.clients.jedis.params.LPosParams;
try (Jedis jedis = new Jedis("localhost", 6379)) {
String key = "tutorial:{lpos}:java";
jedis.del(key);
jedis.rpush(key, "a", "b", "c", "1", "2", "3", "c", "c");
System.out.println(jedis.lpos(key, "c"));
// 2
System.out.println(jedis.lpos(key, "c", new LPosParams().rank(-1)));
// 7
System.out.println(jedis.lpos(key, "c", new LPosParams().rank(2), 3));
// [6, 7]
jedis.del(key);
}
十、原子性与性能注意事项
只读与原子性:LPOS 是只读命令,不修改列表,可在副本节点执行分担读压力;单命令原子执行,结果反映某一时刻的列表状态。
慢命令风险 :无 MAXLEN 时最坏情况要比较整个列表(O(N)),Redis 处理命令是单线程的,在百万级元素列表上做 COUNT 0 会阻塞其他命令,属于典型慢命令。大列表上应尽量指定 MAXLEN 或缩小查找范围,并通过 SLOWLOG 观测。
事务与 Pipeline:LPOS 可在 MULTI/EXEC 和 Lua 脚本中正常使用;"LPOS 查位置 + LSET 修改"两条命令不是原子的,并发修改同一列表时需要用 WATCH 或 Lua 脚本包裹。
集群:LPOS 只涉及单个 Key,在 Redis Cluster 中没有跨槽限制。
十一、典型业务场景
查找并精确替换 :有序配置列表、轮播位等场景中,先用 LPOS key old_value 定位,再用 LSET 覆盖,比重建整个列表更轻量。
重复元素巡检 :用 COUNT 0 查出某元素的全部出现位置,评估是否需要去重,再配合 LREM 或 LSET 清理。
调试与观测:开发期快速确认元素是否在列表中、位于第几位,辅助排查 LPUSH/RPUSH 的顺序问题。
限流查找 :只想确认"最近 N 个元素里有没有它"时,用 MAXLEN 控制比较量,把开销从 O(N) 降到 O(N_prefix)。
十二、练习、排错与总结
练习:新建 tutorial:{lpos}:ex,用 RPUSH 写入 a b a a;执行 LPOS ex a 确认返回 0;LPOS ex a RANK -1 返回 3;LPOS ex a COUNT 0 返回 [0, 2, 3];LPOS ex a COUNT 0 MAXLEN 2 返回 [0];LPOS ex nosuch 返回空值,COUNT 0 模式返回空数组。
排错要点:返回空值或空数组时先确认元素值是否与写入时大小写、空格完全一致(精确字节匹配);报 unknown command 时检查 Redis 版本是否低于 6.0.6;报 RANK/COUNT/MAXLEN 相关错误时检查参数取值(RANK 非 0、COUNT 与 MAXLEN 非负);慢日志出现 LPOS 时加 MAXLEN 或改用其他结构。清理使用 DEL tutorial:{lpos}:mylist tutorial:{lpos}:str tutorial:{lpos}:py tutorial:{lpos}:java tutorial:{lpos}:ex。速记:6.0.6 起支持、按值查找返回 0 基索引、RANK 指定第几个匹配且负数从表尾倒数、COUNT 批量返回且 0 为全部、MAXLEN 限制比较元素数、单结果无匹配返回空值而 COUNT 模式返回空数组、只读不修改列表、大列表务必防全量扫描。