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 形态和大小
相关推荐
java1234_小锋3 小时前
Redis是单线程还是多线程?
数据库·redis·缓存
云计算-Security3 小时前
基于 Keepalived 的 Redis 主备高可用架构设计与实现
redis·keepalived
222you3 小时前
在云服务器上配置redis环境(OpenCloudOS)
数据库·redis·缓存
Zhen (Evan) Wang5 小时前
Docker 完整安装 Redis
redis·docker·容器
-Xie-5 小时前
Redis(十六)——底层数据结构(一)
java·数据结构·redis
MZWeiei16 小时前
Redis持久化机制中的 AOF机制简单介绍
数据库·redis
JoannaJuanCV19 小时前
自动驾驶—CARLA仿真(30)交通管理器(Traffic Manager)
java·redis·自动驾驶
java1234_小锋19 小时前
Redis的热Key问题如何解决?
数据库·redis·缓存
jmxwzy19 小时前
点赞系统问题
java·redis·tidb·pulsar