Redis之BigKey与HotKey问题详解

 更新时间:2026年02月28日 09:19:25   作者:d3y1  
在Redis的实际使用过程中,BigKey和 HotKey是两个常见且可能导致严重性能问题的问题,它们可能导致 Redis 实例响应变慢、内存使用不均、甚至服务不可用,下面就来介绍一下这两个的问题

概述

在 Redis 的实际使用过程中,BigKey(大键)和 HotKey(热键)是两个常见且可能导致严重性能问题的问题。它们可能导致 Redis 实例响应变慢、内存使用不均、甚至服务不可用。

BigKey 指的是单个键值对占用内存过大的情况。

HotKey 指的是某个键的访问频率远高于其他键的情况。

这两个问题往往同时出现,相互影响,需要系统性地进行预防和处理。

BigKey 问题

什么是 BigKey

BigKey 是指单个键值对占用内存过大的键。Redis 官方建议:

  • String 类型:单个 value 不超过 10KB
  • Hash、List、Set、ZSet 类型:元素个数不超过 5000

超过这些阈值的键可以被视为 BigKey。

BigKey 的危害

1. 内存占用不均

BigKey 会占用大量内存,可能导致 Redis 实例内存使用不均衡。在 Redis Cluster 中,这会导致某些节点的内存使用率远高于其他节点,引发内存倾斜。

2. 阻塞主线程

Redis 是单线程模型,BigKey 的操作会阻塞主线程:

操作影响说明
DEL删除大键会阻塞主线程,时间复杂度 O(N)
HGETALL获取所有字段会阻塞主线程
LRANGE大范围获取列表元素会阻塞
KEYS遍历所有键会严重阻塞
FLUSHDB/FLUSHALL清空数据库会长时间阻塞

3. 网络带宽消耗

BigKey 的读写操作会消耗大量网络带宽,影响其他请求的响应速度。

4. 持久化问题

  • RDB:生成 RDB 文件时,BigKey 会导致 fork 子进程时内存占用翻倍
  • AOF:BigKey 的写入会导致 AOF 文件膨胀,重写时耗时较长

5. 主从同步延迟

BigKey 的同步会导致主从复制延迟增加,影响数据一致性。

BigKey 的检测

1. 使用 redis-cli --bigkeys

Redis 自带的 --bigkeys 命令可以扫描并统计大键:

redis-cli --bigkeys -i 0.1

参数说明:

  • -i 0.1:每次扫描间隔 0.1 秒,避免阻塞

输出示例:

-------- summary -------
Sampled 506 keys in the keyspace!
Total key length in bytes is 1885 (avg len 3.73)

Biggest string found 'user:1001:profile' has 10240 bytes
Biggest   list found 'order:queue' has 10003 items
Biggest    set found 'online:users' has 8005 items
Biggest   hash found 'product:info' has 5012 fields

506 keys with 506 types

2. 使用 SCAN 命令

编写脚本使用 SCAN 命令遍历所有键并检查大小:

#!/bin/bash
redis-cli --scan --pattern "*" | while read key; do
    size=$(redis-cli memory usage "$key")
    if [ $size -gt 10240 ]; then
        echo "BigKey: $key, Size: $size bytes"
    fi
done

3. 使用 MEMORY USAGE 命令

redis-cli memory usage your_key

4. 使用 Redis 慢查询日志

配置慢查询阈值:

redis-cli config set slowlog-log-slower-than 10000  # 10ms
redis-cli slowlog get 10

5. 使用 Redis 模块

  • Redis Modules:如 RedisJSON、RedisTimeSeries 等模块提供更详细的内存分析
  • Redis Insight:官方可视化工具,可以查看内存分布

BigKey 的解决方案

1. 拆分 BigKey

Hash 拆分示例

原始结构:

user:1001:info -> {name: "张三", age: 30, address: "...", ...} (5000+ fields)

拆分后:

user:1001:info:base -> {name: "张三", age: 30}
user:1001:info:contact -> {phone: "...", email: "..."}
user:1001:info:address -> {province: "...", city: "..."}

List 拆分示例

原始结构:

order:queue -> [order1, order2, ..., order10000]

拆分后:

order:queue:0 -> [order1, ..., order1000]
order:queue:1 -> [order1001, ..., order2000]
...
order:queue:9 -> [order9001, ..., order10000]

2. 使用合适的数据结构

场景推荐数据结构避免使用
简单键值对StringHash (少量字段时)
对象属性HashString (JSON)
去重集合SetList
排序集合ZSetSet + 排序
计数器String (INCR)Hash

3. 压缩数据

  • 使用更紧凑的序列化格式(如 MessagePack、Protobuf)
  • 对 String 类型的值进行压缩
  • 使用 Hash 的 ziplist 编码(元素较少时)

4. 异步删除 BigKey

使用 UNLINK 命令替代 DEL

redis-cli unlink your_big_key

UNLINK 会在后台线程中删除键,不会阻塞主线程。

5. 分批次操作

对于大集合的操作,分批次进行:

# 原始方式(会阻塞)
redis.hgetall("big_hash")

# 改进方式(分批次)
cursor = 0
while True:
    cursor, data = redis.hscan("big_hash", cursor, count=100)
    process(data)
    if cursor == 0:
        break

6. 设置过期时间

为 BigKey 设置合理的过期时间,避免数据无限累积:

redis-cli expire your_key 3600

HotKey 问题

什么是 HotKey

HotKey 是指某个键的访问频率远高于其他键的键。通常表现为:

  • 某个键的 QPS 远超其他键
  • 某个键的读写请求集中在短时间内爆发
  • 某个键的访问量占整个 Redis 实例的很大比例

HotKey 的危害

1. CPU 负载不均

在 Redis Cluster 中,HotKey 所在节点的 CPU 负载会远高于其他节点:

节点 A: CPU 95% (包含 HotKey)
节点 B: CPU 20%
节点 C: CPU 15%

2. 网络带宽瓶颈

HotKey 的高频访问会消耗大量网络带宽,影响其他请求。

3. 缓存击穿

当 HotKey 过期时,大量请求会同时穿透到后端数据库,导致数据库压力骤增。

4. 请求堆积

由于 Redis 是单线程,HotKey 的处理会导致其他请求排队等待。

5. 主从同步压力

HotKey 的频繁更新会增加主从同步的负担。

HotKey 的检测

1. 使用 Redis INFO 命令

redis-cli info stats

关注 keyspace_hitskeyspace_misses 指标。

2. 使用 MONITOR 命令(谨慎使用)

redis-cli monitor | grep "your_key"

注意MONITOR 会严重影响性能,仅用于调试,不要在生产环境使用。

3. 使用 Redis 慢查询日志

redis-cli config set slowlog-log-slower-than 0
redis-cli slowlog get 100

4. 使用客户端统计

在应用层记录每个键的访问频率:

from collections import defaultdict

access_stats = defaultdict(int)

def get_redis(key):
    access_stats[key] += 1
    return redis.get(key)

# 定期打印统计
def print_stats():
    for key, count in sorted(access_stats.items(), key=lambda x: x[1], reverse=True)[:10]:
        print(f"{key}: {count}")

5. 使用 Redis 4.0+ 的 LFU 淘汰策略

配置 LFU(Least Frequently Used)淘汰策略:

redis-cli config set maxmemory-policy allkeys-lfu

然后使用 OBJECT FREQ 命令查看访问频率:

redis-cli object freq your_key

6. 使用第三方工具

  • Redis Exporter + Prometheus + Grafana:监控 Redis 指标
  • 阿里云 Redis:提供 HotKey 分析功能
  • 腾讯云 Redis:提供热 Key 监控

HotKey 的解决方案

1. 本地缓存

在应用层使用本地缓存(如 Guava Cache、Caffeine)缓存 HotKey:

// 使用 Caffeine 本地缓存
Cache<String, String> localCache = Caffeine.newBuilder()
    .maximumSize(1000)
    .expireAfterWrite(1, TimeUnit.MINUTES)
    .build();

public String get(String key) {
    // 先查本地缓存
    String value = localCache.getIfPresent(key);
    if (value != null) {
        return value;
    }
    // 再查 Redis
    value = redis.get(key);
    if (value != null) {
        localCache.put(key, value);
    }
    return value;
}

2. 读写分离

对于读多写少的 HotKey,使用读写分离:

应用 -> 读请求 -> Redis 从节点
应用 -> 写请求 -> Redis 主节点

3. Key 分片

将 HotKey 拆分成多个 Key:

原始方式

hot_product:1001 -> 商品信息

分片方式

hot_product:1001:0 -> 商品信息
hot_product:1001:1 -> 商品信息(副本)
hot_product:1001:2 -> 商品信息(副本)

访问时随机选择一个分片:

import random

def get_hot_product(product_id):
    shard = random.randint(0, 2)
    return redis.get(f"hot_product:{product_id}:{shard}")

4. 备份 Key

为 HotKey 创建多个备份,分散请求:

# 写入时同步更新所有备份
def set_hot_key(key, value):
    pipe = redis.pipeline()
    for i in range(3):
        pipe.set(f"{key}:backup:{i}", value)
    pipe.execute()

# 读取时随机选择一个备份
def get_hot_key(key):
    backup = random.randint(0, 2)
    return redis.get(f"{key}:backup:{backup}")

5. 使用 Redis Cluster

在 Redis Cluster 中,HotKey 会分散到不同的节点,但需要注意:

  • 确保数据分片均匀
  • 避免使用 Hash Tag 导致数据集中

Hash Tag 示例(会导致数据集中):

user:{1001}:profile
user:{1001}:orders
user:{1001}:cart

这些键会被分配到同一个节点。

6. 限流保护

对 HotKey 的访问进行限流:

from functools import wraps
import time

class RateLimiter:
    def __init__(self, max_calls, period):
        self.max_calls = max_calls
        self.period = period
        self.calls = {}
    
    def allow(self, key):
        now = time.time()
        if key not in self.calls:
            self.calls[key] = []
        # 清理过期记录
        self.calls[key] = [t for t in self.calls[key] if now - t < self.period]
        if len(self.calls[key]) >= self.max_calls:
            return False
        self.calls[key].append(now)
        return True

limiter = RateLimiter(max_calls=1000, period=1)  # 每秒最多 1000 次

def rate_limit(func):
    @wraps(func)
    def wrapper(key, *args, **kwargs):
        if not limiter.allow(key):
            raise Exception("Rate limit exceeded")
        return func(key, *args, **kwargs)
    return wrapper

@rate_limit
def get_hot_key(key):
    return redis.get(key)

7. 缓存预热

在系统启动或低峰期,提前加载 HotKey 到缓存:

def warm_up_cache():
    hot_keys = get_hot_keys_from_db()  # 从数据库获取热点键列表
    for key in hot_keys:
        value = db.get(key)
        redis.set(key, value, ex=3600)

8. 使用多级缓存

构建多级缓存架构:

应用 -> 本地缓存 -> Redis -> 数据库

9. 消息队列削峰

对于写请求较多的 HotKey,使用消息队列削峰:

应用 -> 消息队列 -> 消费者 -> Redis

最佳实践

1. 设计阶段

  • 合理设计 Key 的命名和结构:避免产生 BigKey
  • 预估数据量:提前规划数据规模,选择合适的数据结构
  • 设置过期时间:为所有 Key 设置合理的过期时间

2. 开发阶段

  • 使用 Pipeline:批量操作减少网络开销
  • 避免使用 KEYS:使用 SCAN 替代 KEYS
  • 监控 Key 大小:定期检查是否有 BigKey 产生

3. 运维阶段

  • 定期巡检:使用工具定期检查 BigKey 和 HotKey
  • 设置告警:对内存使用、慢查询等指标设置告警
  • 容量规划:根据业务增长提前规划容量

4. 应急处理

  • 紧急扩容:当发现问题时,及时扩容
  • 限流降级:对异常请求进行限流和降级
  • 数据迁移:将 BigKey 迁移到独立实例

工具推荐

1. Redis 官方工具

工具用途
redis-cli --bigkeys检测 BigKey
redis-cli --memkeys检测占用内存最多的键
redis-cli --hotkeys检测 HotKey(LFU 模式下)
Redis Insight可视化管理工具

2. 第三方工具

工具特点
redis-rdb-tools分析 RDB 文件,找出 BigKey
redis-faina分析 MONITOR 输出,统计访问频率
Redis ExporterPrometheus 指标导出器
Redis CommanderWeb 管理界面
MedisMac 平台的 Redis 客户端

3. 云服务

  • 阿里云 Redis:提供 BigKey 和 HotKey 分析
  • 腾讯云 Redis:提供热 Key 监控
  • AWS ElastiCache:提供 CloudWatch 监控

总结

BigKey 和 HotKey 是 Redis 使用中的常见问题,但通过合理的设计、有效的监控和及时的优化,可以很好地避免和解决这些问题。

核心要点

  1. 预防为主:在设计阶段就考虑避免 BigKey 和 HotKey
  2. 定期巡检:使用工具定期检查,及时发现问题
  3. 合理拆分:对 BigKey 进行拆分,对 HotKey 进行分散
  4. 多级缓存:构建多级缓存架构,减轻 Redis 压力
  5. 监控告警:建立完善的监控和告警机制

到此这篇关于Redis之BigKey与HotKey问题详解的文章就介绍到这了,更多相关Redis BigKey与HotKey内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Redis字符串String操作详解从基础到高级应用小结

    Redis字符串String操作详解从基础到高级应用小结

    本文全面解析Redis字符串类型,涵盖设置、修改、数字运算、过期处理及分布式锁、计数器等高级应用,提供基础命令与性能优化策略,助开发者高效利用这一核心数据类型实现缓存、统计等功能,感兴趣的朋友一起看看吧
    2025-07-07
  • 详解Redis SCAN命令实现有限保证的原理

    详解Redis SCAN命令实现有限保证的原理

    这篇文章主要介绍了Redis SCAN命令实现有限保证的原理,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值 ,需要的朋友可以参考下
    2019-07-07
  • redis批量迁移key的实例

    redis批量迁移key的实例

    我们知道migrate 命令可以迁移redis的多个key,但是如果redis的key有非常多,就不好解决了,本文主要介绍了redis批量迁移key的实例,具有一定的参考价值,感兴趣的可以了解一下
    2025-04-04
  • Redis中Hash从使用过程到原理说明

    Redis中Hash从使用过程到原理说明

    Redis Hash结构用于存储字段-值对,适合对象数据,支持HSET、HGET等命令,采用ziplist或hashtable编码,通过渐进式rehash优化性能,适用于购物车、计数器等场景
    2025-09-09
  • Redis教程(四):Hashes数据类型

    Redis教程(四):Hashes数据类型

    这篇文章主要介绍了Redis教程(四):Hashes数据类型,本文讲解了Hashes数据类型概述、相关命令列表和命令使用示例等内容,需要的朋友可以参考下
    2015-04-04
  • Redis批量删除指定前缀的Key两种方法

    Redis批量删除指定前缀的Key两种方法

    redis作为缓存服务器在项目中经常使用,使用redis存储数据时,我们经常会将key分组,这篇文章主要给大家介绍了关于Redis批量删除指定前缀的Key两种方法,需要的朋友可以参考下
    2024-01-01
  • Redis中散列类型的常用命令小结

    Redis中散列类型的常用命令小结

    散列类型的键值其实也是一种字典解耦,其存储了字段和字段值的映射,但字段值只能是字符串,不支持其他数据类型,所以说散列类型不能嵌套其他的数据类型。下面就来详细介绍下Redis中散列类型的常用命令,有需要的可以参考学习。
    2016-09-09
  • 浅析Redis如何保证数据不丢失

    浅析Redis如何保证数据不丢失

    Redis是一种Nosql类型的数据存储,全称Remote Dictionary Server,也就是远程字典服务器,本文主要来和大家讨论一下Redis如何保证数据不丢失,需要的可以参考下
    2024-02-02
  • Redis的分布式锁如何使用和代码示例

    Redis的分布式锁如何使用和代码示例

    Redis分布式锁是一种有效的工具,用于在分布式环境中实现互斥访问,Redis提供了一种简单且高效的方式来实现分布式锁,下面给大家介绍是如何使用Redis实现分布式锁的,感兴趣的朋友跟随小编一起看看吧
    2025-11-11
  • 解决redis批量删除key值的问题

    解决redis批量删除key值的问题

    在开发过程中,会遇到要批量删除某种规则的key值,但是通常情况下没有批量删除某一个类的命令,遇到这种情况该如何处理呢?下面小编给大家带来了redis批量删除key值的问题,感兴趣的朋友一起看看吧
    2022-03-03

最新评论