一个报表 key 回源要 8 秒,重建锁用的是表名 report,其他 300 个报表 key 全部在等锁,接口成片超时。乍看像缓存或者权限的锅,追下去往往不是。

lockKey = "lock:report:" + reportId;
if (!redis.set(lockKey, "1", "NX", "EX", 3)) wait();

追下去是这么回事:锁的粒度按表加,同一张表的任何 key 重建都互斥,一个慢查询把整张表的重建都堵住。锁还没有超时,拿不到的请求一直阻塞。

碰到「缓存重建锁粒度」这类问题,我习惯先把当前配置原样备份一份,改坏了立刻换回来,不用现场推。测试环境和线上的差别,往往就在版本号和那几行被忽略的配置上。

线上出问题先对时间点,跟发布、备份、任务执行的时刻排一排,能省掉一半功夫。日志级别临时调高可以,记得改回去,长期开着既占磁盘又掩盖真问题。

具体做法是,锁 key 落到具体业务 id 一个 key 一把锁;本地锁加 Redis 锁两级 减少跨实例竞争;锁设 3 秒超时 拿不到就返回旧值或稍后重试。

验收就看一点,慢 key 重建时其他 300 个 key 正常返回,只有同一个 key 的请求等待,接口超时消失。

作者 9823189