⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 http://www.iocoder.cn/Fight/dewuermianRedis-zhongmouge-Key-fangwenliangtebiedazenmebanwoRedis-nengdingdezhu/ 「芋道源码」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

面试考察点

  1. 基础掌握度:能否区分热点 Key 和大 Key(一个是”访问太多”,一个是”数据太大”),以及热点 Key 对 Redis 集群的具体影响。
  2. 排查能力:是否知道生产环境中如何发现热点 Key(redis-cli --hotkeys、业务监控等),而不是等出了问题才排查。
  3. 方案设计能力:能否针对不同场景给出对应方案(本地缓存、读写分离、Key 分片),并说明各自的适用场景和代价。

热点 Key vs 大 Key:别搞混了

热点 Key 是指被大量客户端频繁访问的 Key,QPS 远高于其他 Key,导致单个 Redis 节点成为瓶颈。

维度 热点 Key 大 Key
问题本质 访问频率过高 数据体积过大
核心危害 单节点 CPU/网络被打满 主线程阻塞、网络拥塞
典型场景 秒杀商品、热搜话题 10MB 的 JSON、百万级集合
解决思路 分散访问压力 拆分数据体积

一句话结论:热点 Key 的核心问题是访问集中在一个节点上,解决方案是本地缓存(减少请求)+ 读写分离(分散读压力)+ Key 分片(分散到多个节点)。

热点 Key 的三种死法

单节点过载:热点 Key 集中在某个节点,该节点 CPU 和网卡带宽被瞬间打满,其他节点空闲。

邻居效应:同一节点上的其他 Key 响应变慢,一个热点 Key 拖垮一整片业务。

主从同步延迟:热点 Key 涉及写操作时,主节点压力大导致复制延迟,从节点读到旧数据。

典型场景:

  • 突发热点(明星官宣、热搜):不可预测,最危险
  • 预期内热点(秒杀、限时抢购):可以提前准备
  • 架构缺陷(全局配置放单个 Key):人为制造的热点,改设计就能解决

怎么发现热点 Key

方法一:redis-cli --hotkeys(Redis 4.0+)

# 需要先开启 LFU 淘汰策略
redis-cli --hotkeys

# 输出示例:
# [Hot Key] user:profile:10086 (access count: 583210)
# [Hot Key] seckill:goods:1001 (access count: 421305)

基于 Redis 的 LFU 计数器统计,前提是淘汰策略必须设为 volatile-lfu 或 allkeys-lfu。

方法二:MONITOR 命令(临时排查,慎用)

redis-cli MONITOR | grep "GET\|HGET" | awk '{print $NF}' | sort | uniq -c | sort -nr | head -20

严重警告:MONITOR 在高 QPS 下会导致 10%~30% 性能下降,只能短时间使用。

方法三:业务层监控(推荐长期方案)

public class HotKeyDetector {
// Guava Cache 做 1 秒滑动窗口
private final Cache<String, AtomicLong> counterCache = CacheBuilder.newBuilder()
.expireAfterWrite(1, TimeUnit.SECONDS)
.build();

private static final long HOT_THRESHOLD = 1000;

public void recordAccess(String key) {
AtomicLong counter = counterCache.get(key, () -> new AtomicLong(0));
long count = counter.incrementAndGet();
if (count == HOT_THRESHOLD) {
alertService.warn("检测到热点 Key:" + key + ",QPS:" + count);
}
}
}

对 Redis 零侵入,适合长期运行,可结合 Prometheus 做可视化。

四种解决方案

方案一:本地缓存(最优先)

在应用服务器本地缓存热点数据,请求直接从内存返回,不经过 Redis:

public class LocalCacheSolution {
private final Cache<String, String> localCache = Caffeine.newBuilder()
.maximumSize(10000)
.expireAfterWrite(5, TimeUnit.SECONDS) // 5 秒过期,容忍短暂不一致
.build();

public String getHotData(String key) {
// 1. 先查本地缓存
String value = localCache.getIfPresent(key);
if (value != null) return value;

// 2. 本地 miss,查 Redis
value = redis.get(key);
if (value != null) {
localCache.put(key, value);
return value;
}

// 3. Redis 也 miss,查 DB
value = db.query(key);
if (value != null) {
redis.set(key, value, 30, TimeUnit.MINUTES);
localCache.put(key, value);
}
return value;
}
}

热点 Key 的 Redis QPS 可能从 30 万降到几千。适用于数据量小、更新不频繁的场景。

方案二:Key 分片

将一个热点 Key 复制为 N 个副本,分布在不同节点:

public class HotKeyShardingSolution {
private static final int SHARD_COUNT = 16;

// 写入时:同时写 N 个分片
public void setHotKey(String key, String value) {
for (int i = 0; i < SHARD_COUNT; i++) {
redis.set(key + ":shard:" + i, value, 30, TimeUnit.MINUTES);
}
}

// 读取时:随机选一个分片
public String getHotKey(String key) {
int shard = ThreadLocalRandom.current().nextInt(SHARD_COUNT);
return redis.get(key + ":shard:" + shard);
}
}

30 万 QPS 分 16 片,每个节点只承受约 1.9 万。代价是写入开销和内存都翻 N 倍,适合读远多于写的场景。

方案三:读写分离 + 多级缓存(京东方案)

三级协同:本地缓存挡 90% → Redis 从节点挡 9% → DB 熔断兜底 1%。30 万 QPS 的热点 Key 也能稳定应对。

方案四:限流降级(兜底)

对热点 Key 的访问频率限流,超阈值直接返回降级数据。实现简单,但部分用户会看到降级内容。

方案对比与生产推荐

方案 优点 缺点 适用场景
本地缓存 性能最好,直接挡在应用层 多实例数据不一致 数据量小、更新不频繁
Key 分片 读写都能分散 写入开销大,维护成本高 读远多于写
读写分离 对应用透明 主从延迟 有从节点资源
限流降级 兜底保命 部分用户看到降级数据 配合其他方案使用

生产推荐组合:本地缓存(首选)+ 读写分离(分散读压力)+ 限流降级(兜底保命)。

面试高频追问

追问一:本地缓存和 Redis 的一致性怎么保证?

本地缓存过期时间设短(3~5 秒),容忍短暂不一致。要求高的场景,用 Redis Pub/Sub 或 MQ 广播通知所有实例失效本地缓存。大多数热点数据(商品详情、文章内容)短暂不一致是可接受的。

追问二:热点 Key 和大 Key 同时存在怎么办?

先拆大 Key(减小体积),再分散热点(减少单节点压力)。两者解决思路完全不同,但可能同时出现在同一个 Key 上。

文章目录
  1. 1. 面试考察点
  2. 2. 热点 Key vs 大 Key:别搞混了
  3. 3. 热点 Key 的三种死法
  4. 4. 怎么发现热点 Key
  5. 5. 四种解决方案
    1. 5.0.1. 方案一:本地缓存(最优先)
    2. 5.0.2. 方案二:Key 分片
    3. 5.0.3. 方案三:读写分离 + 多级缓存(京东方案)
    4. 5.0.4. 方案四:限流降级(兜底)
  • 6. 方案对比与生产推荐
  • 7. 面试高频追问