您的当前位置:首页>全部文章>文章详情

【Redis】如何解决 Redis 大 Key(Big Key)问题

CrazyPanda发表于:2025-03-19 17:40:29浏览:35次TAG: #redis

如何解决 Redis 大 Key(Big Key)问题?
1. 什么是大 Key?
大 Key(Big Key) 指的是 单个 Key 的数据量特别大,通常体现在:

单个 String 类型的 Key 存储了超长的内容(如超大 JSON、Base64 图片)。
单个 List/Set/Zset/Hash 存储大量元素,导致:
查询效率下降(一次查询数据过多)。
删除或过期开销大(删除一个 Key 可能会卡 Redis)。
主从复制或数据持久化时阻塞 Redis(大 Key 影响 RDB、AOF 复制)。

2. 如何发现大 Key?

(1)使用 SCAN 命令遍历所有 Key
redis-cli --bigkeys

示例:

# 扫描 Redis 找出最大的 Key
redis-cli --bigkeys

# 结果示例:
# Largest string found 'user_session' with 10MB
# Largest list found 'comments_list' with 1M elements

分析

  • user_session 是超大字符串(10MB)。
  • comments_list 是超长列表(100W 条)。
(2)统计 Key 的类型和大小
# 查看 Key 的类型
TYPE big_key_name

# 统计 List/Set/Hash/Zset 的元素数量
LLEN big_list_key   # List
SCARD big_set_key   # Set
HLEN big_hash_key   # Hash
ZCARD big_zset_key  # Zset

3. 如何解决大 Key 问题?

针对不同的数据结构,采取不同优化策略:

方案 1:大 Key 拆分(Sharding)
核心思路:将一个大 Key 拆分成多个小 Key,减少 Redis 负担。

(1)String 过大:分片存储
问题:单个 String 存储了超大 JSON 或图片,导致 GET 操作慢。
解决方案:

拆分成多个小 Key(如 big_key:0, big_key:1)。
读取时分批合并。
示例

// 存储时拆分
redis.set("big_key:0", sub_string_0);
redis.set("big_key:1", sub_string_1);
redis.set("big_key:2", sub_string_2);

// 读取时合并
string value = redis.get("big_key:0") + redis.get("big_key:1") + redis.get("big_key:2");

(2)List/Set/Hash 过大:拆分 Key
问题:List/Set/Zset 里的元素过多,导致 LRANGE、SMEMBERS 查询慢。
解决方案:

按照 ID 取模拆分多个 Key,如 big_list:0, big_list:1。
查询时 Hash 计算 Key。
示例

// 按 userId 取模
int index = userId % 5;
redis.lpush("big_list:" + index, data);

// 查询时按 hash 取 Key
redis.lrange("big_list:" + (userId % 5), 0, 10);

适用场景

  • 评论列表(List)
  • 大集合(Set/Hash)

方案 2:分页存储

核心思路:使用 分页查询,避免一次性读取过多数据。

示例

int page = 0;
int pageSize = 50;
redis.lrange("big_list", page * pageSize, (page + 1) * pageSize - 1);

方案 3:Lazy Deletion(懒删除)

核心思路防止一次性删除大 Key 影响 Redis 性能,采用分批删除

方案

  1. 异步删除(Redis 4.0+ 提供 UNLINK 命令)
  2. 分批删除(Lua 脚本)

示例

# 使用 UNLINK 异步删除
UNLINK big_key
-- Lua 脚本:分批删除
local key = KEYS[1]
local count = ARGV[1]
for i = 1, count do
    redis.call("LPOP", key) -- 每次删除部分元素
end

适用场景

  • 避免 DEL 造成阻塞

方案 4:使用 HyperLogLog 代替 Set

核心思路:如果不需要存储所有数据,只需要计数,使用 HyperLogLog

示例

# 统计 UV(去重访问量)
PFADD unique_users user_1
PFADD unique_users user_2
PFCOUNT unique_users

方案 5:使用 Redis Stream 替代大 List

核心思路Redis Stream 支持自动删除老数据,适合高并发大数据流场景。

示例

# 添加消息
XADD messages * user "Tom" text "Hello"

# 只保留最近 10W 条消息
XTRIM messages MAXLEN 100000

适用场景

  • 日志流、消息队列

方案 6:Redis 结合 MySQL 分层存储

核心思路冷数据移到 MySQL,Redis 只存热点数据

示例

if (redis.exists(key)) {
    return redis.get(key);  // 先查 Redis
} else {
    std::string value = queryDatabase(key); // 查 MySQL
    redis.setex(key, 3600, value); // 重新写入 Redis
    return value;
}

适用场景

  • 超大用户数据、订单记录

 

总结

问题 解决方案 适用场景
String 过大 分片存储 JSON 配置、Base64 图片
List/Set 过大 分页存储 + Key 拆分 评论列表、好友列表
删除大 Key 慢 UNLINK + 分批删除 热点数据过期
Set 计数过大 用 HyperLogLog 代替 UV 统计、去重计数
高并发大数据流 Redis Stream 代替 List 消息流、日志
数据分层 Redis + MySQL 冷热数据存储 超大用户数据

 

面试标准答案

解决 Redis 大 Key 需要 5 大方案:

拆分 Key(Sharding) → 把大 Key 拆成多个 Key,分摊压力。
分页存储 → 使用 LRANGE 分页查询,避免一次性返回过多数据。
Lazy Deletion(懒删除) → 用 UNLINK 或分批删除,避免 Redis 卡顿。
HyperLogLog/Stream 替代 → 用 HyperLogLog 代替 Set 计数,用 Stream 代替 List。
冷数据存 MySQL,Redis 只存热点数据 → Redis + MySQL 分层存储,减少大 Key 占用。
推荐最佳方案:Key 拆分 + 分批删除 + 分层存储,结合业务需求优化。

猜你喜欢

【Redis】Redis下载安装
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets),带有范围查询的 有序集合(sorted sets) , bitmaps, hyperloglogs 和 地理空间(geospatial) 索引。 Redis 内置了 复制(replication), LUA脚本(Lua scripting), LRU驱动事件(LRU eviction), 事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel) 和自动 分区(Cluster)提供高可用性(high availability)
发表于:2025-03-18 浏览:36 TAG: #redis
【Redis】如何解决 Redis 大 Key(Big Key)问题
1. 什么是大 Key? 大 Key(Big Key) 指的是 单个 Key 的数据量特别大,通常体现在: 单个 String 类型的 Key 存储了超长的内容(如超大 JSON、Base64 图片)。 单个 List/Set/Zset/Hash 存储大量元素,导致: 查询效率下降(一次查询数据过多)。 删除或过期开销大(删除一个 Key 可能会卡 Redis)。 主从复制或数据持久化时阻塞 Redis(大 Key 影响 RDB、AOF 复制)。
发表于:2025-03-19 浏览:36 TAG: #redis
【阿里云】日志服务sls查询语句SPL
一、阿里云日志服务SPL语法归纳1.SPL简介SPL(SearchProcessingLanguage)用于查询和处理日志数据,支持检索、过滤、分析日志。2.基本查询语法查询所有日志:* 1...
发表于:2025-03-06 浏览:44 TAG: #sls
【Redis】Memcached和Redis的区别
一、Redis与Memcached的区别两者都是非关系型内存键值数据库,现在公司一般都是用Redis来实现缓存,为什么不用Memcached呢?接下来分析一下Redis、Memcached的区别:1、...
发表于:2025-03-18 浏览:34 TAG: #memcached #redis
【负载均衡】Nginx实现负载均衡的4种方式
Nginx提供了多种方式实现负载均衡,以下是其中常用的4种方式:1.轮询(Round Robin):         这是默认的负载均衡算法,Nginx按照请求的顺序依次将请求分配给后端的服务器。每个服务器按照其权重来处理请求,然后按顺序循环分配。这种算法简单且平均地将负载分配给后端服务器,适用于后端服务器配置相同、处理能力相当的场景。
发表于:2025-03-18 浏览:34 TAG: #负载均衡
【Redis】Redis的主从复制和哨兵模式
Redis 支持三种集群模式,分别为主从模式、哨兵模式和Cluster(集群)模式。 主从模式:从节点异步的从主节点复制数据,这种架构主节点故障后无法自动切主。类似于mysql的主从复制。 哨兵模式:该模式在主从复制基础上加了一个哨兵集群负责监控主节点和从节点。如果检测到主节点故障,系统可以自动将从节点晋升为新的主节点。实现了自动故障转移。类似于mysql的主从复制+MHA。 集群模式:主从模式面临内存容量和写入性能的限制,因为这种模式的写入能力仍然局限于单个节点。为了解决这一问题,Redis在3.x版本之后推出了Cluster集群模式。Cluster模式通过数据分片和节点的水平扩展,实现了更高效的内存利用和写入性能。类似于mysql的mycat分片。
发表于:2025-03-18 浏览:37 TAG: #redis
【Docker】Docker常用命令
Docker常用命令一、启动类1.启动docker2.关闭docker3.重新启动docker4.docker设置自启动5.查看docker运行状态6.查看docker版本号等信息7.docker帮助...
发表于:2025-03-13 浏览:41 TAG: #docker
【后端】微服务和分布式系统的区别
微服务和分布式系统是两种不同的概念,它们在软件架构和系统设计中扮演着不同的角色。以下是它之间的主要区别:1. 含义不同。微服务是一种架构风格,它将一个单一应用程序开发为一组小型服务,每个服务运行在自己的进程中,服务间通信采用轻量级通信机制(通常用HTTP资源API)。这些服务围绕业务能力构建并且可通过全自动部署机制独立部署。而分布式系统是若干独立计算机的集合,这些计算机对用户来说就像单个相关系统,即整个系统是由不同的计算机组成,而用户是无感知的,就像访问一台计算机一样。2. 概念层次不同。微
发表于:2024-06-06 浏览:260 TAG:
【后端】PHP、Python、Java 和 Go语言对比
PHP、Python、Java 和 Go 都是流行的编程语言,每种语言都有其独特的优势和适用场景。下面是对这些语言的一些基本对比:一:PHP适用场景:主要用于Web开发,特别是服务器端脚本。特点:语法简单易懂,学习曲线平缓。与HTML结合紧密,适合快速开发小型到中型Web应用。性能:通常性能低于Java和Go,但在一些优化和缓存策略下也能达到不错的效果。生态系统:拥有庞大的社区和丰富的库/框架,如Laravel、thinkphp,Symfony等。优势:PHP语言开发的优势在于其开源免费、跨平台
发表于:2024-02-24 浏览:310 TAG:
【负载均衡】负载均衡是什么,负载均衡有什么作用
一、什么是负载均衡 负载均衡是一种在计算机网络和系统架构中使用的技术,用于均衡分发工作负载到多个资源,比如:服务器、计算节点或存储设备上,以提高系统的性能、可伸缩性。 在传统的单个服务器架构中,当请求量增加时,单个服务器可能无法处理所有的请求,导致性能下降或系统崩溃。 负载均衡技术通过将负载(请求)分发到多个服务器上,实现资源的合理利用,从而平衡服务器的负载。 这样可以提高系统的处理能力,增加并发处理能力,并减少单点故障的风险。
发表于:2025-03-18 浏览:35 TAG: #负载均衡