美文网首页Java Blog
你遇到过哪些Redis的问题?

你遇到过哪些Redis的问题?

作者: 爪哇部落格 | 来源:发表于2019-04-21 20:44 被阅读2次

    在Redis的运维使用过程中你遇到过那些问题,又是如何解决的呢?本文收集了一些Redis的常见问题以及解决方案,与大家一同探讨。

    \color{red}{码字不易,欢迎大家转载,烦请注明出处;谢谢配合}

    你的Redis有bigkeys吗?

    什么是bigkeys

    bigkeys是指key不恰当设定,抑或是key对应的value值占用内存空间过大;具体表现为以下几种情形:

    • key值不恰当设定(比较少见),key设定冗长
    • String类型 value值长度过大
    • Hash,List,Set,Zset 包含元素个数过多

    bigkeys有什么危害

    为什么我们必须警惕bigkey呢?其实bigkey主要有以下几个方面的危害:

    • 内存使用不均匀,例如:在Redis-Cluster模式中,bigkey会造成节点内存使用不均匀。
    • 超时阻塞,由于Redis是单线程架构,操作bigkey耗时较长,有可能造成Redis阻塞。
    • 网络拥阻,例如:一个bigkey占用空间是1M,每秒访问1000次,将造成1000M的流量,可能造成打满机器带宽。

    当然,如果bigkey访问频率不高,也仅会造成节点间内存使用不均;而当bigkey访问频繁时,其带来的影响是不可想象的,所以日常在开发运维的过程中应该警惕bigkey的存在。

    如何找到bigkeys

    了解到bigkey危害,我们该如何发现bigkeys呢?

    Redis在设计之初就考虑到bigkeys的问题,我们可以使用 redis-cli --bigkeys 来发现bigkeys的分布情况;之后你如果想进一步了解bigkeys的具体情况可以使用 debug object <key> 来确定该key的具体信息。参考以下示例:

    利用redis-cli --bigkeys找到bigkey,具体生产环境执行时强烈建议在从节点实行,如果担心OPS太高,可以使用 -i 0.1 ,表示没100条scan命令休眠0.1秒;其实该命令实现的原理就是利用我们常用的scan + type + strlen/hlen/llen/scard/zcard 命令实现的,具体可以从redis-cli.c的源码中探寻。

    [root@VM_0_16_centos src]# redis-cli -p 6380 --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 'h' with 1 bytes
    [00.00%] Biggest string found so far 'hello' with 105 bytes
    [00.00%] Biggest string found so far 'heml' with 1434 bytes
    
    -------- summary -------
    
    Sampled 3 keys in the keyspace!
    Total key length in bytes is 10 (avg len 3.33)
    
    Biggest string found 'html' has 1434 bytes
    
    0 lists with 0 items (00.00% of keys, avg size 0.00)
    0 hashs with 0 fields (00.00% of keys, avg size 0.00)
    3 strings with 1540 bytes (100.00% of keys, avg size 513.33)
    0 streams with 0 entries (00.00% of keys, avg size 0.00)
    0 sets with 0 members (00.00% of keys, avg size 0.00)
    0 zsets with 0 members (00.00% of keys, avg size 0.00)
    

    执行结果是发现String类型的"html"为bigkey,我们紧接着来了解"html"的具体信息;使用
    debug object <key> 命令,还有如果是对于元素个数较多的数据结构,该命令可能会阻塞redis实例,所以强烈建议在从节点执行

    [root@VM_0_16_centos src]# redis-cli -p 6380
    127.0.0.1:6379> debug object html
    Value at:0x7f0b13a665c0 refcount:1 encoding:raw serializedlength:251 lru:12181323 lru_seconds_idle:229
    127.0.0.1:6379> strlen html
    (integer) 1434
    

    我们发现key为"html"的String类型的value长达1434个字节,以上便是演示查找bigkeys的过程;除了以上方式我们可以在bigkeys影响redis正常提供服务之前,通过 scan + debug object 对怀疑的bigkeys进行逐个检查。

    当然你如果担心执行相关命令会对正式环境有一定的影响,你也可以通过对RDB进行备份,然后根据RDB文件的结构,对RDB中的数据进行逐个分析同样的可以找到bigkey,不过这种方式的开发成本会有些高;使用者可以依据自己的实际情况来酌情判断。

    如何处理bigkeys

    经过一番折腾,我们终于找到bigkeys了,那么我们应该如何处理它呢?

    在Redis4.0之前版本,由于DEL命令是同步删除的,针对String类型的bigkeys确实可以使用DEL命令,删除速度相对较快,一般不会阻塞redis;然而对于元素个数较多的数据结构,使用DEL命令来删除可能会阻塞redis实例;针对 Hash 结构,我们可以利用 HSCAN + HDEL 删除元素的成员,成员删除之后再利用 DEL 删除key;其余数据类似都是渐进的方式先删除成员,再删除key。

    Redis4.0版本之后则支持了Lazy Delete Free模式,你可以使用 UNLINK 命令来删除bigkeys,它的实现是异步的,具体可以从redis-cli.c的源码中探寻,你需要先确认打开了lazyfree相关配置。

    bigkeys总结

    bigkeys的表现形式是内存分配不均;频繁操作的实际影响是有可能造成超时阻塞,网络拥阻;解决思路是事前监控,事中找到bigkeys,并通过正确的方式删除bigkeys。

    你的Redis有hotkeys吗?

    什么是hotkeys?

    hotkeys是在Redis实例中某些key的操作频次远高于其他key,那么这些被频繁操作的热点key我们就称之为hotkeys。

    hotkeys有什么危害?

    hotkeys有什么危害呢?以Redis-Cluster模式为例,存在hotkeys的节点,将面临以下挑战:

    • 请求分配不均,存在hotkeys的节点面临较大的访问压力
    • 缓存击穿,hotkeys过期时,大量请求将直接导向DB
    • 缓存雪崩,击垮存在hotkeys的节点,导致不能正常提供服务

    如何发现hotkeys呢?

    Redis4.0之后客户端提供了hotkeys发现的相关命令,我们可以通过 redis-cli --hotkeys 来发现hotkeys;

    Redis4.0之前我们也可以通过客户端,代理端,服务端,机器端等多个方面来发现hotkeys:

    • 在客户端建立全局字典表,对key和调用次数进行统计;缺点:侵入客户端
    • 如果你的集群是通过proxy + redis 的方式搭建的,那你可以很方便的从代理端对key和调用次数进行监控;缺点:限制代理模式的集群
    • 在服务端可以利用 monitor,对服务端接收的请求进行监控;缺点:侵入服务端,在高并发情况下会使内存暴增,适合短时间使用
    • 如果你不想侵入服务端与客户端,可以对服务端接收的请求进行抓包,分析以及监控;例如使用ELK(Elasticsearch + Logstach + kinbana)用packetbeat进行抓包。

    如何处理hotkeys?

    我们了解到hotkeys的危害,并可以通过技术手段找到hotkeys以后,我们该怎么对系统做优化呢?

    首先针对hotkeys过期,面临的重建问题,可以使用以下有效手段来尽可能的减少key重建的过程:

    • 设置互斥锁,保证由一个线程完成热点key的重建,避免大量的请求直接导向DB
    • "永不过期",将hotkeys的过期时间设置较长的时间,或者永不过期;等待hotkeys触发的热点事件过去后再考虑过期。

    针对Redis集群的优化,包括但不限于以下几种方式:

    • hotkeys表现就是请求分配不均;我们可以以此为出发点,来想办法使请求尽可能的分布平均;例如:利用<hotkeys_n,value> ,n为随机数,尽可能的多个实例都有该数据,在访问时在n的范围内取随机数以此来分摊请求;此方式需要一定的代码改造;
    • 本地缓存,此方式需要对热点信息有预知,例如:电商产品大促,热点产生在可以预知的范围内,便可以考虑此方式;
    • 集群的热点数据的节点的扩容,此方式原理同第一种方式相同,不同点在于不需要对代码进行改造,而是直接增加hotkeys对应节点的数据副本,使多个节点都具备提供该数据的读取能力,以此来均衡请求。

    hotkeys总结

    hotkeys的表现形式是请求的分配不均,问题恶化将导致Redis集群请求倾斜,甚至集群雪崩,我们可以通过多种途径来均衡请求,避免单个节点过热;如果hotkeys的超时实现过短,可能会导致大量请求涌入到DB,并发重建key,可以通过合理的锁机制或者设置合理的超时时间来避免。

    Redis缓存穿透

    什么是缓存穿透

    缓存穿透是大量请求的key在缓存中没有,直接请求到DB,使缓存失去保护数据库的作用;例如:黑客刻意构建大量缓存中没有的key,导致每次处理请求都需要去访问数据库。

    正常缓存处理流程

    正常缓存流程.png

    缓存穿透处理流程

    缓存穿透.png

    缓存穿透的流程便是故意构建缓存中没有的key导致,所有的请求必须查库;缓存失去其保护数据库的意义。

    解决缓存穿透

    通过以上流程图我们对缓存穿透有了一定的了解,那该如何解决此类问题呢?通常解决的方式有两种:

    (1) 对空值进行缓存,设置较短的失效时间;

    缓存空结果.png

    分析:我们对null进行缓存,Redis需要更大的内存空间;此方案适用于请求key变化不频繁的情况;如何黑客恶意攻击,每次构建的不同的请求key,这种方案并不能从根本上解决此问题。

    (2) 使用布隆过滤器,布隆过滤器优势在于检索一个元素是否在一个集合内;我们可以利用布隆过滤器来判断请求的key是否在合理的范围内,如果不存在,则直接过滤掉。

    布隆过滤器.png

    分析:可以利用Redis的 BitMap来实现布隆过滤器,用其来缓存目标数据集变化不频繁,而请求key变化频繁的情况。

    Redis缓存雪崩

    什么是缓存雪崩

    缓存雪崩是指由于缓存集中过期或者缓存不可用,导致大量请求直接导向数据库。在高并发的情况下,巨大的请求量有可能导致数据库的崩溃,甚至导致整个应用体系的全盘崩溃;缓存失去保护是数据库的作用,而巨大导致流量流向数据库就是缓存雪崩的表现形式。

    如何预防及避免

    • 设置合理的过期策略,避免缓存集中过期。
    • hotkeys分片存储,避免请求数据的倾斜,导致缓存。
    • hotkeys设置合理的过期时间或者“永不过期”。

    Redis阻塞

    我们知道Redis是单线程模型,如果线上Redis发生阻塞对整个应用将是毁灭性的;那什么原因会导致Redis阻塞呢?

    API或数据结构使用不合理

    常见的是在生产上执行时间复杂度高的命令如: KEYS,可以通过RENAME 方式将命令修改为不易猜测的,避免开发运维人员的不当执行。
    数据结构的不合理,如存在频繁操作bigkeys,有可能造成阻塞,将bigkeys拆分成成员较小的key。
    

    CPU饱和

    Redis单实例OPS可以到达平均10W+左右,如果你的Redis实例OPS已经达到较高的数值,那你可以考虑集群的水平扩展,来降低实例的OPS;但是你的Redis实例OPS不高,CPU使用率较高,那你应该检查应用是否使用了时间复杂度较高的命令。 
    

    持久化阻塞

    我们知道Redis可以进行持久化来防止数据的丢失;RDB方式,主进程会fork一个共享内存子进程来创建RDB文件,如果fork耗时过长,必然将阻塞主进程。
    AOF刷盘,通常我们设置的刷盘策略是everysec,但由于磁盘压力过大,fsync有可能耗时较长,当时间大于1秒时,为了保证数据安全,下次fsync调用将阻塞知道上次调用结束。
    

    其他原因

    CPU竞争:Redis是CPU密集型应用,应避免跟其他CPU密集型应用部署在一起
    内存交换:Redis由于从内存中直接读取,所以响应速度很快;当内存严重不足时,可能会存在内存交换,这将影响Redis的执行效率;通过cat /proc/$pid/smaps | grep Swap 来确认是否有频繁的内存交换。
    网络问题:连接数限制或者网络延时等也有可能导致阻塞
    

    总结

    本文介绍了bigkeys,hotkeys,缓存穿透,缓存雪崩,阻塞等问题;然而我们在实际应用中难免会遇到各种各样的问题,本文难以一一列举;但是面对问题我们要沉着冷静,了解清楚问题的现象与本质,找到问题的症结所在,随后在对症下药便可以解决问题。

    相关文章

      网友评论

        本文标题:你遇到过哪些Redis的问题?

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