Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 287|回复: 0

Redis常见问题及解决方案

[复制链接]
  • TA的每日心情
    奋斗
    2024-4-6 11:05
  • 签到天数: 748 天

    [LV.9]以坛为家II

    2034

    主题

    2092

    帖子

    70万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    705612
    发表于 2021-5-14 06:22:27 | 显示全部楼层 |阅读模式

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

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

    你的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,导致每次处理请求都需要去访问数据库。

    正常缓存处理流程

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

    解决缓存穿透

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

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

     

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

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

     

    分析:可以利用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 来确认是否有频繁的内存交换。 网络问题:连接数限制或者网络延时等也有可能导致阻塞 

    Redis淘汰策略

    当Redis的内存使用达到限制时(可通过maxmemory <bytes>设置),会根据根据淘汰策略来移除Keys;有如下淘汰策略:

    • allkeys-random:在所有keys中随机移除
    • allkeys-lru:在所有keys中使用lru移除
    • allkeys-lfu:在所有keys中使用lfu移除
    • volatile-random:在过期keys中随机移除
    • volatile-lru:在过期keys中使用lru移除
    • volatile-lfu:在过期keys中使用lfu移除
    • volatile-ttl:移除即将过期
    • noevction:不移除任何key,空间不足时将抛出error

    lru:Least Recently Used 最近最少使用
    lfu:Least Frequently Used 最不经常使用

    总结

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

     

     

     

     

     
     
     
    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-4-30 00:24 , Processed in 0.069666 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表