redis-cli --hotkeys可在大促前压测环境使用,但需满足Redis≥4.0、淘汰策略为LFU、接受1–3秒主线程阻塞三个硬约束;它对突发热点不敏感,建议在ramp-up后执行并人工核对top20热key。

大促前不做热点Key探测,等于把Redis当骰子扔——等它自己崩给你看。自动探测必须在业务低峰期完成建模,防击穿必须在缓存写入时就埋下伏笔,两者不能割裂。
redis-cli --hotkeys 能不能直接用在大促前压测环境?
能,但有三个硬约束:
-
redis-cli --hotkeys要求 Redis ≥ 4.0 且淘汰策略必须是allkeys-lfu或volatile-lfu,否则返回空; - 它基于 LFU 计数器采样,只统计最近 N 次访问(默认 100 万),对突发型热点(如开售前 5 秒预热)不敏感;
- 执行期间会阻塞主线程约 1–3 秒(取决于 key 数量),压测环境可接受,但禁止在大促中段运行。
建议做法:在压测流量 ramp-up 阶段结束后,立即执行一次 redis-cli -h $HOST -p $PORT --hotkeys,抓取 top 20 热 key,再人工核对是否命中已知高风险 key(如 seckill:sku:10086、product:detail:99999)。
客户端埋点统计怎么避免侵入业务代码?
不要在每个 redis.get() 前手动加计数。用 AOP 或二方包拦截更稳妥:
- Spring Boot 项目可在
RedisTemplate的execute()方法上切面,提取命令第一个参数作为 key,过滤掉DEL、EXPIRE等非读操作; - 统计维度必须带时间窗口,比如每 10 秒聚合一次,用
ConcurrentHashMap<string longadder></string>存 key → count,避免锁争用; - 上报阈值设为「10 秒内 count > 3000」,而不是绝对 QPS,否则冷启动时误报率高;
- 关键点:埋点数据要异步落盘或发 Kafka,绝不能同步调用远程监控服务,否则埋点本身成瓶颈。
防击穿的 Lua 脚本里,锁 key 和缓存 key 怎么设计才不冲突?
锁 key 必须和缓存 key 严格隔离,否则缓存失效时锁也被删了:
- 缓存 key 示例:
product:detail:10086; - 对应锁 key 必须带前缀和唯一标识,例如:
lock:hot:product:detail:10086:${user_id % 10}(按用户分片降低锁竞争); - 不能用
lock:product:detail:10086这种简单拼接——万一有人误执行DEL lock:product:detail:10086,整个防护就失效; - Lua 释放锁必须校验 value,脚本里用
redis.call('get', KEYS[1]) == ARGV[1]判断,否则 A 线程锁过期后,B 线程可能误删 A 写入的缓存。
本地缓存 Caffeine 怎么配才不拖垮 JVM?
Caffeine 不是越大越好,尤其在容器化部署下:
-
maximumSize(10_000)是安全起点,超过需结合堆内存评估(每万条 String key + value 约占 20–30MB); -
expireAfterWrite(1, TimeUnit.SECONDS)必须设,否则热点 key 一旦写入就永远不更新,和 Redis 数据不一致; - 禁用
refreshAfterWrite:它会在后台异步 reload,但 reload 失败时旧值仍被返回,掩盖问题; - 真正关键的是监听淘汰事件:
removalListener((key, value, cause) -> log.warn("Caffeine evict: {} cause={}", key, cause)),如果发现大量SIZE原因淘汰,说明 size 设小了;如果是EXPIRED过多,说明 expire 时间太短。
最易被忽略的一点:Caffeine 的 get(key, loader) 方法里,loader 函数必须是幂等的,且内部不能递归调用自身——否则一个 key 加载失败会触发无限重试,吃光线程池。


















