Redis调优BigKey如何处理?

2024年 4月 16日 101.7k 0

Redis大Key核心问题

Redis库中大数据量如何遍历?

主机配置:

  • redis: 6.2.14
  • 主机内存:8G

图片图片

执行步骤:

  • 生成1000W条记录脚本,插入redis数据库
  • #!/bin/bash  
      
    # Redis服务器地址和端口  
    REDIS_HOST="localhost"  
    REDIS_PORT=6379  
      
    # 输出文件名  
    OUTPUT_FILE="/tmp/redis-bigkey.txt"  
      
    # 要插入的数据条数  
    NUM_ENTRIES=1000000  
      
    # 清除输出文件,如果它已存在  
    > "$OUTPUT_FILE"  
      
    # 生成数据并插入到Redis中,同时输出到文件  
    for ((i=1; i> "$OUTPUT_FILE"  
      
        # 如果需要的话,可以在这里添加检查来确认SET操作是否成功  
        # 比如:redis-cli -h $REDIS_HOST -p $REDIS_PORT GET "$KEY" | grep -q "$VALUE"  
        # 如果上面的命令返回非零状态,可以记录错误或者退出脚本  
      
    done  
      
    echo "数据已插入Redis并输出到$OUTPUT_FILE"
  • 读取命令集,插入redis数据库
  • cat /tmp/redis-bigkey.txt | /usr/local/redis/redis-6.2.14/src/redis-cli -h 192.168.XXX.XXX -p 6379 -a ****** --pipe

    这条命令是会将一个文本文件的内容通过管道(pipe)发送到Redis的命令行接口并执行。

    重要参数说明:

    cat /tmp/redis-bigkey.txt:

    • cat 命令用于读取 /tmp/redis-bigkey.txt 这个文件的内容。

    -a ******:

    • -a 参数用于指定连接Redis服务器所需的密码。
    • ****** 是连接Redis服务器时使用的密码。

    --pipe:

    • --pipe 是一个特殊的选项,它告诉 redis-cli 通过管道从标准输入读取数据,并作为Redis命令发送到服务器。

    注意:这里/tmp/redis-bigkey.txt 文件包含一系列的Redis命令,这些命令将被批量执行。例如,文件中可能包含 SET、GET、DEL 等命令,每行一个命令。使用 --pipe 选项时,需要确保Redis服务器配置允许批量操作。

  • 执行后结果,redis数据库中有1000W数据
  • 127.0.0.1:6379> dbsize
    (integer) 1000000

    尝试用 keys * 遍历,耗时8.55s

    图片图片

      由此可见,生产环境的数据量可能不止这些。可想遍历一次可能的耗时。那么,如何正确遍历呢? 使用SCAN命令。

    SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]

      SCAN 命令是一个基于游标的迭代器,每次被调用之后, 都会向用户返回一个新的游标, 用户在下次迭代时需要使用这个新游标作为 SCAN 命令的游标参数, 以此来延续之前的迭代过程。

    简单演示

    127.0.0.1:6379> scan 2 match * count 10
    1) "720898"
    2)  1) "key772152"
        2) "key318823"
        3) "key851172"
        4) "key137276"
        5) "key658069"
        6) "key486655"
        7) "key795861"
        8) "key300972"
        9) "key488665"
       10) "key479460"
       11) "key15673"

    什么是大Key,多大是大Key?

    注意:Redis中的大key,实际上指的是key所关联的value值特别大,或者是某种数据结构(如hash, set, zset, list)中存储了过多的元素。

    详情可参照《阿里Redis开发规范》

    图片图片

    一般来讲,String类型控制在10KB以内,hash、list、set、zset元素个数不要超过5000。

    为什么会产生BigKey?

    大key的产生一般与业务方设计有关,对vaule的动态增长问题预估不足。造成大key问题的原因有:

    • 数据结构设计不合理。在不适用的场景下使用Redis,易造成Key的value过大,如使用String类型的Key存放大体积二进制文件型数据;
    • 业务规划设计不足。没有对Key中的成员进行合理的拆分将大key变成小key,从而造成个别Key中一直往value里面塞数据,没有删除机制,未定期清理无效数据,导致不断增加。
    • 上线前期预估不足。如头条重大新闻,造成value值动态突增。如:百度热搜

    图片图片

    • 汇总统计类,随着时间推移value逐渐增加

    产生大Key会有什么问题?

    • 内存不足(因为redis基于内存)
    • 删除超时
    • 网络阻塞
    • 集群节点容量倾斜甚至宕机

    因此需引起足够重视。

    如何判定redis变慢了?

  • Redis 基准性能测试
    • 测试基准

      了解Redis 在生产环境服务器上的基准性能,才能进一步评估,当其延迟达到什么程度时,才认为Redis确实变慢了。例如:按自身硬件配置,可能延迟是0.5ms 时就可以认为Redis 变慢了。

    • 测试方法

    执行以下命令,测试出这个实例60 秒内的最大响应延迟:

    ./redis-cli --intrinsic-latency 60
    
    [root@bogon src]# ./redis-cli --intrinsic-latency 60
    Max latency so far: 1 microseconds.
    Max latency so far: 25 microseconds.
    Max latency so far: 220 microseconds.
    Max latency so far: 253 microseconds.
    Max latency so far: 351 microseconds.
    Max latency so far: 448 microseconds.
    Max latency so far: 514 microseconds.
    
    1706810010 total runs (avg latency: 0.0352 microseconds / 35.15 nanoseconds per run).
    Worst run took 14622x longer than the average latency.

    从输出结果可以看到,这60 秒内的最大响应延迟为514 微秒(0.514 毫秒)。

    还可以使用以下命令,查看一段时间内Redis 的最小、最大、平均访问延迟。如下:redis-cli 每隔1秒向 Redis 服务器发送一个 PING 命令,并测量其往返时间.

    Redis-cli -h 127.0.0.1 -p 6379 --latency-history -i 1
    
    [root@bogon src]# redis-cli -h 127.0.0.1 -p 6379 --latency-history -i 1
    min: 0, max: 1, avg: 0.15 (82 samples) -- 1.01 seconds range
    min: 0, max: 1, avg: 0.06 (80 samples) -- 1.00 seconds range
    min: 0, max: 1, avg: 0.12 (82 samples) -- 1.00 seconds range
    min: 0, max: 1, avg: 0.09 (81 samples) -- 1.01 seconds range
    min: 0, max: 1, avg: 0.07 (82 samples) -- 1.00 seconds range
    min: 0, max: 1, avg: 0.07 (82 samples) -- 1.01 seconds range

    根据《阿里开发手册》如果你观察到的Redis 运行时延迟是其基线性能的2倍及以上,就可以认定Redis变慢了。

  • 使用Redis慢日志
  • Redis 提供了慢日志命令的统计功能,它记录了有哪些命令在执行时耗时比较久。

    例如,设置慢日志的阈值为5毫秒,并且保留最近10条慢日志记录:

    # 命令执行耗时超过 5 毫秒,记录慢日志
    CONFIG SET slowlog-log-slower-than 5000
    
    # 只保留最近 10 条慢日志
    CONFIG SET slowlog-max-len 10

      如果你查询慢日志发现,并不是复杂度过高的命令导致的,而都是SET/DEL这种简单命令出现在慢日志中,那么你就要怀疑你的实例否写入了bigkey。

    如何发现BigKey?

    使用命令redis-cli --bigkeys给出每种数据结构最大的bigkey,同时给出每种数据类型的键值个数和平均大小。

    redis-cli --bigkeys
    
    [root@bogon src]# redis-cli --bigkeys
    
    # Scanning the entire keyspace to find biggest keys as well as
    # average sizes per key type.  You can use -i 0.1 to sleep 0.1 sec
    # per 100 SCAN commands (not usually needed).
    
    [00.00%] Biggest string found so far '"key162116"' with 6 bytes
    [75.91%] Biggest string found so far '"key1000000"' with 7 bytes
    [100.00%] Sampled 1000000 keys so far
    
    -------- summary -------
    
    Sampled 1000000 keys in the keyspace!
    Total key length in bytes is 8888896 (avg len 8.89)
    
    Biggest string found '"key1000000"' has 7 bytes
    
    0 lists with 0 items (00.00% of keys, avg size 0.00)
    0 hashs with 0 fields (00.00% of keys, avg size 0.00)
    1000000 strings with 5888896 bytes (100.00% of keys, avg size 5.89)
    0 streams with 0 entries (00.00% of keys, avg size 0.00)
    0 sets with 0 members (00.00% of keys, avg size 0.00)
    0 zsets with 0 members (00.00% of keys, avg size 0.00)

    注意:对线上实例进行bigkey扫描时,Redis 的OPS(Operation Per Second 每秒操作次数)会突增,扫描过程最好控制一下扫描的频率,指定-i 参数,命令:redis-cli -h 127.0.0.1 -p 6379 --bigkeys -i 1.它表示扫描过程中每次扫描后休息的时间间隔,单位是秒。

    但是,如果想要获得一个 key 和它的值在 RAM 中所占用的字节数。需要使用以下命令:

    redis 127.0.0.1:6379> MEMORY USAGE key [SAMPLES count]

    例如:

    127.0.0.1:6379> MEMORY usage key1000000
    (integer) 56

    当我们发现生产的大Key后,那么如何进行删除?

    如何处理大Key?

    我们按照不同数据类型,给出以下命令:

    • String类型: DEL/UNLINK

    删除Redis中String类型的大Key,你可以使用DEL命令:

    DEL key [key ...]

    如果你使用的是Redis的集群模式,可以使用redis-cli的-c选项来启用集群模式,并执行删除命令。

    redis-cli -c DEL key_name

    由于DEL命令会对Redis服务器造成阻塞,可以考虑使用UNLINK命令。Redis 4.0及以上版本中可用,它会异步地删除Key,避免阻塞。

    UNLINK key [key ...]

    注意: 即使使用UNLINK命令,删除非常大的Key仍然可能会对Redis服务器造成一些影响,因为它仍然需要释放内存。因此,在生产环境中执行此类操作时,请务必谨慎,并考虑在低峰时段进行,同时监控Redis的性能指标。

    • Hash类型:HSCAN + HDEL
    HSCAN key cursor [MATCH pattern] [COUNT count]
    
    127.0.0.1:6379> HSET myhash field1 value1
    (integer) 1
    127.0.0.1:6379> HSET myhash field2 value2
    (integer) 1
    127.0.0.1:6379> HSET myhash field3 value3
    (integer) 1
    127.0.0.1:6379> HSCAN myhash 0 MATCH * COUNT 10
    1) "0"
    2) 1) "field1"
       2) "value1"
       3) "field2"
       4) "value2"
       5) "field3"
       6) "value3"
    127.0.0.1:6379> HDEL myhash field2 
    (integer) 1
    127.0.0.1:6379> HGETALL myhash
    1) "field1"
    2) "value1"
    3) "field3"
    4) "value3"
    • List类型:LTRIM渐进式删除
    LTRIM key start stop
    
    redis> RPUSH mylist "one"
    (integer) 1
    redis> RPUSH mylist "two"
    (integer) 2
    redis> RPUSH mylist "three"
    (integer) 3
    redis> LTRIM mylist 1 -1
    "OK"
    redis> LRANGE mylist 0 -1
    1) "two"
    2) "three"
    redis>
    • Set类型:使用sscan每次获取部分元素,再使用srem命令删除每个元素
    127.0.0.1:6379> SADD myset e1 e2 e3 
    (integer) 0
    127.0.0.1:6379> SSCAN myset 1
    1) "0"
    2) 1) "e3"
    127.0.0.1:6379> SMEMBERS myset
    1) "e2"
    2) "e1"
    3) "e3"
    127.0.0.1:6379> SREM myset e2
    (integer) 1
    127.0.0.1:6379> SMEMBERS myset
    1) "e1"
    2) "e3"
    127.0.0.1:6379>
    • Zset类型: 使用zscan每次获取部分元素,再使用ZREM命令删除每个元素
    127.0.0.1:6379> zadd score 98 xm 99 xb 100 xh
    (integer) 3
    127.0.0.1:6379> zscan score 0
    1) "0"
    2) 1) "xm"
       2) "98"
       3) "xb"
       4) "99"
       5) "xh"
       6) "100"
    127.0.0.1:6379> ZRANGE score 0 -1 WITHSCORES
    1) "xm"
    2) "98"
    3) "xb"
    4) "99"
    5) "xh"
    6) "100"
    127.0.0.1:6379> ZREM score xm
    (integer) 1
    127.0.0.1:6379> ZRANGE score 0 -1 WITHSCORES
    1) "xb"
    2) "99"
    3) "xh"
    4) "100"
    127.0.0.1:6379>

    生产BigKey如何调优?

      采用惰性删除策略。具体在${redis_home}/redis.conf 文件配置修改

    lazyfree-lazy-server-del yes
    replica-lazy-flush yes
    lazyfree-lazy-user-del yes

    总结

    Redis中的大Key指的是占用内存特别大的Key,处理不当可能导致性能下降、内存消耗大等问题。

    解决方案:

    • 避免创建大Key:设计数据结构时,尽量分散数据,避免单一Key过大。
    • 分批次处理:对于已存在的大Key,使用相关命令(如SCAN)分批次读取和删除。
    • 设置过期时间:为大Key设置TTL,让Redis自动清理。
    • 监控与告警:使用监控工具及时发现大Key,并设置告警通知。
    • 优化网络:如果删除大Key时网络压力大,考虑增加带宽或优化网络连接。

    注意事项:

    • 处理大Key时要谨慎,最好在低峰时段操作。

    相关文章

    Oracle如何使用授予和撤销权限的语法和示例
    Awesome Project: 探索 MatrixOrigin 云原生分布式数据库
    下载丨66页PDF,云和恩墨技术通讯(2024年7月刊)
    社区版oceanbase安装
    Oracle 导出CSV工具-sqluldr2
    ETL数据集成丨快速将MySQL数据迁移至Doris数据库

    发布评论