Redis LSET 命令详细教程
LSET将列表指定索引位置的元素覆盖为新值,列表长度保持不变,是按位置改写元素的基础命令。索引规则与 LRANGE 一致,0 基且支持负索引从表尾倒数。它从 Redis 1.0.0 起提供,注意它不会自动创建 Key,索引越界与 Key 不存在都会直接报错。
资料合集:https://pan.quark.cn/s/10e98d308913、https://pan.quark.cn/s/f56bc69c5338
一、概览与语法
LSET key index element| 项目 | 说明 |
|---|---|
| 数据类型 | List |
| 支持版本 | Redis 1.0.0 起 |
| key | 目标列表 Key |
| index | 目标位置索引,0 基,可为负数(-1 表示最后一个元素) |
| element | 覆盖后的新值 |
| 返回值 | 简单字符串 OK |
| 时间复杂度 | O(N),N 为列表长度;设置表头或表尾元素为 O(1) |
| ACL | @write、@list、@slow |
| 命令标记 | write、denyoom |
官方说明:将列表 index 位置的元素设置为 element。索引超出范围或 Key 不存在时返回错误。$TRAE_REF
二、基础示例
以下命令在测试实例的 redis-cli 中执行。文中结果是预期说明,未实际连接 Redis 运行。示例沿用官方文档的数据与结果。
DEL tutorial:{lset}:mylist RPUSH tutorial:{lset}:mylist "one" "two" "three" LSET tutorial:{lset}:mylist 0 "four" LRANGE tutorial:{lset}:mylist 0 -1预期结果:LSET返回OK;LRANGE返回"four"、"two"、"three",索引 0 的 one 被覆盖为 four,列表长度仍为 3。
三、索引规则
LSET 与 LRANGE 使用同一套索引约定:
- 0 基索引:表头元素是 0,不是 1。
- 负索引:-1 是最后一个元素,-2 是倒数第二个,以此类推。
- 长度不变:LSET 只做覆盖,不插入也不删除,执行前后 LLEN 相同。
- 首尾最快:索引 0 与 -1 直接定位端点,复杂度 O(1);中间位置需要从表头或表尾走到目标。
LSET tutorial:{lset}:mylist -1 "six" LRANGE tutorial:{lset}:mylist 0 -1 LLEN tutorial:{lset}:mylist预期结果:LSET返回OK;列表变为"four"、"two"、"six";LLEN返回3,长度未变。
四、越界与 Key 不存在
LSET有两类专属错误,需要单独留意:
- 索引越界:对非空列表执行时,index 必须落在
[0, LLEN-1]区间内或是有合法负索引,否则报ERR index out of range。 - Key 不存在:
LSET不会像 LPUSH 那样自动创建 Key,对不存在的 Key 执行报ERR no such key。
LSET tutorial:{lset}:nokey 0 x EXISTS tutorial:{lset}:nokey LSET tutorial:{lset}:mylist 5 x预期结果:第一条报ERR no such key;EXISTS返回0,确认 LSET 没有创建 Key;第三条对长度 3 的列表设置索引 5,报ERR index out of range。示例中的 nokey 从未被成功创建,无需清理。
五、返回值说明
成功时LSET恒返回简单字符串OK,失败时直接报错,没有“部分成功”的中间态。由于成功与失败都是明确结果,客户端可以依据返回值可靠判断;但不能用 LSET 给不存在的 Key 建表,这是它与 LPUSH/RPUSH 的本质差异。
| 结果 | 返回 |
|---|---|
| 覆盖成功 | OK |
| Key 不存在 | ERR no such key |
| 索引越界 | ERR index out of range |
| Key 类型不是 List | WRONGTYPE |
ERR no such key ERR index out of range WRONGTYPE Operation against a key holding the wrong kind of value六、边界情况与错误处理
| 场景 | 行为 |
|---|---|
| Key 不存在 | 报 ERR no such key,不创建 Key |
| 索引越界(超出长度或非法负索引) | 报 ERR index out of range |
| Key 类型不是 List | 报 WRONGTYPE |
| element 为空字符串 | 允许,正常覆盖 |
| 缺少 index 或 element | 报语法错误 |
| 空列表 | 列表非空才存在,实际不会遇到;Key 不存在走 no such key |
SET tutorial:{lset}:str text LSET tutorial:{lset}:str 0 a预期结果:对 String 类型执行报 WRONGTYPE,类型检查先于索引定位。
七、与相近命令的区别
| 命令 | 操作方式 | 列表长度 | 说明 |
|---|---|---|---|
| LSET | 按索引覆盖 | 不变 | 只改写指定位置的值 |
| LINSERT | 按值邻位插入 | 加 1 | 在锚点元素前后插入,不改锚点 |
| LPOS | 按值查索引 | 不变 | 返回匹配元素的索引,常与 LSET 组合 |
| LREM | 按值删除 | 变短 | 删除匹配元素 |
| LPUSH / RPUSH | 两端追加 | 加 1 | 可对不存在的 Key 自动创建列表 |
“查找替换”组合:先LPOS key element拿到索引,再LSET key index newValue,即可按值精准覆盖,不影响其他元素。
八、客户端示例
前提为已安装 redis-py 并准备好本地测试实例。redis-py 的lset(name, index, value)成功返回 True,失败抛出 ResponseError。
importredis r=redis.Redis(host="localhost",port=6379,decode_responses=True)key="tutorial:{lset}:py"try:r.delete(key)r.rpush(key,"one","two","three")print(r.lset(key,0,"four"))# Trueprint(r.lrange(key,0,-1))# ['four', 'two', 'three']print(r.lset(key,-1,"six"))# True,负索引覆盖表尾print(r.lrange(key,0,-1))# ['four', 'two', 'six']try:r.lset(key,5,"x")# 索引越界exceptredis.exceptions.ResponseErrorase:print(e)# index out of rangefinally:r.delete(key)r.close()Java(Jedis)示例,lset返回"OK",错误情形抛出 JedisDataException:
try(Jedisjedis=newJedis("localhost",6379)){Stringkey="tutorial:{lset}:java";jedis.del(key);jedis.rpush(key,"one","two","three");System.out.println(jedis.lset(key,1,"TWO"));// OKSystem.out.println(jedis.lrange(key,0,-1));// [one, TWO, three]try{jedis.lset("tutorial:{lset}:nokey",0,"x");}catch(redis.clients.jedis.exceptions.JedisDataExceptione){System.out.println(e.getMessage());// ERR no such key}jedis.del(key);}九、原子性与并发安全
单命令原子:LSET本身原子执行,并发客户端同时改写同一索引时最后执行者获胜,不会出现半写状态。
读改写组合非原子:典型模式“LINDEX 读出旧值、业务判断、LSET 写回新值”由三步组成,整体不具备原子性,并发下可能覆盖他人刚写入的修改(丢失更新)。
解决方案:需要“读取-判断-写回”一致语义时,用 WATCH/MULTI/EXEC 乐观锁,或把整段逻辑放进 Lua 脚本执行。
性能:中间位置的定位是 O(N);大列表上高频改写中间元素有开销,若热点更新集中且顺序无业务意义,考虑改用 Hash 按字段更新。
十、典型业务场景
状态回写:任务列表中某个任务完成后,用LSET key idx new-status把对应元素替换为带新状态的值。
轮播与推荐位:以 List 维护坑位顺序,运营调整某个坑位内容时按索引覆盖。
按值精准替换:LPOS查索引 +LSET覆盖,实现“把元素 a 替换为 b”而不动其他元素。
修复脏数据:列表某位置的值写错时直接按索引改写,避免删了重插破坏原有顺序。
十一、练习、排错与总结
练习:新建tutorial:{lset}:ex,用 RPUSH 写入a b c;执行LSET ex 1 B返回OK且列表变为a B c;执行LSET ex -1 C返回OK且列表变为a B C,LLEN仍为3;执行LSET ex 3 x报ERR index out of range;对不存在的 Key 执行LSET报ERR no such key且EXISTS为0;对 String Key 执行报 WRONGTYPE。
排错要点:报 no such key 时确认是否误以为 LSET 会建 Key,应先 RPUSH/LPUSH 再改写;报 index out of range 时先用 LLEN 核对长度,注意负索引最小只到 -LLEN;值没变检查是否改错了索引(0 基);报 WRONGTYPE 时用 TYPE 检查类型;并发覆盖问题用 WATCH 或 Lua 包裹读改写流程。清理使用DEL tutorial:{lset}:mylist tutorial:{lset}:ex tutorial:{lset}:str tutorial:{lset}:py tutorial:{lset}:java(示例中的 nokey 从未被创建,无需清理)。速记:1.0 起支持、按索引覆盖长度不变、返回 OK、不创建 Key、Key 不存在与索引越界都报错、首尾 O(1) 中间 O(N)、与 LPOS 组合实现查找替换、读改写组合需自行保证原子性。