DEBUG.WIKI
V.PS:让项目靠近用户,亚太、欧洲、北美 KVM VPS
IPPeak:从目标地区访问网页,住宅代理、SEO 监测、广告验证
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国

Redis GETSET 更新值后 TTL 变成 -1

GETSET 会清除键原有过期时间。按 Redis 版本选择 SET KEEPTTL 或 GET KEEPTTL,保留截止时间,并处理键已消失和无有效期的分支。

Redis 字符串键原本设置了过期时间,执行 GETSET 更新值后,TTL 却变成 -1。值虽然更新成功,键也变成了没有过期时间的持久键。成功执行的 GETSET 会清除原有过期设置;它返回旧值,并不表示保留旧值附带的有效期。

这个场景需要先区分“保留原截止时间”和“从更新时重新计时”。例如原本只剩 20 秒的缓存,更新后仍应在原时间到期,就不能简单重新设置为 60 秒。

用 TTL 确认丢失的是过期设置

在正确的实例和逻辑数据库中,对实际目标键读取:

TYPE demo:ttl
TTL demo:ttl

demo:ttl 是示例键名。本文针对字符串键;其他类型不能直接套用字符串覆盖操作。Redis 2.8 起,TTL 返回 -1 表示键存在但没有过期时间,-2 表示键不存在。两者处理方式不同:键可能已经正常过期,不能看到负数就重新写入。

若更新前有正的 TTL,成功 GETSET 后变成 -1,应检查应用实际发送的写入命令。普通 SET 没有指定保留过期时间时,同样可能把原过期设置清掉。客户端方法名称不够,应核对参数和服务器版本。

Redis 6.0 起:不需要旧值时使用 KEEPTTL

已经确认目标键存在、只允许更新现有键时,可以采用:

SET demo:ttl value-b XX KEEPTTL

KEEPTTL 保留键当时已有的过期时间,XX 则限制为键存在才写入。如果键已经消失,命令不创建新键,应用要按未写入的返回结果处理。

这两个选项并不负责建立新的有效期。如果目标键本来没有过期时间,更新后仍然没有;如果业务允许创建新键,去掉 XX 又会改变不存在时的行为,不能不加判断地替换。

KEEPTTL 从 Redis 6.0 开始提供。它与 EX、PX、EXAT、PXAT 不能在同一条 SET 中混用;要么保留现有期限,要么明确指定新的期限。

Redis 6.2 起:同时需要返回旧值

需要保留 GETSET 返回旧字符串的用途时,可使用 Redis 6.2 起支持的 SET 的 GET 选项:

SET demo:ttl value-b XX GET KEEPTTL

服务器在同一条命令中处理读旧值、写新值和保留期限。不要假定返回值仍是普通 SET 的 OK:GET 会改变返回内容,应用必须同步调整处理逻辑。已有值不是字符串时会报错并中止操作,不能用它替换其他类型的更新命令。

GETSET 从 Redis 6.2 起已被标记为弃用。旧 Redis 2.8 环境不能使用这些新选项;客户端升级也不会让旧服务器识别它们。

为什么不先读 TTL,再把秒数写回

客户端先读取 TTL,再执行 SET ... EX,中间可能经过排队、网络延迟或其他客户端修改。把读到的剩余秒数从后一次写入重新开始计算,会改变截止时间;原键也可能在两条命令之间过期。

因此,这种拆成两次请求的办法不能当作原子替代。旧版本必须继续运行时,需要另行审核服务端原子逻辑,覆盖键不存在、无过期时间和并发修改等分支,不能照搬“读 TTL 后无条件 EXPIRE”的片段。

已经被 GETSET 清掉的截止时间,也不会被后来加上的 KEEPTTL 自动找回。应根据应用保存的业务期限或允许的缓存重建规则恢复,不能给所有异常键猜一个统一 TTL。

在隔离键上验收

下面的示例只用于 Redis 6.2 及以上的可丢弃测试数据,不要使用业务键名:

SET demo:ttl-check value-a EX 60
TTL demo:ttl-check
SET demo:ttl-check value-b XX GET KEEPTTL
GET demo:ttl-check
TTL demo:ttl-check

在 60 秒内完成时,更新应返回旧值,读取应得到新值,剩余时间继续减少而非变成 -1 或重新回到完整时长。再验证键已过期与原本无 TTL 的分支,确认应用不会把它们误当作正常续期。

发布前保留原客户端调用与配置。回退代码并不会恢复已经丢失的截止时间;若旧调用仍会清除 TTL,应先控制这类写入,再按已确认的业务规则处理受影响键。