美文网首页redis浅析
关于Redis大key问题

关于Redis大key问题

作者: 超鸽带你飞 | 来源:发表于2021-03-18 17:32 被阅读0次

什么是大key?

我们从[空间复杂性]和访问它的[时间复杂度]两个方面来定义大键。
前者主要表示Redis键的占用内存大小;
后者表示Redis集合数据类型(set/hash/list/sorted set)键,所含有的元素个数。举例:

1个大小200MB的String键(String Object最大512MB);内存空间角度占用较大
1个包含100000000(1kw)个字段的Hash键,对应访问模式(如hgetall)时间复杂度高。

内存空间复杂性处理耗时都非常小,测试 del 200MB String键耗时约1毫秒,
而删除一个含有1kw个字段的Hash键,却会阻塞Redis进程数十秒。

大Key会带来哪些问题?

  • 当value过大时,可能会导致以下问题:
    • 内存使用不均匀:当slot分配均匀的时候,大key的出现会导致redis内存使用的不均。
    • 网络拥塞:每次获取bigkey产生的网络流量比较大。涉及到大key的操作,尤其是使用hgetall、lrange 0 -1、get、hmget 等操作时,网卡可能会成为瓶颈,也会到导致堵塞其它操作,qps 就有可能出现突降或者突升的情况,趋势上看起来十分不平滑,严重时会导致应用程序连不上,实例或者集群在某些时间段内不可用的状态。假设一个bigkey为1MB,每秒访问量为1000,那么每秒产生1000MB的流量。
  • 当元素个数过多时,会导致以下问题:
    (元素个数过多针对的是key为集合类型set,sorted set,list和hash)
    • 超时阻塞:Redis是单线程处理。单个耗时过大命令,导致阻塞其他命令,容易引起应用程序雪崩或Redis集群发生故障切换。所以避免在生产环境中使用耗时过大命令。

优雅的删除大key

Redis是单线程处理。
单个耗时过大命令,导致阻塞其他命令,容易引起应用程序雪崩或Redis集群发生故障切换。所以避免在生产环境中使用耗时过大命令。

DEL命令在删除单个集合类型的Key时,命令的时间复杂度是O(M),其中M是集合类型Key包含的元素个数。

Redis删除大的集合键的耗时, 测试估算,


image.png
  • 脚本+删除命令:
    hash: 使用 hscan + hdel
    set : 使用 sscan + srem
    zset : 使用 zremrangebyrank
    list : 使用 scan + ltrim
  • lazy delete free:
    3.4版本开始,Redis会支持lazy delete free的方式,删除大键的过程不会阻塞正常请求。

scan 命令

SCAN 命令:用于迭代当前数据库中的数据库键。跟keys命令返回全部的数据库键不同,它每次执行都只会返回少量元素,所以可以用于生产环境。
SCAN 命令是一个基于游标的迭代器(cursor based iterator): SCAN 命令每次被调用之后, 都会向用户返回一个新的游标, 用户在下次迭代时需要使用这个新游标作为 SCAN 命令的游标参数, 以此来延续之前的迭代过程。

使用示例:

172.30.127.3:9200> scan 0
1) "851968"
2)  1) "rand:41834283"
    2) "rand:23627572"
    3) "rand:53205379"
    4) "LESSON_STUDENT_CNTS_222500"
    5) "rand:72655133"
    6) "ltwx1:wxserver:fans:info:wx85dd92cd24d7e29e:o_aiL0dqLx92iwu48nqjWp7xOhFg"
    7) "rand:39536088"
    8) "rand:89380089"
    9) "rand:25366977"
   10) "V1_SJ_EXAM_DB_RELATION_BINDID_206773_BINDTYPE_0_RELATIONTYPE_9"
   11) "rand:27227021"
172.30.127.3:9200> scan 851968

第一次使用 scan 0 返回一个列表,列表分为两部分1) 下一次的游标,2) 列表。
第二次使用第一次返回的游标继续请求,知道返回的游标是0表示结束遍历了。

删除bigkey
使用del命令删除bigkey通常来说会阻塞Redis服务器,在生产环境中要尽量避免。
当bigkey对应的value越来越大,删除的时间也会随着增加。除了string类型删除时一般不产生阻塞,其他四种数据结构的删除都有可能产生阻塞。
删除bigkey要使用渐进式遍历的方式,利用sscan、hscan、zscan命令将若干个键拿出来。

Redis 4.0支持lazy delete free模式,删除bigkey不会阻塞Redis。

实际中如何避免bigkey:

尽量把大key进行拆分
尽量避免使用hgetall 这样的命令

如何统计大key?

发现bigkey的两种方式:

redis sdk中增加大key使用的监控报警:在框架的Redis sdk中修改,当set了大key时,通过打印日志,对接监控报警系统。
脚本定时扫描:在流量低峰时,通过定时任务执行。使用scan命令渐进的扫描出所有的key,分别计算每个key的serializedlength,找到对应的bigkey进行相应的处理和报警,这种方式比较推荐。在scan + debug object的命令,如果怀疑存在bigkey,可以

其他一些查看大key的方法:

在redis实例上执行bgsave,然后我们对dump出来的rdb文件进行分析,找到其中的大KEY
redis-cli 原生自带 –bigkeys 功能,统计bigkey的分布。

➜  goApp redis-cli -h 172.30.127.3 -p 9200 -a Zyb-Test --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 'rand:41834283' with 8 bytes
[00.00%] Biggest hash   found so far 'LESSON_STUDENT_CNTS_222500' with 33 fields
[00.00%] Biggest string found so far 'docker_nuke_session:78379868-7a34-4c24-9f17-58b4a5a4cdc1' with 25 bytes
[00.00%] Biggest string found so far 'FUDAO_ELIVE_USER_EXT_DATA_227543_2000024247' with 35 bytes
[00.01%] Biggest string found so far 'cwTask7480' with 51 bytes
[00.01%] Biggest string found so far 'ACLS2_COURSE_INFO_201940' with 1899 bytes
[00.03%] Biggest zset   found so far 'TARGET_CALC_RANKING_L_236412' with 5 members
[00.03%] Biggest zset   found so far 'TEACHER_MESSAGE_2000076523' with 19 members
[00.06%] Biggest string found so far 'teacher_kv_id_2298403389' with 3284 bytes
[00.09%] Biggest string found so far 'billing_course_kv_228486' with 8876 bytes
[00.33%] Biggest string found so far 'ACLS2_QUESTION_INFO_240405_0_9' with 9323 bytes

可见redis在计算大key的时候不是以key的占用内存大小来计算的,string是使用STRLEN方法来计算,list是llen,hash是使用HLEN来计算。

相关文章

网友评论

    本文标题:关于Redis大key问题

    本文链接:https://www.haomeiwen.com/subject/ukrpcltx.html