下单时把状态放进 Redis,顺手给了两分钟过期。支付回调把值改成 paid,看起来也正常。再查一次 TTL,原来在倒数的 key 却变成了 -1。
要是你在缓存里看到这种 key,先翻最后一次写入。问题可能就出在那个平平无奇的 SET。
先拿一个测试 key 看一眼
用 Redis 6.0 或更新版本,打开测试实例的 redis-cli,输入下面四行。示例会改动这一个测试 key,请用自己的测试前缀。
SET java-li:ttl-demo ready EX 120
TTL java-li:ttl-demo
SET java-li:ttl-demo paid
TTL java-li:ttl-demo
第一次 TTL 是剩余秒数,刚设置时接近 120。第二次是 -1,表示 key 还在,过期时间已经没了。普通 SET 成功覆盖原值时会清掉已有的过期时间。要是看到 -2,说明检查那一刻已经没有这个 key,可能它刚好过期了。
更新状态时,你要从哪一刻算时间
比如这条状态只允许从第一次写入起保留两分钟。更新时用 KEEPTTL,就能沿用原来的到期时刻。XX 让这次更新只在 key 存在时发生,避免回调迟到后又把它建回来。
SET java-li:ttl-demo ready EX 120
SET java-li:ttl-demo paid XX KEEPTTL
TTL java-li:ttl-demo
读到的秒数会接着往下走。要是 key 在更新前已经过期,带 XX 的 SET 不会写入。还有一种容易漏掉的情况,旧 key 本来就是永久的,KEEPTTL 会照样保留这个状态。所以修完写入逻辑,已经没有 TTL 的旧数据还得按业务规则单独处理。
如果你的约定是每次更新都重新留两分钟,就在写入时直接带 EX 120。允许缺失时补建才省略 XX。
SET java-li:ttl-demo paid XX EX 120
TTL java-li:ttl-demo
最后这组会让已有 key 从本次更新重新倒数。示例 key 随后会自行过期。
回到项目里,测两种回调
找到 Java 代码里真正覆盖值的那一行。可能是某个状态更新方法,也可能是封装好的缓存工具。看实际发出的 Redis 命令有没有带对应选项。先写入带 TTL 的值,等几秒走一次更新,检查剩余时间是否按约定延续或重算。再让 key 先过期,走一次迟到的回调,确认是否允许重建。
能用一条 SET 带上到期规则,就别拆成先写值再补 EXPIRE。两条命令之间,连接一断或另一个请求一插队,key 的状态就可能和你想的不一样。需要分辨命令到底有没有送到 Redis 时,可以先排建连和客户端等待。 Spring Boot Redis 连接超时怎么排查
参考资料
Redis 的过期时间,怎么改个值就没了
https://java.li/archives/redis-set-keepttl
评论