Redis Bigkey 排查

2023年 8月 15日 59.7k 0

文章顶部.png

薛顺.png

在处理 bigkey 问题可以先从一下几点入手

  • 什么是 bigkey?
  • bigkey 危害?
  • bigkey 是如何产生的?
  • 如何发现 bigkey ?
  • 如何处理 bigkey?
  • 什么是 Bigkey

    Redis bigkey 是指在 Redis 数据库中占用空间较大的键值对。这些键通常包含了大量的数据,可能会影响 Redis 的性能和内存使用。例如,在一个集合、哈希表、列表或有序集合中存储了大量元素的键。

    实际生产环境中出现下面两种情况,我们就可以认为它是 bigkey。

  • 字符串类型:它的 big 体现在单个 value 值很大,超过 10KB。如果 key 过大也是不行的。
  • 非字符串类型:哈希、列表、集合、有序集合,元素超过 5000 个。
  • Bigkey 的危害

  • 超时阻塞:由于 Redis 单线程的特性,操作 bigkey 比较耗时。
  • 集群节点失衡:在 Redis 集群中,如果某个节点中存在大量的 bigkey,可能会导致该节点的负载过高,从而导致集群节点失衡,影响整个集群的性能和稳定
  • 备份和恢复困难:当 Redis 需要进行备份和恢复时,bigkey 也会成为一个问题,因为备份和恢复需要占用大量的磁盘空间和网络带宽,如果存在大量的 bigkey,备份和恢复的过程可能会非常耗时和困难。
  • Bigkey 如何产生的

    一般来说,bigkey 的产生都是由于程序设计不当,或者对于数据规模预料不清楚造成的。

    要避免 bigkey 产生,需要合理选择数据结构、拆分大型字符串、压缩数据及定期检查数据库中的键值对大小。

    如何发现 Bigkey

    使用 Redis 的内置命令行

     $ redis-cli --bigkeys
    ​
    # Scanning the entire keyspace to find biggest keys as well as
    # average sizes per key type.  You can use -i 0.01 to sleep 0.01 sec
    # per SCAN command (not usually needed).
    ​
    [00.00%] Biggest string found so far 'key-419' with 3 bytes
    [05.14%] Biggest list   found so far 'mylist' with 100004 items
    [35.77%] Biggest string found so far 'counter:__rand_int__' with 6 bytes
    [73.91%] Biggest hash   found so far 'myobject' with 3 fields
    ​
    -------- summary -------
    ​
    Sampled 506 keys in the keyspace!
    Total key length in bytes is 3452 (avg len 6.82)
    ​
    Biggest string found 'counter:__rand_int__' has 6 bytes
    Biggest   list found 'mylist' has 100004 items
    Biggest   hash found 'myobject' has 3 fields
    ​
    504 strings with 1403 bytes (99.60% of keys, avg size 2.78)
    1 lists with 100004 items (00.20% of keys, avg size 100004.00)
    0 sets with 0 members (00.00% of keys, avg size 0.00)
    1 hashs with 3 fields (00.20% of keys, avg size 3.00)
    0 zsets with 0 members (00.00% of keys, avg size 0.00)
    

    不过需要注意,执行 --bigkeys 时,是通过扫描数据库来查找 bigkey,所以会对 Redis 实例的性能产生影响。

    如果是主从,最好使用从节点执行。

    # redis-cli 会没扫描 100 次暂停 0.1 秒
    ./redis-cli  --bigkeys -i 0.1
    

    使用 redis-cli --bigkey 不足:

  • 这个方法只能返回每种类型中最大的那个 bigkey,无法得到大小排在前 N 位的 bigkey;
  • 对于集合类型来说,只统计集合元素个数的多少,而不是实际占用的内存量。但是,一个集合中的元素个数多,并不一定占用的内存就多。因为,有可能每个元素占用的内存很小,这样的话,即使元素个数有很多,总内存开销也不大
  • 统计 value 内存大小,可以通过 scan 命令迭代,具体步骤如下:

  • 通过 SCAN 命令进行全局扫描。

    #SCAN cursor [MATCH pattern] [COUNT count]
    #cursor - 游标。
    #pattern - 匹配的模式。
    #count - 可选,用于指定每次迭代返回的 key 的数量,默认值为 10 。
    redis 127.0.0.1:6379> scan 0   # 使用 0 作为游标,开始新的迭代
    1) "17"                        # 第一次迭代时返回的游标
    2)  1) "key:12"
        2) "key:8"
        3) "key:4"
        4) "key:14"
        5) "key:16"
        6) "key:17"
        7) "key:15"
        8) "key:10"
        9) "key:3"
       10) "key:7"
       11) "key:1"
    redis 127.0.0.1:6379> scan 17  # 使用的是第一次迭代时返回的游标 17 开始新的迭代
    1) "0"
    2) 1) "key:5"
       2) "key:18"
       3) "key:0"
       4) "key:2"
       5) "key:19"
       6) "key:13"
       7) "key:6"
       8) "key:9"
       9) "key:11"
    
  • 通过 TYPE 命令判断 key 的类型。

    redis> SET weather "sunny"
    OK
    ​
    redis> TYPE weather
    string
    
  • 根据 key 类型,统计 value 大小

  • a. String 类型:STRLEN 就是占用内存大小。

    > STRLEN 22de5ac4e8074ff4bf03d777850de62c
    640
    

    b. 集合类型:如果已知元素大小,乘上元素个数就是占用内存大小。

    # List
    redis 127.0.0.1:6379> LLEN list1
    (integer) 2
    ​
    # Hash
    redis 127.0.0.1:6379> HLEN myhash
    (integer) 2
    ​
    # Set
    redis 127.0.0.1:6379> SCARD myset
    (integer) 2
    ​
    # Sorted Set
    redis 127.0.0.1:6379> ZCARD myzset
    (integer) 2
    

    c. 未知可以通过 memory usage

    memory usage 0188a87272cb4558905b0cfbe64a30d6
    1624
    

    分析 RDB 文件

  • 先执行下面的命令

    set hello redis
    save
    
  • 找到 dump.rdb 文件,并执行下面命令

    od -A x -t x1c -v dump.rdb
    000000  52  45  44  49  53  30  30  30  39  fa  09  72  65  64  69  73
             R   E   D   I   S   0   0   0   9 372  t   r   e   d   i   s
    000010  2d  76  65  72  05  35  2e  30  2e  37  fa  0a  72  65  64  69
             -   v   e   r 005   5   .   0   .   7 372  n   r   e   d   i
    000020  73  2d  62  69  74  73  c0  40  fa  05  63  74  69  6d  65  c2
             s   -   b   i   t   s 300   @ 372 005   c   t   i   m   e 302
    000030  12  ff  54  64  fa  08  75  73  65  64  2d  6d  65  6d  c2  c8
           022 377   T   d 372  b   u   s   e   d   -   m   e   m 302 310
    000040  bb  0d  00  fa  0c  61  6f  66  2d  70  72  65  61  6d  62  6c
           273  r   372  f   a   o   f   -   p   r   e   a   m   b   l
    000050  65  c0  00  fe  00  fb  01  00  00  05  68  65  6c  6c  6f  05
             e 300   376   373 001     005   h   e   l   l   o 005
    000060  72  65  64  69  73  ff  db  4d  64  00  c2  0b  2d  8d
             r   e   d   i   s 377 333   M   d   302  v   - 215
    00006e
    
  • 一个 RDB 主要是有三部分组成

  • 文件头:Redis 魔数,RDB 版本,Redis 版本,RDB 创建时间,键值对占用内存大小等
  • 文件数据:Redis 数据库所有键值对
  • 文件尾:RDB 文件结束标识符,以及文件校验值。这个校验值用来在 Redis Server 加载 RDB 文件是否被篡改过。
  • 这里解读文件的一部分

    image-20230512203931176

    RDB 文件格式主要如下:

    未命名文件 (2)

    type 类型如下
    # 0 =  "String Encoding"
    # 1 =  "List Encoding"
    # 2 =  "Set Encoding"
    # 3 =  "Sorted Set Encoding"
    # 4 =  "Hash Encoding"
    # 9 =  "Zipmap Encoding"
    # 10 = "Ziplist Encoding"
    # 11 = "Intset Encoding"
    # 12 = "Sorted Set in Ziplist Encoding"
    # 13 = "Hashmap in Ziplist Encoding"
    

    这里 type 常量都代表了一种对象类型或底层编码,当服务器读入 RDB 文件中键值对数据,程序会根据 type 的来决定如何读入和解释 value。

    • key 总是一个字符串对象,他的编码和 String Encoding 类型的 value 一样。
    • 根据 type 的不同,以及保存内容的长度不同,保存的 value 的结构和长度也会有所不同。

    如果需要解读其他类型需要我们对 Redis 的对象底层编码结构了解,下面是个简单的关系图。具体可以查看OBJECT ENCODING

    image-20230512205131067

    如果想深入了解 RDB 文件格式可以访问 Redis-RDB-Dump_File_Foramt

    所以比起使用命令直接调用 Redis Server 获取 bigkey。分析 RDB 文件是个不错的选择。

    redis-rdb-tool

    从文档中可以看到 redis-rdb-tool 的主要功能:

  • 生成内存报告;
  • 将 dump.rdb 文件转化为 json 格式;
  • 比较两个 dump 文件等。
  • 将 dump.rdb 文件转化为 json 格式。

    rdb --command json dump.rdb 
    [{"hello":"redis"}]
    

    有了 json 数据之后,我们就可以方法对 Redis 的数据进行统计和监控,也不会对 Redis Server 产生影响。

    具体使用手册可以访问redis-rdb-tool

    如何处理 Bigkey

    当发现 Bigkey 的时候,不应该直接删除。而是通知调用方,让调用方去处理。选择数据结构、拆分大型字符串、压缩数据等。

    当发现 Redis 变慢了,可以通过下面的 checklist 来排查问题:

  • 使用复杂度过高的命令或一次查询全量数据;
  • 操作 bigkey;
  • 大量 key 集中过期;
  • 内存达到 maxmemory;
  • 客户端使用短连接和 Redis 相连;
  • 当 Redis 实例的数据量大时,无论是生成 RDB,还是 AOF 重写,都会导致 fork 耗时严重;
  • AOF 的写回策略为 always,导致每个操作都要同步刷回磁盘;
  • Redis 实例运行机器的内存不足,导致 swap 发生,Redis 需要到 swap 分区读取数据;
  • 进程绑定 CPU 不合理;
  • Redis 实例运行机器上开启了透明内存大页机制;
  • 网卡压力过大。
  • 推荐阅读

    Kubernetes Gateway API

    架构方法论

    redisString结构解析及内存使用优化

    Trino 插件开发入门

    精准测试体系构建

    招贤纳士

    政采云技术团队(Zero),Base 杭州,一个富有激情和技术匠心精神的成长型团队。规模 500 人左右,在日常业务开发之外,还分别在云原生、区块链、人工智能、低代码平台、中间件、大数据、物料体系、工程平台、性能体验、可视化等领域进行技术探索和实践,推动并落地了一系列的内部技术产品,持续探索技术的新边界。此外,团队还纷纷投身社区建设,目前已经是 google flutter、scikit-learn、Apache Dubbo、Apache Rocketmq、Apache Pulsar、CNCF Dapr、Apache DolphinScheduler、alibaba Seata 等众多优秀开源社区的贡献者。

    如果你想改变一直被事折腾,希望开始折腾事;如果你想改变一直被告诫需要多些想法,却无从破局;如果你想改变你有能力去做成那个结果,却不需要你;如果你想改变你想做成的事需要一个团队去支撑,但没你带人的位置;如果你想改变本来悟性不错,但总是有那一层窗户纸的模糊……如果你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的自己。如果你希望参与到随着业务腾飞的过程,亲手推动一个有着深入的业务理解、完善的技术体系、技术创造价值、影响力外溢的技术团队的成长过程,我觉得我们该聊聊。任何时间,等着你写点什么,发给 zcy-tc@cai-inc.com

    微信公众号

    文章同步发布,政采云技术团队公众号,欢迎关注

    文章顶部.png

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论