Redis 处理大 Key 的主要方法可以分为预防、识别、处理和优化几个方面。下面我将详细说明每一步。
1.什么是大key
没有一个固定的数值可以定义所有场景下的大 Key。通常,我们可以将 String 类型超过 10 KB,集合类型元素超过 5000 作为大 Key 的参考阈值。但更重要的是,需要根据实际的业务场景、Redis 实例的配置和性能要求来定义适合自己的大 Key 阈值。
在不确定的情况下,建议进行性能测试,观察不同大小的 Key 对 Redis 操作延迟的影响,从而确定适合自己业务的大 Key 阈值。
实际案例中的建议
阿里巴巴 Redis 开发规范:建议字符串类型控制在 10 KB 以内,集合元素数量不要超过 5000。腾讯云 Redis:建议字符串类型不超过 10 KB,集合元素数量不超过 4000。大 Key 处理流程图
发现大 Key → 评估影响 → 选择策略
↓
预防为主 → 无法避免 → 渐进处理
↓
监控告警 → 异步删除 → 架构优化
预防大 Key
在设计阶段,避免存储过大的数据。例如,对于存储集合类型的数据,可以拆分多个键来存储。使用合适的数据结构。例如,使用 HyperLogLog 代替集合进行基数统计,使用位图(bitmap)存储布尔值等。对数据进行压缩后再存储(但需要注意,压缩和解压会增加 CPU 开销,而且 Redis 本身不支持压缩,需要客户端处理)。识别大 Key
使用 Redis 自带的命令:例如debug object key 可以查看某个 key 的序列化长度,但注意这个命令会影响性能,不建议在线使用。使用 redis-cli --bigkeys 命令可以扫描整个数据库,并统计出大 key。这个命令是通过扫描所有 key,然后统计它们的大小,注意这个过程会阻塞 Redis,最好在从节点上执行或者低峰期执行。使用 SCAN 命令编写脚本,逐步扫描所有 key,然后使用 strlen、hlen、llen、scard、zcard 等命令获取 key 的大小。这样不会阻塞 Redis,但需要自己编写脚本。
处理大 Key
拆分大 Key:将一个大 key 拆分成多个小 key。例如,一个存储了百万个元素的哈希表,可以拆分成多个哈希表,每个哈希表存储一部分数据。然后通过分片的方式(例如使用固定的哈希策略)来访问。删除大 Key:如果大 Key 不再需要,删除它。但是直接删除一个大的 key 可能会阻塞 Redis(因为 Redis 是单线程的)。所以建议使用渐进式删除:对于哈希、列表、集合、有序集合,可以使用对应的命令进行渐进式删除。例如,对于哈希,可以使用hscan 每次获取一部分字段,然后使用 hdel 删除。对于字符串,直接使用 del 命令,但如果是非常大的字符串,也会阻塞一段时间。可以考虑使用 unlink 命令(Redis 4.0 以上),它会在后台异步删除。对于集合类型,可以使用对应的扫描命令(如 sscan、hscan、zscan)来逐步删除。
优化大 Key 的使用
对于读取,可以考虑使用分片的方式,将请求分散到多个 key 上,减少单个 key 的压力。对于写入,同样使用分片,避免对单个 key 频繁写入导致性能问题。考虑使用 Redis 集群,将数据分布到多个节点上,这样每个节点上的 key 就不会太大。使用 Redis 4.0 的新特性
Redis 4.0 引入了异步删除(unlink 命令),可以用于删除大 key 而不阻塞主线程。Redis 6.0 引入了多线程,可以在删除大 key 时使用后台线程处理,减少阻塞。监控和告警
定期监控 Redis 的内存使用情况,以及大 key 的情况。可以使用INFO memory 命令查看内存使用情况。设置告警,当发现大 key 时及时处理。
下面是一个使用 SCAN 和 hscan 删除大哈希表的示例脚本(假设我们要删除一个名为 big_hash 的 key,它包含大量字段):每次扫描100个字段,然后删除它们(渐进式删除)
# 使用 hscan 每次扫描 100 个字段,然后删除它们 cursor=0 while true; do # 使用 hscan 命令,每次获取 100 个字段 redis-cli hscan big_hash $cursor count 100 > /tmp/hscan_output.txt # 读取输出,第一行是下一个游标,第二行是字段列表 cursor=$(head -1 /tmp/hscan_output.txt) fields=$(tail -1 /tmp/hscan_output.txt) # 将字段转换为数组,然后逐个删除 # 注意:这里假设字段之间用空格分隔,但实际上 hscan 输出的字段是每行一个,所以需要根据实际情况调整 # 以下是一个简单的处理方式,但可能不适用于所有情况 for field in $fields; do redis-cli hdel big_hash $field done # 如果游标为0,表示扫描结束 if [ $cursor -eq 0 ]; then break fi done
注意:上面的脚本是一个简单的示例,实际使用时需要根据实际情况调整,比如处理 hscan 输出的格式,以及考虑网络连接和错误处理。
另外,也可以使用编程语言(如 Python、Java)来编写更复杂的脚本,利用相应 Redis 客户端的 SCAN 功能。
总结:处理大 Key 的核心思想是预防为主,及时发现并处理。在处理时,尽量避免阻塞 Redis 主线程,采用渐进式删除或异步删除。同时,通过拆分、分片等方式优化数据结构和访问模式。
Redis 处理大 Key 主要有以下几种方式:
1.预防措施(设计阶段)
数据结构优化
拆分大 Key:将一个大的哈希/集合/列表拆分成多个小的 Key
# 原始大 Key hset user:1_data field1 value1 ... field10000 value10000 # 拆分成多个 Key hset user:1:part1 field1 value1 ... field1000 value1000 hset user:1:part2 field1001 value1001 ... field2000 value2000 ———————————————— 版权声明:本文为CSDN博主「困知勉行1985」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/wdquan19851029/article/details/156572934
使用合适的数据结构
HyperLogLog 替代大集合进行基数统计Bitmaps 替代布尔值数组考虑使用其他存储(如关系数据库)存储大对象2.识别大 Key
使用工具检测
# Redis 自带命令 redis-cli --bigkeys # 扫描模式 redis-cli --bigkeys -i 0.1 # 每100毫秒扫描一次,减少对业务影响 # RDB 分析工具 redis-rdb-tools # 分析 RDB 文件找出大 Key
使用 SCAN 命令
使用 SCAN 命令编写脚本,逐步扫描所有 key,然后使用 strlen、hlen、llen、scard、zcard 等命令获取 key 的大小。这样不会阻塞 Redis,但需要自己编写脚本。
# 编写脚本定期扫描 EVAL " local cursor = 0 repeat local result = redis.call('SCAN', cursor, 'COUNT', 100) cursor = tonumber(result[1]) local keys = result[2] for i, key in ipairs(keys) do local type = redis.call('TYPE', key) local size = 0 if type == 'string' then size = redis.call('STRLEN', key) elseif type == 'hash' then size = redis.call('HLEN', key) elseif type == 'list' then size = redis.call('LLEN', key) elseif type == 'set' then size = redis.call('SCARD', key) elseif type == 'zset' then size = redis.call('ZCARD', key) end if size > 10000 then -- 设置阈值 print(key, type, size) end end until cursor == 0 " 0
3.处理现有大 Key
渐进式删除
# 对于大 Hash - 使用 hscan + hdel cursor=0 while true; do result=$(redis-cli HSCAN big_hash $cursor COUNT 100) cursor=$(echo "$result" | head -1) fields=$(echo "$result" | tail -n +2 | awk '{print $1}') for field in $fields; do redis-cli HDEL big_hash $field done if [[ $cursor -eq "0" ]]; then break fi done # 对于大 Set - 使用 sscan + srem # 对于大 ZSet - 使用 zscan + zrem # 对于大 List - 分批次 ltrim redis-cli LTRIM big_list 0 99 # 保留前100个元素
异步删除(Redis 4.0+)
# 使用 UNLINK 替代 DEL(异步删除) redis-cli UNLINK big_key # 配置异步删除参数(redis.conf) lazyfree-lazy-eviction yes lazyfree-lazy-expire yes lazyfree-lazy-server-del yes replica-lazy-flush yes
4.配置优化
# 监控告警 # 设置内存使用阈值 maxmemory 4gb maxmemory-policy allkeys-lru # 客户端缓冲区限制 client-output-buffer-limit normal 0 0 0 client-output-buffer-limit replica 256mb 64mb 60 client-output-buffer-limit pubsub 32mb 8mb 60
5.架构层面优化
使用集群模式
# 将数据分散到不同节点 # 大 Key 会自动分布到不同实例 redis-cli --cluster create node1:6379 node2:6379 ...
6.总结建议
设计阶段避免大 Key:合理拆分数据定期扫描检测:建立监控体系使用渐进式操作:避免阻塞 Redis升级到 Redis 4.0+:利用异步删除特性考虑数据迁移:将大对象存储到其他存储系统容量规划:提前预估数据增长记住:预防胜于治疗,良好的数据模型设计是避免大 Key 问题的根本。
到此这篇关于Redis中大key处理的问题解决的文章就介绍到这了,
