Redis大key处理

目录

1.什么是大key

实际案例中的建议

[大 Key 处理流程图](#大 Key 处理流程图)

[1. 预防措施(设计阶段)](#1. 预防措施(设计阶段))

数据结构优化

[2. 识别大 Key](#2. 识别大 Key)

使用工具检测

[使用 SCAN 命令](#使用 SCAN 命令)

[3. 处理现有大 Key](#3. 处理现有大 Key)

渐进式删除

[异步删除(Redis 4.0+)](#异步删除(Redis 4.0+))

[4. 配置优化](#4. 配置优化)

[5. 架构层面优化](#5. 架构层面优化)

使用集群模式

6.总结建议


Redis 处理大 Key 的主要方法可以分为预防、识别、处理和优化几个方面。下面我将详细说明每一步。

1.什么是大key

没有一个固定的数值可以定义所有场景下的大 Key。通常,我们可以将 String 类型超过 10 KB,集合类型元素超过 5000 作为大 Key 的参考阈值。但更重要的是,需要根据实际的业务场景、Redis 实例的配置和性能要求来定义适合自己的大 Key 阈值。

在不确定的情况下,建议进行性能测试,观察不同大小的 Key 对 Redis 操作延迟的影响,从而确定适合自己业务的大 Key 阈值。

实际案例中的建议

  • 阿里巴巴 Redis 开发规范:建议字符串类型控制在 10 KB 以内,集合元素数量不要超过 5000。

  • 腾讯云 Redis:建议字符串类型不超过 10 KB,集合元素数量不超过 4000。

大 Key 处理流程图

text

复制代码
发现大 Key → 评估影响 → 选择策略
    ↓
预防为主 → 无法避免 → 渐进处理
    ↓
监控告警 → 异步删除 → 架构优化
  1. 预防大 Key

    • 在设计阶段,避免存储过大的数据。例如,对于存储集合类型的数据,可以拆分多个键来存储。

    • 使用合适的数据结构。例如,使用 HyperLogLog 代替集合进行基数统计,使用位图(bitmap)存储布尔值等。

    • 对数据进行压缩后再存储(但需要注意,压缩和解压会增加 CPU 开销,而且 Redis 本身不支持压缩,需要客户端处理)。

  2. 识别大 Key

    • 使用 Redis 自带的命令:例如 debug object key 可以查看某个 key 的序列化长度,但注意这个命令会影响性能,不建议在线使用。

    • 使用 redis-cli --bigkeys 命令可以扫描整个数据库,并统计出大 key。这个命令是通过扫描所有 key,然后统计它们的大小,注意这个过程会阻塞 Redis,最好在从节点上执行或者低峰期执行。

    • 使用 SCAN 命令编写脚本,逐步扫描所有 key,然后使用 strlenhlenllenscardzcard 等命令获取 key 的大小。这样不会阻塞 Redis,但需要自己编写脚本。

  3. 处理大 Key

    • 拆分大 Key:将一个大 key 拆分成多个小 key。例如,一个存储了百万个元素的哈希表,可以拆分成多个哈希表,每个哈希表存储一部分数据。然后通过分片的方式(例如使用固定的哈希策略)来访问。

    • 删除大 Key:如果大 Key 不再需要,删除它。但是直接删除一个大的 key 可能会阻塞 Redis(因为 Redis 是单线程的)。所以建议使用渐进式删除:

      • 对于哈希、列表、集合、有序集合,可以使用对应的命令进行渐进式删除。例如,对于哈希,可以使用 hscan 每次获取一部分字段,然后使用 hdel 删除。

      • 对于字符串,直接使用 del 命令,但如果是非常大的字符串,也会阻塞一段时间。可以考虑使用 unlink 命令(Redis 4.0 以上),它会在后台异步删除。

    • 对于集合类型,可以使用对应的扫描命令(如 sscanhscanzscan)来逐步删除。

  4. 优化大 Key 的使用

    • 对于读取,可以考虑使用分片的方式,将请求分散到多个 key 上,减少单个 key 的压力。

    • 对于写入,同样使用分片,避免对单个 key 频繁写入导致性能问题。

    • 考虑使用 Redis 集群,将数据分布到多个节点上,这样每个节点上的 key 就不会太大。

  5. 使用 Redis 4.0 的新特性

    • Redis 4.0 引入了异步删除(unlink 命令),可以用于删除大 key 而不阻塞主线程。

    • Redis 6.0 引入了多线程,可以在删除大 key 时使用后台线程处理,减少阻塞。

  6. 监控和告警

    • 定期监控 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

bash

复制代码
# 原始大 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
  • 使用合适的数据结构

    • HyperLogLog 替代大集合进行基数统计

    • Bitmaps 替代布尔值数组

    • 考虑使用其他存储(如关系数据库)存储大对象

2. 识别大 Key

使用工具检测

复制代码
# Redis 自带命令
redis-cli --bigkeys

# 扫描模式
redis-cli --bigkeys -i 0.1  # 每100毫秒扫描一次,减少对业务影响

# RDB 分析工具
redis-rdb-tools  # 分析 RDB 文件找出大 Key

使用 SCAN 命令

使用 SCAN 命令编写脚本,逐步扫描所有 key,然后使用 strlenhlenllenscardzcard 等命令获取 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.总结建议

  1. 设计阶段避免大 Key:合理拆分数据

  2. 定期扫描检测:建立监控体系

  3. 使用渐进式操作:避免阻塞 Redis

  4. 升级到 Redis 4.0+:利用异步删除特性

  5. 考虑数据迁移:将大对象存储到其他存储系统

  6. 容量规划:提前预估数据增长

记住:预防胜于治疗,良好的数据模型设计是避免大 Key 问题的根本。

相关推荐
C_心欲无痕1 小时前
浏览器缓存: IndexDB
前端·数据库·缓存·oracle
lkbhua莱克瓦241 小时前
进阶-索引3-性能分析
开发语言·数据库·笔记·mysql·索引·性能分析
剑来.2 小时前
事务没提交,数据库为什么会越来越慢?
数据库·oracle
韦东东3 小时前
DeepSeek:R1本地RAG 问答: 功能新增,附 六大关键技术优化路径参考
数据库·mysql
Leon-Ning Liu3 小时前
19c RAC 环境 Patch 38326922 应用实战
数据库·oracle
虫小宝3 小时前
优惠券省钱app高并发秒杀系统:基于Redis与消息队列的架构设计
数据库·redis·缓存
赵渝强老师4 小时前
【赵渝强老师】MySQL的数据约束
数据库·mysql
半部论语4 小时前
MySQL 主机被封问题详解:原因、解除方法与预防策略
数据库·mysql
少许极端4 小时前
Redis入门指南(五):从零到分布式缓存-其他类型及Java客户端操作redis
java·redis·分布式·缓存
工具罗某人5 小时前
docker快速部署redis
redis·docker·容器