一、背景在Redis运维过程中,Bigkey的存在会影响业务程序的响应速度,严重时还会造成数据丢失可用性。DBA们一直都在和业务开发人员强调Bigkey的规避方式和危害,但是Bigkey并没有被完全规避。全网Redis集群2200多个,实例数达到45000多个。现阶段对全网进行一次Bigkey校验,估计要以年为单位,非常耗时。我们需要新的思路来解决Bigkey问题。2、Bigkey简介2.1.什么是大键?在Redis中,一个字符串类型最大可达512MB,一个二级数??据结构(如hash、list、set、zset等)可以存储大约40亿(2^32-1)个元素,但实际上它会达不到这么大的价值。一般达到以下情况,就可以认为是Bigkey了。【字符串类型】:单个超过1MB的字符串类型值可以认为是一个Bigkey。【非字符串类型】:Hash、list、set、orderedset等,如果元素个数超过2000,可以认为是Bigkey。2.2Bigkey是如何产生的我们遇到的Bigkey一般是由于程序设计不当或者数据规模预测不明确造成的,比如以下几种情况。【统计】:遇到一个统计关键,就是记录某个网站的访问用户的IP。随着时间的推移,随着访问网站的用户越来越多,这个key的元素数量也会越来越多,形成Bigkey。[Cache]:缓存类key,一般遵循从数据库中查询数据,序列化到Redis中的逻辑。如果业务程序没有从Redis访问它,它会查询数据库并将查询到的数据追加到Redis缓存中,大量的数据会在短时间内缓存在Rediskey中,形成Bigkey。【队列】:使用Redis作为队列处理任务。如果消费不及时,队列会越来越大,形成Bigkey。这三种情况我们在实际运维中都会遇到,需要谨慎使用,合理优化。2.3Bigkey危害当我们在运维中遇到Bigkey时,会导致一些问题,触发监控告警,严重影响Redis实例的可用性,进而影响业务可用性。当需要横向扩展时,可能会导致横向扩展失败。2.3.1内存空间不均匀内存空间不均匀不利于集群统一管理内存,存在数据丢失的风险。下图中的三个节点属于同一个集群,它们的key个数差不多,但内存容量相差较大。带Bigkey的实例占用内存4G多。可以通过Daas平台“工具集-操作项管理”选择对应的slave实例进行分析,找出具体的Bigkey。2.3.2超时阻塞Redis工作在单线程中。一般来说,它只能同时处理一条Redis访问命令。操作Bigkey的命令通常很耗时。这段时间Redis无法处理其他命令,只能阻塞其他命令。等待,这会导致客户端阻塞,导致客户端访问超时,更严重的会导致主从故障切换。造成阻塞的操作不仅是业务程序的访问,还有key过期自动删除和del删除命令。对于Bigkey来说,这些操作也需要谨慎使用。超时阻塞案例我们遇到过这样一个超时阻塞的案例。业务方反映程序访问Redis集群超时。hkeys访问Redis的平均响应时间在200毫秒左右,最大响应时间达到了500多毫秒,如下图所示。hkeys是获取哈希表中所有字段的命令。分析应该是集群中部分实例存在hash类型的Bigkey,导致hkeys命令执行时间过长,出现阻塞。1、使用Daas平台“服务监控-数据库实例监控”,选择master节点,选择Redis响应时间监控指标“redis.instance.latency.max”,如下图,我们可以从监控图(一)正常情况下,该实例的响应时间在0.1毫秒左右。(2)监测指标尖峰多。该实例的响应时间约为70毫秒,最大值约为100毫秒。在这种情况下,该实例将处理Bigkey访问命令100毫秒,并且无法处理其他命令。通过查看监控指标,我们验证了我们的分析是正确的。正是这些监控指标的尖峰,导致hkeys命令的响应时间比较长。我们找到了具体的master实例,然后利用master实例的slave来分析Bigkey的情况。2、使用Daas平台“工具集-操作项管理”选择slave实例进行分析。分析结果如下图所示。有一个包含12102218个字段的哈希类型键。3.与业务沟通。这个Bigkey已经连续存储了30天的业务数据。建议按照二级哈希的方式拆分成多个key。也可以将30天的数据按照分钟级别拆分成多个key。每个key的元素个数控制在5000以内,业务目前正在进行调度优化。优化后,监控指标响应时间的尖峰现象将消失。2.3.3网络拥塞Bigkey的价值比较大,这也意味着每次获取都会产生大量的网络流量。假设一个Bigkey为10MB,客户端访问量为每秒100个,则每秒产生1000MB流量。对于普通的千兆网卡(以字节计128MB/s)服务器简直就是一场灾难。而且我们现在的Redis服务器采用的是单机多实例的方式来部署Redis实例,这就意味着一个Bigkey可能会影响到同一台服务器上的其他Redis集群实例,进而影响到其他业务。2.3.4迁移难点我们在运维中经常做的变更操作是水平扩展,就是增加Redis集群的节点数量,达到扩展的目的。这个水平扩容操作会涉及到key的迁移,将原来实例上的key迁移到新扩容的实例上。当要迁移key时,是通过migrate命令完成的。Migrate其实是将dump+restore+del三个命令组合成一个原子命令完成的。它会在执行过程中阻塞这两个迁移实例,直到出现以下任何一种结果时才会释放:迁移成功、迁移失败、等待超时。如果在key迁移过程中遇到Bigkey,会导致迁移的两个实例长时间阻塞,可能导致客户端阻塞,导致客户端访问超时;或者迁移时间过长,导致迁移超时,导致迁移失败,水平扩展失败。迁移失败案例我们也遇到过一些因为Bigkey扩容导致迁移失败的案例。如下图所示,是一个Redis集群横向扩展的工单,需要进行key迁移。当工单执行到60%时,迁移失败。1、进入工单查找故障实例,使用故障实例的slave节点,在Daas平台的“工具-操作项管理”中进行Bigkey分析。2、经过分析,发现hash类型的Bigkey有8,421,874个字段。就是这个Bigkey导致迁移时间过长,超过迁移时限,导致工单失败。3.为了与业务进行通信,这些key记录了用户访问系统某个功能模块的ip地址,所有访问该功能模块的ip都会记录在key中。随着时间的积累,这把钥匙变得越来越大。也是通过拆分来优化的。可以考虑按照时间和日期维度拆分,即将一段时间的访问IP记录在一个key中。4.Bigkey优化完成后,可以重试扩容工单完成集群扩容操作。3、Bigkey的发现Bigkey首先需要着重源码管理,防止Bigkey的出现;其次,需要能够及时发现,发现后及时处理。有很多方法可以分析Bigkey。这里介绍两种常用的方法,也是Daas平台用来分析Bigkey的方法。分别是Bigkeys命令分析法和RDB文件分析法。3.1scan命令分析Redis4.0及以上版本提供--Bigkeys命令,可以分析实例中每个数据结构的top1Bigkey,同时给出每个key值的个数和平均大小数据类型。执行--Bigkeys命令时需要注意以下几点:建议在slave节点上执行,因为--Bigkeys也是通过scan完成的,可能会导致节点阻塞。建议在节点本地执行,可以减少网络开销。如果没有slave节点,可以使用--i参数,例如(--i0.1表示每100毫秒执行一次)。--Bigkeys只能计算每个数据结构的top1。如果某些数据结构的Bigkey较多,则无法找到。Daas平台在原有Bigkeys代码的基础上集成了查询Bigkeys的方法。这种方法的缺点是只能计算每个数据结构的top1。如果某些数据结构的Bigkey较多,则无法找到。这种方式比较安全,已经开放给业务开发的同学使用。3.2RDB文件分析借助开源工具,如rdb-tools,分析Redis实例的RDB文件,找到其中的Bigkey。该方法需要生成RDB文件,需要注意以下几点:建议在slave节点上执行,因为生成RDB文件会影响节点性能。需要生成RDB文件,会影响节点的性能。虽然是在从节点上执行的,但是可能会造成主从中断,从而影响到主节点。Daas平台集成了基于RDB文件分析代码查询Bigkey的方法。可以根据实际需要自定义填写N,分析topN个Bigkeys。这种方式风险比较大,只有DBA才有权限进行分析。3.3Bigkey巡检通过巡检,可以提前发现隐患,解决隐患,避免故障发生。进行全网Bigkey巡检是避免Bigkey故障的较好方法。由于全网Redis实例较多,分析速度比较慢,用目前的分析方法很难完成。为了解决这个问题,存储研发组的分布式数据库同学打算开发一个高效的RDB分析工具,然后通过大规模解析RDB文件来分析Bigkey,可以提高分析速度,实现Bigkey的检查。4.Bigkey处理优化4.1Bigkey拆分优化Bigkey的原理是减少字符串的长度,减少list、hash、set、zset等中的元素个数。当我们知道哪些key是Bigkey时,我们就可以进行拆分一个键变成多个键。例如,可以参考以下拆分方法。biglist:list1,list2,...listNbighash:可以做secondaryhash,比如hash%100按照日期拆分成倍数:key20220310,key20220311,key2022032124.2Bigkey分析工具优化我们有不止全网2200个Redis集群,实例数达到45000多个,一些比较大的集群有1000多个实例。上面提到的两个Bigkey分析工具,依然是实例维度分析。对于实例数比较多的集群,进行全聚类分析。这也很耗时。为了提高分析效率,从以下几个方面进行优化:可以从集群维度中选取所有slave进行分析。同一集群中同一台服务器的从实例串行分析,不同服务器的从实例并行分析。默认最大并发10,可以同时分析10个实例,输入执行分析的并发可以自定义。分析所有符合Bigkey规定标准的key信息:所有大于1MB的string类型的key,如果不存在则列出最大的50个key;hash、list、set、zset等类型元素大于2000的所有key,如果不存在则给出每种类型最大的50个key。增加暂停、重启、结束功能,暂停分析后可以重启。4.3水平扩展和迁移优化目前的情况下,我们发现了一些Bigkey是被动发现的,还有一些是水平扩展时发现的。由于Bigkeys的存在导致扩容失败,严重时会触发主从故障切换。这时候可能已经导致业务程序的访问超时,导致可用性下降。分析了DaaS平台横向扩展过程中密钥迁移的过程及影响参数。内容如下:(1)[cluster-node-timeout]:控制集群的节点切换参数。master阻塞超过cluster-node-timeout/2的时间,会主观判断节点处于offlinepfail状态。如果迁移Bigkey阻塞时间超过cluster-node-timeout/2,可能会导致主从切换。(2)[migratetimeout]:控制迁移io的超时时间。如果超过此时间仍未完成迁移,则迁移将中断。(3)【迁移重试周期】:迁移重试周期由横向扩展的节点数决定。例如集群扩容到10个节点,迁移失败后的重试周期为10次。(4)【一个迁移重试周期内的重试次数】:在一个迁移重试周期内,会进行3次迁移重试,每次迁移超时时间分别为10秒、20秒、30秒,每次之间没有间隔重试。例如集群扩容到10个节点,迁移时遇到Bigkey,第一次迁移的迁移超时时间为10秒。如果迁移在10秒后仍未完成,迁移超时将设置为20秒以重试。如果再次失败,则会设置为Themigratetimeoutis30seconds重试。如果还是失败,程序会迁移另外9个新节点,但是每次迁移其他新节点之前,都会设置migratetimeout为10秒,20秒,30秒重试迁移那个迁移失败的Bigkey.在这个重试过程中,每个重试周期阻塞(10+20+30)秒,共重试10个周期,共计600秒。其实后面的9个重试周期是没有用的。每次重试之间没有间隔,Redis实例会一直阻塞。(5)【迁移失败日志】:迁移失败后,记录的日志不包含迁移节点、solt、key信息,根据日志无法立即定位到问题key。我们对这个迁移过程进行了优化,如下:(1)[cluster-node-timeout]:默认60秒,迁移前设置为15分钟,防止迁移Bigkey阻塞导致主从故障切换。(2)【migratetimeout】:为了尽量减少实例阻塞时间,每次重试的超时时间为10秒,3次重试间隔30秒,这样Redis实例只会连续阻塞10秒最多。(3)【重试次数】:迁移失败后,只重试3次(重试是为了避免网络抖动等原因导致迁移失败),每次重试间隔30秒,3次后失败retries,迁移会被暂停,日志会记录Bigkey,其他节点迁移的重试会被移除。(4)【优化日志记录】:迁移失败日志记录迁移节点、solt、key信息,可第一时间定位问题节点和key。五、总结本文通过对Bigkey的分析,着重阐述运维中bigkey问题的处理思路和解决方案。首先,要从源头上进行管理,防止形成Bigkey。DBA们应该加强对业务开发同学bigkey相关问题的宣传;其次,他们要有及时发现的能力,这也是我们目前的不足。后面我们会从Bigkey检测和Bigkey分析工具两个方面提升Bigkey的发现能力。参考:Redis命令参考Github:redis的rdb-toolsbigkey(看这篇文章就够了)
