问题记录
后端服务有多个实例,监听 Redis key 过期事件的时候为了防止重复消费,将过期的 key 加上 duplicate_check: 前缀使用 setnx 放到 Redis 中,功能上也没什么问题。
今天排查一个 bug 的时候需要查看 Redis 中的 key 剩余存活时间,无意间发现一些很诡异的 key,有非常长的前缀:
1 2 3 4 5 6 7
| chat_api_conversation_close:8D0A670E3447431285EAF69FC2D0DD02" chat_duplicate_expired_key:chat_api_conversation_close:8D0A670E3447431285EAF69FC2D0DD02" chat_duplicate_expired_key:chat_duplicate_expired_key:chat_api_conversation_close:8D0A670E3447431285EAF69FC2D0DD02" chat_duplicate_expired_key:chat_duplicate_expired_key:chat_duplicate_expired_key:chat_api_conversation_close:8D0A670E3447431285EAF69FC2D0DD02" chat_duplicate_expired_key:chat_duplicate_expired_key:chat_duplicate_expired_key:chat_duplicate_expired_key:chat_api_conversation_close:8D0A670E3447431285EAF69FC2D0DD02" chat_duplicate_expired_key:chat_duplicate_expired_key:chat_duplicate_expired_key:chat_duplicate_expired_key:chat_duplicate_expired_key:chat_api_conversation_close:8D0A670E3447431285EAF69FC2D0DD02" chat_duplicate_expired_key:chat_duplicate_expired_key:chat_duplicate_expired_key:chat_duplicate_expired_key:chat_duplicate_expired_key:chat_duplicate_expired_key:chat_api_conversation_close:8D0A670E3447431285EAF69FC2D0DD02"
|
一个正常的业务 key 前面重复拼接了放重复 key 的前缀。
代码逻辑:
1 2 3 4 5
| public boolean tryAcquireProcessLock(String originalKey, int expireSeconds) { String duplicateKey = "chat_duplicate_expired_key:" + originalKey; Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent(duplicateKey, "1", expireSeconds, TimeUnit.SECONDS); return Boolean.TRUE.equals(ifAbsent); }
|
原因
一开始以为是代码逻辑的问题,看了代码没发现问题。Debug 之后发现是因为在做重复性检查的时候将加了 duplicate_key 前缀的过期 key 又加了过期时间放到了 Redis 中,等到这些 key 超时之后会被监听到,再次被加上防重复前缀又放到 Redis 中,于是就出现了上面套娃的 key 了。
在过期 key 监听中忽略这个前缀的 key 就可以了。