当前位置: 首页 > 科技观察

Redis的BigKey和HotKey再次引发线上事故!

时间:2023-03-12 03:39:55 科技观察

问题的严重性首先要声明问题的严重性。BigKey(大键)和HotKey(热键)问题比较常见。此类问题不仅会降低服务的性能,还会影响用户的正常使用,甚至会造成大规模的服务故障。经济损失,品牌损失巨大。所以在Redis运维过程中,由于Bigkey的存在,DBA一直在和业务开发方强调Bigkey的规避方法和危害。开发同学在开发过程中也需要高度重视和防范这个问题。1.什么是BigKey和HotKey?什么是BigKey俗称“大键”,是指在redis的日常生产过程中,某些键占用内存空间过大。通俗地说,redis就是一种key-value的存储方式。当一个键对应的存储值达到一定程度时,就会出现一个大键。redis中有多种数据存储结构,如String、List、Hash等,每种存储结构都有一个可以承载的数据上限。当密钥包含的内容接近限制或高于平均值时,将生成一个大密钥。关注公众号:码猿技术专栏,回复关键字:1111获取阿里内部Java性能调优手册Redis中一个string类型最大可达512MB,二级数据结构(如hash、list、set、zset等))可以存储大约40亿(2^32-1)个元素,但实际上不会达到这么大的值。一般来说,如果达到以下条件,就可以认为是Bigkey。【字符串类型】:单个超过1MB的字符串类型值可以认为是一个Bigkey。【非字符串类型】:Hash、list、set、orderedset等,如果元素个数超过2000,可以认为是Bigkey。什么是HotKey俗称“热键”,一个key对应一个redisshard。当短时间内有大量请求命中分片,且key被频繁访问时,该key就是hotkey。当大量的请求被分发计算,最后集中到同一个redis实例下的某个key上时,该key请求频繁,占用资源多。对于其他分片,由于key分配不合理,请求较少。整个redis集群资源使用不均衡。例如:某一线女明星官宣结婚证,女星微博访问量短时间内暴增(假设缓存同步账号内容,使用账号id作为key),微博服务瘫痪(没有任何实时性,仅供参考,以虚拟为例)。在这种场景下,上述键被大量访问,导致热键。简而言之,当某个Key获得的访问次数明显高于其他Key时,我们就可以称它为HotKey。在访问量方面,一个常见的HotKey如:Redis实例每秒总访问量为10000次,其中一个Key每秒访问量达到7000次(访问量明显高于其他Key)。每秒发送大量的HGETALLs给一个拥有数千个成员且总大小为1MB的HASHKey(带宽占用明显高于其他Key)每秒发送大量的ZRANGE给一个拥有数万个成员的ZSETKeymembers(CPU时间消耗明显高于其他Keys)。二、服务中的bigkey和hotkey会造成什么问题?我们可以利用上面两个键的特点,来简单分析一下可能出现的几种问题。No.1:bigkey问题主要问题是一个key占用空间太大,内存空间分配不均衡(小tips:redis是内存key-value数据库)。那可能会导致以下问题:1.大量数据请求超时:Redis是单线程的。当一个关键数据的响应时间变长时,后续请求会频繁超时。如果业务容灾措施考虑不够,会引发更大的问题。2、带宽被侵占和网络拥塞:当一个key占用空间过大时,多个请求会占用很大的带宽,直接影响服务的正常运行。3、内存溢出或处理阻塞:存在大key时,如果继续添加,key占用的内存会越来越大,严重时会导致内存数据溢出;当key过期需要删除时,由于数据量过大,可能会出现主库响应时间过长,主从数据同步异常(被删除的数据还在使用由从属数据库)。No.2:热键问题热键,热键的问题是单点访问频率太高。这可能会导致以下问题:1.分片服务瘫痪:redis集群会被分成很多分片,每个分片都有自己的数据范围需要处理。当某个分片被频繁请求时,分片服务可能会瘫痪。2、Redis分布式集群的优势被削弱:如果请求不够均衡,过于单一,那么Redis分布式集群的优势必然被削弱。3、可能造成资金损失:在极端场景下,很容易出现边界数据处理不及时的情况,在订单等场景下,可能会造成资金损失。4、导致缓存崩溃:我们都知道,当缓存请求不可用时,就会去请求数据库。如果请求过于集中,redis承载不了,就会有大量的请求打到数据库上。这时,数据库服务可能会瘫痪。然后触发系统雪崩。5.CPU占用率高影响其他业务:单个shard的CPU占用率过高,其他shard没有CPU资源,从而受到影响。三、如何发现bigkey和hotkey1.业务分析结合技术方案:通常需要分析业务场景结合技术方案来判断bigkey和hotkey是否会出现问题。例如:(1)在购物车场景中,当购物车的key设计没有上限,也没有其他随机值约束时,只使用mid。这时候我们就要注意了,万一有个购物狂,一次性多买5万个产品怎么办?(2)活动资格列表场景,当一个活动资格查询列表放入key时,在活动过程中会进行频繁的查询和操作。这时候我们要注意列表中有多少数据?查询资质的操作是中心化的吗?如果集中,qps是多少?2、使用redis命令发现:Redis4.0及以上版本提供了--Bigkeys,--hotkeys命令,可以分析实例中各个数据结构的top1Bigkey,并给出各个数据类型的key同时值的个数和它们的平均大小。查看bigkey:redis-cli-a登录密码--bigkeys查看hotkey:redis-cli-a登录密码--hotkeys--bigkey使用示例3.使用工具:(1)可以使用redis可视化工具查看(例如:另一个redisdesktopmanager)可视化工具,可以清晰的给出redis集群当前的信息,经过简单的数据分析,可以观察到异常情况。(2)Redis-rdb-tools(附:https://github.com/sripathikrishnan/redis-rdb-tools)借助市面上的开源工具(本文不深入讨论)(3)借助公司自研工具Ifvivo内部的DaaS(数据即服务)平台。4.RDB文件分析方法通过RDB文件分析bigkey4.如何解决bigkey和hotkey问题解决方法Bigkey解决方法主要方法:拆分bigkey,拆分bigkey中存放的数据(bigvalue),变成value1,value2...valueN,如果bigvalue是一个bigjson,通过mset的方式将这个key的内容分散到各个instance中,减少bigkey对数据量倾斜的影响。关注公众号:码猿技术专栏,回复关键词:1111获取阿里内部Java性能调优手册如果大值是大列表,可以拆分成小块。=list_1,list_2,list3,listN其他数据类型同热键解。主要方法:在客户端使用本地缓存,从而减少redis集群对hotkey的访问量,但是本地缓存带来了两个A问题:1.如果所有可能成为hotkey的key都缓存在本地,本地缓存会不会太大,影响应用本身需要的缓存开销。2、如何保证本地缓存和redis集群数据有效期的一致性。以上两个问题,详细看:浅谈Redis+Caffeine二级缓存5.生产实例:下面是vivo团队Bigkey问题的解决方案互联网数据库团队-陈杜霆刚梳理了它的结构为了方便大家学习。vivo团队运维的Redis集群介绍。全网Redis集群2200多个,实例数达到45000多个。全网一次Bigkey校验估计需要数年时间,非常耗时。Bigkey的来源Bigkey一般是由于程序设计不当,或者数据大小估计错误,比如以下几种情况。【统计】场景遇到一个统计key,记录来访用户的IP。随着访问网站的用户越来越多,这个key的元素会越来越大,形成一个Bigkey。[Cache]场景CacheAside模式,业务程序从数据库中查询数据并序列化到Redis中,然后查询数据库并将查询到的数据追加到Redis缓存中,短时间内将大量数据缓存到Rediskey中时间,形成Bigkey。【队列】场景使用Redis作为队列使用场景。如果消费不及时,队列就会越来越大,就会出现Bigkey。问题一:内存空间不均匀内存空间不均匀不利于集群统一管理内存,存在数据丢失的风险。下图中的三个节点属于同一个集群。它们的键号相似,但内存容量却大不相同。带Bigkey的实例占用内存4G多。可以通过Daas平台“工具集-操作项管理”选择对应的slave实例进行分析,找出具体的Bigkey。问题二:超时阻塞Redis是单线程工作的。一般来说,一次只能处理一条Redis访问命令。操作Bigkey的命令通常很耗时。这段时间Redis无法处理其他命令,只能阻塞其他命令。等待,这会导致客户端阻塞,导致客户端访问超时,更严重的会导致主从故障切换。当然,造成阻塞的操作不仅是业务程序的访问,还有自动过期key的删除和del删除命令。对于Bigkey来说,这些操作也需要谨慎使用。让我们以生产中的超时阻塞为例。我们遇到过这样一个超时阻塞的情况。业务方反映程序访问Redis集群超时。hkeys访问Redis的平均响应时间在200毫秒左右,最大响应时间达到了500多毫秒,如下所示。hkeys是获取哈希表中所有字段的命令。分析应该是集群中部分实例存在hash类型的Bigkey,导致hkeys命令执行时间过长,出现阻塞。1、使用Daas平台“服务监控-数据库实例监控”,选择master节点,选择Redis响应时间监控指标“redis.instance.latency.max”,如下图,我们可以从监控图(1)正常情况下,本实例的响应时间在0.1毫秒左右。(2)监测指标尖峰多。该实例的响应时间约为70毫秒,最大值约为100毫秒。在这种情况下,该实例将处理Bigkey访问命令100毫秒,并且无法处理其他命令。通过查看监控指标,我们验证了我们的分析是正确的。这些监控指标的尖峰导致hkeys命令的响应时间比较长。我们找到了具体的master实例,然后利用master实例的slave来分析Bigkey的情况。2、使用Daas平台“工具集-操作项管理”,选择slave实例进行分析,分析结果如下图,有一个hash类型的key,字段为12102218。与业务沟通,拆分Bigkey。这个Bigkey已经连续存储了30天的业务数据。建议按照二级哈希法拆分成多个key,或者将30天的数据按照分钟级别拆分成多个key,将每个key的元素个数控制在5000以内。优化后,监控指标响应时间的尖峰现象将消失。问题三:Bigkey网络阻断的价值比较大,这也意味着每次获取都会产生大量的网络流量。假设一个Bigkey是10MB,客户端每秒访问100次,那么每秒产生1000MB的流量。对于普通的千兆网卡(以字节计128MB/s)服务器简直就是一场灾难。而且我们现在的Redis服务器采用的是单机多实例的方式来部署Redis实例,这就意味着一个Bigkey可能会影响到同一台服务器上的其他Redis集群实例,进而影响到其他业务。问题四:迁移难我们在运维中经常做的变更操作是水平扩展,也就是增加Redis集群的节点数量,达到扩展的目的。这个水平扩容操作会涉及到key的迁移,将原来实例上的key迁移到新扩容的实例上。当要迁移key时,是通过migrate命令完成的。migrate其实就是将dump+restore+del三个命令组合成一个原子命令完成的。当它被执行时,它会阻塞这两个迁移实例,直到出现以下任何一种结果时才会被释放:迁移成功、迁移失败、等待超时。如果在key迁移过程中遇到Bigkey,会导致迁移的两个实例长时间阻塞,可能导致客户端阻塞,导致客户端访问超时;或者迁移时间过长,导致迁移超时,导致迁移失败,水平扩展失败。让我们来看一个生产迁移失败的案例。我们也遇到过一些因为Bigkey扩容迁移失败的案例。如下图,这是一个Redis集群水平扩展的工单,需要迁移。%的时间,迁移失败。如何解决?大体的解决流程是:进入工单查找故障实例,使用故障实例的slave节点,在Daas平台的“工具集-操作项管理”中进行Bigkey解析。经过分析,发现哈希类型Bigkey有8,421,874个字段。就是这个Bigkey导致迁移时间过长,超过迁移时限,导致工单失败。3.为了与业务进行通信,这些key记录了用户访问系统某个功能模块的ip地址,所有访问该功能模块的ip都会记录在key中。随着时间的积累,这把钥匙变得越来越大。也是通过拆分来优化的。可以考虑按照时间和日期维度拆分,即将一段时间的访问ip记录在一个key中。4.Bigkey优化完成后,可以重试扩容工单完成集群扩容操作。如何在生产中发现Bigkey,首先需要关注源头治理,防止Bigkey的出现;其次,需要能够及时发现,发现后及时处理。有很多方法可以分析Bigkey。这里介绍两种常用的方法,也是Daas平台用来分析Bigkey的方法。分别是Bigkeys命令分析法和RDB文件分析法。1.Bigkeys命令分析Redis4.0及以上版本提供--Bigkeys命令,可以分析实例中各个数据结构的top1Bigkey,同时给出key值的个数和平均大小每种数据类型。执行--Bigkeys命令时需要注意以下几点:建议在slave节点上执行,因为--Bigkeys也是通过scan完成的,可能会导致节点阻塞。建议在节点本地执行,可以减少网络开销。如果没有slave节点,可以使用--i参数,例如(--i0.1表示每100毫秒执行一次)。--Bigkeys只能计算每个数据结构的top1。如果某些数据结构的Bigkey较多,则无法找到。Daas平台集成了一种基于native-Bigkeys代码查询Bigkeys的方法。这种方法的缺点是只能计算每个数据结构的top1。如果某些数据结构的Bigkey较多,则无法找到。这种方式比较安全,已经开放给业务开发的同学使用。2、RDB文件分析使用开源工具,如rdb-tools,分析Redis实例的RDB文件,找出其中的Bigkey。该方法需要生成RDB文件。需要注意以下几点:建议在slave节点上执行,因为生成的RDB文件会影响节点性能。需要生成RDB文件,会影响节点的性能。虽然是在从节点上执行的,但是可能会造成主从中断,从而影响到主节点。Daas平台集成了基于RDB文件分析代码查询Bigkey的方法。可以根据实际需要自定义填写N,分析topN个Bigkeys。这种方式风险较大,只有DBA才有权限进行分析。3、Bigkey巡检通过巡检,可以提前发现隐患,解决隐患,避免故障的发生。全网Bigkey的检测是避免Bigkey故障的较好方法。由于全网Redis实例较多,分析速度比较慢,使用目前的分析方法难以完成。为了解决这个问题,存储研发组的分布式数据库同学打算开发一个高效的RDB分析工具,然后通过大规模解析RDB文件来分析Bigkey,可以提高分析速度,实现Bigkey的检查.生产中Bigkey处理优化1.Bigkey拆分优化Bigkey的原理是对于strings减少字符串的长度,对于lists、hashes、sets、zsets减少元素个数。当我们知道哪些键是Bigkey时,我们可以将单个键拆分为多个键。例如,可以参考以下拆分方法。biglist:list1,list2,...listNbighash:可以做secondaryhash,比如hash%100可以按日期分成几个:key20220310,key20220311,key2022032122。Bigkey分析工具优化我们全网Redis集群有2200多个,实例数达到了45000多个,一些比较大的集群有1000多个实例。上面提到的两个Bigkey分析工具,依然是实例维度的分析。对于实例数比较多的集群,还需要进行全聚类分析,比较耗时,为了提高分析效率,从以下几个方面进行优化:可以从集群维度中选取所有的slave进行分析.同一个集群的同一个serverslave实例串行分析,不同server的slave实例并行分析。默认最大并发10,可以同时分析10个实例,输入执行分析的并发可以自定义。分析所有符合Bigkey规定标准的key信息:所有大于1MB的string类型的key,如果不存在则列出最大的50个key;hash、list、set、zset等元素超过2000个类型的所有键,如果不存在,给出每种类型最大的50个键。增加暂停、重启、结束功能,暂停分析后可以重启。在目前横向扩展和迁移优化的情况下,我们有一些是被动发现的Bigkey,有一些是在横向扩展时发现的。由于Bigkeys的存在导致扩容失败,严重时会触发主从故障切换。这时候,可能已经导致业务程序访问超时,导致可用性下降。分析了DaaS平台横向扩展过程中密钥迁移的过程及影响参数。内容如下:(1)[cluster-node-timeout]:控制集群的节点切换参数,master阻塞超过cluster-node-timeout/2的时间,会主观判断该节点是处于离线pfail状态。如果迁移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阻塞导致的master-slavefailover.(2)【migratetimeout】:减少阻塞时间为了尽量减少实例阻塞时间,每次重试的超时时间为10秒,3次重试间隔30秒,这样只会连续阻塞10个Redis实例最多。第二。(3)【重试次数】:去掉其他节点迁移的重试次数。迁移失败后只重试3次(重试是为了避免网络抖动等原因导致迁移失败),每次重试间隔30秒。3次重试后,均失败,迁移将被暂停,日志将记录Bigkey,其他节点迁移的重试将被移除。(4)【优化日志记录】:日志记录迁移失败日志记录迁移节点、solt、key信息,可以第一时间定位到问题节点和key。BigKey和Hotkey的总结首先是要从源头上进行管理,防止BigKey和Hotkey的形成,加强对业务开发同学bigkey相关问题的宣传;其次,提高及时发现能力,实现Bigkey和Hotkey的及时检测。参考资料:Github:rdb-tools:https://github.com/sripathikrishnan/redis-rdb-tools(一)redis命令:Redis命令参考-Redis命令参考(二)Github:https://github.com/sripathikrishnan/redis-rdb-tools(3)另一个redis桌面管理器下载地址AnotherRedisDesktopManagerrelease:https://gitee.com/qishibo/AnotherRedisDesktopManager/releases