得物二面:Redis 中某个 Key 访问量特别大怎么办?我:Redis 能顶得住... 生瓜蛋子 生瓜蛋子

热点 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+)

bash 复制代码
# 需要先开启 LFU 淘汰策略
redis-cli --hotkeys

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

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

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

bash 复制代码
redis-cli MONITOR | grep "GET|HGET" | awk '{print $NF}' | sort | uniq -c | sort -nr | head -20

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

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

ini 复制代码
public class HotKeyDetector {
    // Guava Cache 做 1 秒滑动窗口
    privatefinal Cache<String, AtomicLong> counterCache = CacheBuilder.newBuilder()
            .expireAfterWrite(1, TimeUnit.SECONDS)
            .build();

    privatestaticfinallong 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:

typescript 复制代码
public class LocalCacheSolution {
    privatefinal 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 个副本,分布在不同节点:

arduino 复制代码
public class HotKeyShardingSolution {
    privatestaticfinalint 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 上。

相关推荐
掘金者阿豪2 小时前
Spring Data JPA 接入金仓数据库:少写代码,多干活
前端·后端
Moment2 小时前
AI 时代,为什么全栈项目越来越离不开 Monorepo 和 TypeScript
前端·javascript·后端
神奇小汤圆2 小时前
字节一面凉了!被问接口超时频繁,线程池该怎么优化?面试官:你管这叫高并发优化?
后端
Jenlybein2 小时前
用 uv 替代 conda,速度飙升(从 0 到 1 开始使用 uv)
后端·python·算法
用户298698530142 小时前
Java 提取 HTML 文本内容:两种轻量级实现方案对比
java·后端
程序边界3 小时前
行标识符的秘密:OID和ROWID的技术演进之路
后端
golang学习记3 小时前
Go 结构化日志新宠:`slog` 入门与实战指南(附避坑秘籍)
后端
tltwuyulw3 小时前
Java的函数式编程(三)
java·后端
直奔標竿3 小时前
Java开发者AI转型第九课!突破知识边界!企业级 RAG (检索增强生成) 核心架构与 ETL 管道初探
java·开发语言·人工智能·后端·spring