Redis大Key与热点Key问题解决方案

Redis 大 Key 与 热点 Key 问题(解决方案大全 + 可落地实践)

大 Key 和热点 Key 是 Redis 线上事故的两大元凶:

  • 大 Key:单个 key 的 value 太大 / 元素太多,导致阻塞、网络抖动、复制变慢、RDB/AOF 变慢
  • 热点 Key :某个 key QPS 超高,导致单点打满(CPU/带宽/网卡/单线程)
    本文给你一套发现 → 定位 → 解决 → 预防的完整打法(含代码模板与运维命令)。

目录

  • [1. 大 Key / 热点 Key 的定义与危害](#1. 大 Key / 热点 Key 的定义与危害)
  • [2. 如何发现:线上快速定位方法](#2. 如何发现:线上快速定位方法)
  • [3. 大 Key 解决方案(拆分、压缩、异步删除)](#3. 大 Key 解决方案(拆分、压缩、异步删除))
  • [4. 热点 Key 解决方案(多级缓存、分片热点、逻辑过期)](#4. 热点 Key 解决方案(多级缓存、分片热点、逻辑过期))
  • [5. 读写模型与一致性注意事项](#5. 读写模型与一致性注意事项)
  • [6. 预防清单(上线前就该做的事)](#6. 预防清单(上线前就该做的事))
  • [7. Java/Spring Boot 代码模板(可直接抄)](#7. Java/Spring Boot 代码模板(可直接抄))
  • [8. 面试标准回答(1~2 分钟)](#8. 面试标准回答(1~2 分钟))

1. 大 Key / 热点 Key 的定义与危害

1.1 什么是大 Key

常见判定(经验值,不是硬标准):

  • String:value > 1MB(或更小就明显影响延迟)
  • Hash/Set/ZSet/List:元素数量 > 1w(视业务与元素大小)
  • 单次 HGETALL/SMEMBERS/ZRANGE 结果太大(网络与序列化成本爆炸)

1.2 大 Key 的危害

  • 阻塞 Redis 单线程:某些操作时间复杂度与元素数量有关(O(N))
  • 网络抖动:一次返回几十 MB,网卡被打满,其他请求排队
  • 复制与持久化变慢:RDB/AOF rewrite、主从同步更慢
  • 删除卡死:DEL 大 key 是 O(N),可能阻塞几百毫秒甚至秒级

1.3 什么是热点 Key

热点 key = 单个 key 的访问 QPS 极高(例如全站配置、爆款商品、秒杀库存)。

1.4 热点 Key 的危害

  • Redis 是单线程处理命令(主线程),热点会导致:
    • CPU 飙高
    • 请求排队、延迟上升
    • 甚至触发超时/雪崩
  • Cluster 下热点 key 也只在一个槽 → 仍然是单点瓶颈

2. 如何发现:线上快速定位方法

2.1 发现大 Key

方法 A:redis-cli bigkeys(简单但有开销)
bash 复制代码
redis-cli --bigkeys

注意:

  • 会扫描全库,线上大库慎用
  • 适合离峰、或在从库跑
方法 B:MEMORY USAGE(精准但需要逐个 key)
bash 复制代码
MEMORY USAGE mykey
方法 C:SCAN + 采样统计(推荐做自动化)
  • 用 SCAN 抽样 key
  • 对可疑 key 做 TYPE + STRLEN/HLEN/LLEN/SCARD/ZCARD + MEMORY USAGE

2.2 发现热点 Key

方法 A:MONITOR(强烈不建议线上长开)
bash 复制代码
MONITOR
方法 B:命令统计(推荐)
bash 复制代码
INFO commandstats

看:

  • 某些命令调用次数是否异常(GET/HGETALL 等)
方法 C:客户端埋点 + TopN key(最靠谱)
  • 在业务侧记录 key 的 QPS TopN(采样即可)
  • 或使用网关/代理层统计

3. 大 Key 解决方案(拆分、压缩、异步删除)

3.1 方案一:拆分 Key(核心解法)

3.1.1 String 大对象拆分

把一个大 JSON 拆成多个块:

  • user:profile:1:part:0
  • user:profile:1:part:1
  • ...

优点:单次读写变小

缺点:需要拼装、版本控制

3.1.2 Hash 拆分(字段分桶)

原:user:tags:{uid} 一个 hash 放几万字段

改:按字段 hash 分桶:

  • user:tags:{uid}:0
  • user:tags:{uid}:1
  • ...(比如 64 桶)
3.1.3 List/Set/ZSet 拆分(按分页/时间/范围)

例如消息列表:

  • msg:{uid}:202512 按月拆
  • 或 msg:{uid}:page:{p} 按页拆

3.2 方案二:避免全量读取(别用 HGETALL/SMEMBERS)

  • Hash:用 HMGET / HSCAN
  • Set:用 SSCAN
  • ZSet:用 ZRANGE 分页(带 LIMIT)
  • List:用 LRANGE 分页

3.3 方案三:压缩/编码优化(减少传输与内存)

  • JSON → MessagePack / Protobuf
  • 使用更短字段名
  • 对大文本压缩(gzip/snappy)
    但注意:压缩会增加 CPU,得压测权衡

3.4 方案四:删除大 Key 用异步(避免阻塞)

3.4.1 UNLINK(推荐)
bash 复制代码
UNLINK bigkey
3.4.2 过期删除

给 key 设置短 TTL,让它自然过期(适合可接受延迟删除的业务)。


4. 热点 Key 解决方案(多级缓存、分片热点、逻辑过期)

4.1 多级缓存(本地缓存 Caffeine + Redis)

热点 key 读多写少:

  • 本地缓存命中直接返回
  • Redis 作为二级

优点:立刻减少 Redis QPS

缺点:一致性更复杂(但对配置类很合适)


4.2 热点 Key 复制 N 份(读扩散)

把一个热点 key 复制成 N 份:

  • hot:config:0
  • hot:config:1
  • ... hot:config:N-1

读时随机读一份;写时更新所有副本(或异步批量更新)。


4.3 逻辑过期 + 异步刷新(热点最稳)

  • key 物理 TTL 设很长
  • value 带 expireAt
  • 逻辑过期后返回旧值,同时只触发一个线程刷新

优点:不会击穿 DB

缺点:短时间读旧值


4.4 限流/降级(保护系统)

热点持续异常时:

  • 限流
  • 返回默认值/旧值
  • 或降级到静态配置

5. 读写模型与一致性注意事项

  • 热点 key 复制:写时带版本号,读时校验版本(避免读旧副本)
  • 多级缓存:发布事件清理本地缓存(或短 TTL)
  • 大 key 拆分:需要版本号/分片校验,避免部分更新拼装错乱

6. 预防清单(上线前就该做的事)

  • 限制单 value 最大大小、单集合最大元素数
  • 严禁/审查:HGETALL、SMEMBERS、KEYS
  • 缓存 TTL 加随机抖动
  • 大 key 删除用 UNLINK
  • 业务侧统计 TopN 热点 key(采样)
  • 热点接口预留本地缓存/降级开关

7. Java/Spring Boot 代码模板

7.1 热点 key 本地缓存(Caffeine)+ Redis 二级

java 复制代码
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.time.Duration;

public class HotKeyCache {

    private final Cache<String, String> local = Caffeine.newBuilder()
            .maximumSize(10_000)
            .expireAfterWrite(Duration.ofSeconds(30))
            .build();

    private final StringRedisTemplate redis;

    public HotKeyCache(StringRedisTemplate redis) {
        this.redis = redis;
    }

    public String get(String key) {
        String v = local.getIfPresent(key);
        if (v != null) return v;

        v = redis.opsForValue().get(key);
        if (v != null) {
            local.put(key, v);
        }
        return v;
    }

    public void invalidate(String key) {
        local.invalidate(key);
    }
}

7.2 热点 key 复制 N 份(读扩散)

java 复制代码
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.concurrent.ThreadLocalRandom;

public class HotKeyReplica {

    private final StringRedisTemplate redis;
    private final int replicas;

    public HotKeyReplica(StringRedisTemplate redis, int replicas) {
        this.redis = redis;
        this.replicas = replicas;
    }

    private String replicaKey(String baseKey, int idx) {
        return baseKey + ":" + idx;
    }

    public String get(String baseKey) {
        int idx = ThreadLocalRandom.current().nextInt(replicas);
        return redis.opsForValue().get(replicaKey(baseKey, idx));
    }

    public void setAll(String baseKey, String value) {
        for (int i = 0; i < replicas; i++) {
            redis.opsForValue().set(replicaKey(baseKey, i), value);
        }
    }
}

7.3 大 Key 删除用 UNLINK(避免阻塞)

java 复制代码
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;

import java.util.Collections;

public class SafeDelete {

    private static final DefaultRedisScript<Long> UNLINK_SCRIPT = new DefaultRedisScript<>(
            "return redis.call('unlink', KEYS[1])", Long.class);

    private final StringRedisTemplate redis;

    public SafeDelete(StringRedisTemplate redis) {
        this.redis = redis;
    }

    public void unlink(String key) {
        redis.execute(UNLINK_SCRIPT, Collections.singletonList(key));
    }
}

8. 面试标准回答(1~2 分钟)

大 key 指单个 key 过大或集合元素过多,会导致 Redis 单线程阻塞、网络抖动、复制与持久化变慢,删除也会卡住。解决通常是拆分 key、避免全量读取、压缩编码,以及删除时用 UNLINK 异步释放。

热点 key 指某个 key 访问 QPS 极高,会把单节点 CPU/带宽打满。解决通常是多级缓存、本地缓存、热点 key 复制 N 份做读扩散、逻辑过期异步刷新,以及必要时限流降级。

线上需要监控与定位:bigkeys、MEMORY USAGE、commandstats、业务侧 TopN key 采样等。


一句话总结

  • 大 key:拆分 + 分页读 + UNLINK 删除
  • 热点 key:本地缓存 + 读扩散 + 逻辑过期
  • 预防 > 救火:上线前限制 key 形态和大小
相关推荐
IT大白鼠3 天前
Redis 系列 · 第 01 篇——认知入门:Redis 是什么
redis·nosql
彧azz3 天前
Linux 环境下 Redis 学习总结:数据类型、持久化、锁、事务、主从与缓存问题
linux·redis·笔记·学习·面试
lbb 小魔仙3 天前
OpenClaw + cpolar 实战:远程 NAS、分享小游戏、RDP,再配置公网 AI 入口
数据库·人工智能·redis·oracle·prometheus
夕除3 天前
redis--018
redis
两锭子3 天前
Redis基础
redis
烟沙九洲3 天前
Redis 缓存穿透、缓存击穿、缓存雪崩
数据库·redis
字节探索3 天前
Redis 只会当缓存用?这 10 大实战场景,让你的系统快到飞起
redis·后端
于樱花森上飞舞3 天前
【Redis】哨兵详解
java·开发语言·数据库·redis
Rain的Java大神之路3 天前
如何快速上传10G文件
java·spring boot·redis·后端·mysql·spring cloud·面试
Wang's Blog3 天前
Java 接入Redis: 通用命令与键管理
java·服务器·redis