当前位置: 首页 > 后端技术 > Java

Redis中的BigKey问题:排查及解决思路

时间:2023-04-02 00:52:03 Java

本文已收录到Github,推荐阅读WemayencountertheBigKeyproblem。这个问题是Redis中某个key的值太大,所以BigKey问题本质上是BigValue问题,导致Redis的性能下降或者崩溃。本文将向您展示如何排查和解决此问题。BigKey问题介绍在Redis中,每个key都有对应的值。如果某个key的值过大,会导致Redis的性能下降或者崩溃,这比玄学还玄学,因为Redis需要加载所有的大key。对于内存来说,这样会占用大量的内存空间,降低Redis的响应速度。这个问题被称为大密钥问题。别小看这个问题,它能让你的Redis瞬间变成“乌龟”。由于Redis的单线程特性,通常操作BigKey比较耗时,这意味着阻塞Redis的可能性更大,这将导致Clients阻塞或导致故障转移,潜在地导致“慢查询”。一般来说,以下两种情况称为大键:String类型的键对应的值超过10MB。List、set、hash、zset等集合类型,集合元素个数超过5000。以上判断BigKey的标准不是唯一的,而是一个通用的。在实际业务开发中,BigKey的判断需要根据具体的使用场景进行不同的判断。例如,如果对某个键的操作导致请求响应时间变慢,则可以确定该键为BigKey。在Redis中,大键通常是由以下原因造成的:序列化对象的大小过大存储大量数据的容器,如set、list等大型数据结构,如bitmap、hyperloglog等。这些大数据结构如果不及时key处理,会逐渐消耗Redis服务器的内存资源,最终导致Redis崩溃。BigKey故障排查当Redis性能急剧下降时,很可能是因为存在一个bigkey。在排查大键问题时,可以考虑以下方法:使用Redis自带的BIGKEYS命令查询当前Redis中所有键的信息,统计分析整个数据库中键值对的大小,例如,统计键值对的数量和每种数据类型的平均大小。另外,这条命令执行后,会输出每种数据类型中最大bigkey的信息。对于String类型,会输出最大bigkey的字节长度。对于collection类型,会输出bigkey的最大元素个数BIGKEYS命令会扫描整个数据库,命令本身会阻塞Redis,找出所有的bigkey,并以列表的形式返回给客户端。命令格式如下:$redis-cli--bigkeys返回示例如下:#扫描整个键空间以找到最大的键以及#每个键类型的平均大小。您可以使用-i0.1每100个SCAN命令休眠0.1秒#(通常不需要)。[00.00%]迄今为止找到的最大字符串“a”有3个字节[05.14%]迄今为止找到的最大列表“b”有100004items[35.77%]迄今为止找到的最大字符串'c'有6个字节[73.91%]迄今为止找到的最大哈希'd'有3个字段--------摘要--------采样506个键在密钥空间中!以字节为单位的总密钥长度为3452(平均len6.82)找到的最大字符串“c”有6个字节找到的最大列表“b”有100004个项目找到的最大哈希“d”有3个字段504个字符串,有1403个字节(99.60%的键,平均大小2.78)1包含100004个项目的列表(00.20%的键,平均大小100004.00)0个包含0个成员的集合(00.00%的键,平均大小0.00)1个包含3个字段的哈希(00.20%的键,平均大小3.00)0zsetswith0members(00.00%ofkeys,avgsize0.00)需要注意的是,由于BIGKEYS命令需要扫描整个数据库,所以它可能会对Redis实例造成一定的负担在执行该命令之前,请确保您的Redis实例有足够的资源来处理。建议在slave节点上执行。DebugObject如果找到BigKey,我们需要进一步分析它。我们可以使用命令debugobjectkey查看某个键的详细信息,包括该键的值。这时候你可以在Redis内部“窥探”一下,看看是哪个key太大了。DebugObject命令是调试命令,当key存在时,返回相关信息。当键不存在时返回错误。redis127.0.0.1:6379>DEBUGOBJECTkeyValueat:0xb6838d20refcount:1encoding:rawserializedlength:9lru:283790lru_seconds_idle:150redis127.0.0.1:6379>DEBUGOBJECTkey(errorskey)valueuchserkey序列对应ngserkeyno的个数bytes转换后的内存占用在Redis4.0之前,key(字段serializedlength)的内存占用只能通过DEBUGOBJECT命令来估算,但是DEBUGOBJECT命令是有错误的。4.0及以上版本,我们可以使用memoryusag命令。内存使用命令使用非常简单,只需按下内存使用键的名称即可;如果当前key存在,则返回key的值实际占用内存的预估值;如果密钥不存在,它将返回nil。127.0.0.1:6379>setk1value1OK127.0.0.1:6379>memoryusagek1//这里k1值占用57字节内存(整数)57127.0.0.1:6379>memoryusageaaa//aaa键不存在,返回nil.(nil)对于String类型以外的类型,内存使用命令采用采样方式。默认采样5个元素,所以计算的是一个近似值。我们还可以指定样本数。示例说明:生成100w个字段的哈希键:hkey,每个字段的值长度为1到1024字节的随机值。127.0.0.1:6379>hlenhkey//hkey有100w个字段,每个字段的取值长度在1~1024字节之间(整数)1000000127.0.0.1:6379>MEMORYusagehkey//默认SAMPLES为5,解析hkeykey内存占用521588753字节(整数)521588753127.0.0.1:6379>MEMORYusagehkeySAMPLES1000//指定SAMPLES为1000,分析hkeykey内存占用617977753字节(整数)617977753127.0.0.1:6379MEMORY/SAMPLESAMPLES1:6379/指定SAMPLES为10000,分析hkeykey内存占用624950853bytes(integer)624950853为获得更准确的key内存值,指定更大的sample数。但是样本数越大,占用的CPU时间片也越大。redis-rdb-toolsredis-rdb-tools是一个用于解析rdb文件的python工具。在分析内存的时候,我们主要用它来生成内存快照。您可以从rdb快照文件生成CSV或JSON文件,或将它们导入MySQL以生成报告以供分析。使用PYPI安装pipinstallrdbtools生成内存快照rdb-cmemorydump.rdb>memory.csv生成的CSV文件中有如下几列:Redis中的databasekeydbtypekeytypekeyvaluesize_in_byteskeymemorysizeencodingvalue要在num_elements键中存储值的个数,在len_largest_element键中存储值的长度,可以在MySQL中新建一张表并导入进行分析,然后直接通过SQL语句进行查询分析。CREATETABLE`memory`(`database`int(128)DEFAULTNULL,`type`varchar(128)DEFAULTNULL,`KEY`varchar(128),`size_in_bytes`bigint(20)DEFAULTNULL,`encoding`varchar(128)DEFAULTNULL,`num_elements`bigint(20)DEFAULTNULL,`len_largest_element`varchar(128)DEFAULTNULL,PRIMARYKEY(`KEY`));例子:查询内存占用最高的3个keymysql>SELECT*FROMmemoryORDERBYsize_in_bytesDESCLIMIT3;+---------+-----+-----+-------------+----------+------------+--------------------+|数据库|类型|钥匙|字节大小|编码|元素数|len_largest_element|+---------+-----+-----+-------------+----------+----------------+--------------------+|0|设置|k1|624550|哈希表|50000|10||0|设置|k2|420191|哈希表|46000|10||0|设置|k3|325465|哈希表|38000|10|+----------+-----+-----+----------------+-----------+------------+--------------------+3rowsinset(0.12sec)BigKey问题解决方案当我们发现有一个大的关键问题时,我们需要及时采取措施解决这个问题。下面列出了几种可能的解决方案:拆分大密钥,将大密钥拆分成多个小密钥。这种方法比较简单,但需要修改应用代码。就像把一个大蛋糕切成小蛋糕一样,有点费力,但很管用。或者尝试将BigKey转换为Redis数据结构。例如,将BigKey转换成Hash、List或Set等数据结构。对象压缩如果主要是因为序列化后的对象体积较大导致key变大,我们可以考虑使用压缩算法来减小对象的体积。Redis本身支持多种压缩算法,如LZF、Snappy等。直接删除如果你使用的是Redis4.0+版本,可以直接使用unlink命令异步删除。4.0以下的版本可以考虑使用scan来批量删除。无论使用哪种方法,都需要注意以下几点:避免使用过大的值。如果需要存储大量数据,可以将其拆分成多个小值。就像吃饭一样,一次吃一口,不要太贪嚼。避免不必要的数据结构。例如,如果你只需要存储一个字符串,就不要使用Hash或List等数据结构。定期清理过期密钥。如果Redis中有大量的过期键,Redis的性能会下降。就像家里的垃圾一样,需要定期清理。对象压缩总结BigKey问题是Redis中常见的问题之一,但是通过合理的排查和解决方案,我们可以有效避免这个问题。在使用Redis时需要注意避免使用过大的值和不必要的数据结构,定期清理过期键。本文就到此为止,感谢大家的阅读,如果本博客有什么错误或者建议,欢迎大家留言指正。文章持续更新中,大家可以关注公众号第一时间阅读。