Rdeis做缓存使用的一些问题和解决方案

缓存架构的设计

常见的缓存架构设计

image.png 文件缓存
这里的文件缓存一般是基于http协议的文件缓存,一般放在nignx中。 常用的是静态文件(比如css,js,图片),不经常更新的话,nginx使用proxy_cache将用户的请 求缓存到本地一个目录。下一个相同请求可以直接调取缓存文件,就不用去请求服务器了。

JVM缓存
JVM缓存就是本地缓存,设计在应用服务器中(tomcat)。 通常可以采用Ehcache和Guava Cache,在互联网应用中,由于要处理高并发,通常选择Guava Cache。 适用本地(JVM)缓存的场景:
1、对性能有非常高的要求。
2、不经常变化
3、占用内存不大
4、有访问整个集合的需求
5、数据允许不时时一致(不需要强一致)

Redis缓存
分布式缓存

缓存问题

缓存穿透

缓存穿透是指在高并发下查询key不存在的数据(不存在的key),会穿过缓存查询数据库。导致数据库压力过大而宕机

解决方案:

1、对查询结果为空的情况也进行缓存,缓存时间(ttl)设置短一点,或者该key对应的数据insert了 之后清理缓存。
存在的问题:缓存太多空值占用了更多的空间

2、使用布隆过滤器。在缓存之前在加一层布隆过滤器,在查询的时候先去布隆过滤器查询 key 是否存在,如果不存在就直接返回,存在再查缓存和DB。

布隆过滤器简单介绍

布隆过滤器实际上是一个很长的二进制向量和一系列随机 hash映射函数。

image.png 布隆过滤器的原理:当一个元素被加入集合时,通过K个Hash函数将这个元素映射成一个数组中的K 个点,把它们置为1。检索时,我们只要看看这些点是不是都是1就(大约)知道集合中有没有它了:如果这些点有任何一个0,则被检元素一定不在;如果都是1,则被检元素很可能在。这就是布隆过滤器的 基本思想。

优点
二进制存储,省空间。通过下标快速定位,省时间。

缓存雪崩

当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如 DB)带来很大压力。

突然间大量的key失效了或redis重启,大量访问数据库,数据库崩溃

解决方案:
1、 key的失效期分散开 不同的key设置不同的有效期
2、设置二级缓存(数据不一定一致)
3、高可用(脏读)

缓存击穿

对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑一个问题:
缓存被“击穿”的问题,这个和缓存雪崩的区别在于这里针对某一key缓存,前者则是很多key。

缓存在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。

解决方案:
用分布式锁控制访问的线程
使用redis的setnx互斥锁先进行判断,这样其他线程就处于等待状态,保证不会有大并发操作去操作数据库。

数据不一致

缓存和DB的数据不一致。
解决方案
根据不同业务场景可以使用不同的策略。
1、要求数据强一致
加锁 更新数据时,加写锁;查询数据时,加读锁

image.png

2、要求数据最终一致 1、先删缓存再更新数据库
在数据库还未更新的情况下,缓存就已经删除了,那么高并发的情况下,会存在另外的线程去从缓存拿数据,但是缓存没有,所以会去数据库里取然后在进行缓存,因为这时候数据库还未更新,所以缓存的是旧数据。

image.png

2、先更新数据库再删缓存
主从同步的延迟,也会造成数据不一致

image.png 解决方案:延时双删

1、先更新数据库同时删除缓存项(key),等读的时候再填充缓存
2、2秒后再删除一次缓存项(key)

缺点:延时双删,延时多少时间不易确定

还有一种可能性就是: 先更新数据库,再删除缓存或先删缓存,再更新数据库。第二步操作都有可能失败

image.png

image.png 针对上面的问题,可以通过消息队列+异步重试订阅binlog日志解决
其他更多的情形和解决方案参考:www.zhihu.com/question/31…

数据并发竞争

问题描述:多客户端同时并发写一个key,可能本来应该先到的数据后到了,导致数据版本错了。或者是多客户端同时获取一个key,修改值之后再写回去,只要顺序错了,数据就错了。 一个key的值是1,本来按顺序修改为2,3,4,最后是4,但是顺序变成了4,3,2,最后变成了2。

解决方案
1、分布式锁+时间戳

image.png 2、利用消息队列+时间戳

在并发量过大的情况下,可以通过消息中间件进行处理,把并行读写进行串行化。 把Redis的set操作放在队列中使其串行化,必须的一个一个执行。

那么如何保证到达消息队列的消息是有序的呢?
没办法保证,所以消息里面带上时间戳,消费的时候比较一下时间戳。

Hot key

当有大量的请求(几十万)访问某个Redis某个key时,由于流量集中达到网络上限,从而导致这个redis的服务器宕机。造成缓存击穿,接下来对这个key的访问将直接访问数据库造成数据库崩溃,或者访问数据库回填Redis再访问Redis,继续崩溃。

image.png 如何发现热key

1、预估热key,比如秒杀的商品、火爆的新闻等
2、在客户端进行统计,实现简单,加一行代码即可
3、如果是Proxy,比如Codis,可以在Proxy端收集
4、利用Redis自带的命令,monitor、hotkeys。但是执行缓慢(不要用)
5、利用基于大数据领域的流式计算技术来进行实时数据访问次数的统计,比如 Storm、Spark Streaming、Flink,这些技术都是可以的。发现热点数据后可以写到zookeeper中

image.png 如何处理热Key:

1、变分布式缓存为本地缓存
发现热key后,把缓存数据取出后,直接加载到本地缓存中。可以采用Ehcache、Guava Cache都可 以,这样系统在访问热key数据时就可以直接访问自己的缓存了。(数据不要求时时一致)
2、在每个Redis主节点上备份热key数据,这样在读取时可以采用随机读取的方式,将访问压力负载到每个Redis上。
3、利用对热点数据访问的限流熔断保护措施
每个系统实例每秒最多请求缓存集群读操作不超过 400 次,一超过就可以熔断掉,不让请求缓存集群,直接返回一个空白信息,然后用户稍后会自行再次重新刷新页面之类的。(首页不行,系统友好性差)
通过系统层自己直接加限流熔断保护措施,可以很好的保护后面的缓存集群。

Big Key

大key指的是存储的值(Value)非常大,常见场景:

  • 热门话题下的讨论
  • 大V的粉丝列表
  • 序列化后的图片
  • 没有及时处理的垃圾数据

.....

大key的影响:

  • 大key会大量占用内存,在集群中无法均衡
  • Redis的性能下降,主从复制异常
  • 在主动删除或过期删除时会操作时间过长而引起服务阻塞

如何发现大key:

1、redis-cli --bigkeys命令。可以找到某个实例5种数据类型(String、hash、list、set、zset)的最大 key。
但如果Redis 的key比较多,执行该命令会比较慢
2、获取生产Redis的rdb文件,通过rdbtools分析rdb生成csv文件,再导入MySQL或其他数据库中进行分析统计,根据size_in_bytes统计bigkey

大key的处理:

优化big key的原则就是string减少字符串长度,list、hash、set、zset等减少成员数。

1、string类型的big key,尽量不要存入Redis中,可以使用文档型数据库MongoDB或缓存到CDN上。

如果必须用Redis存储,最好单独存储,不要和其他的key一起存储。采用一主一从或多从。

2、单个简单的key存储的value很大,可以尝试将对象分拆成几个key-value, 使用mget获取值,这样分拆的意义在于分拆单次操作的压力,将操作压力平摊到多次操作中,降低对redis的IO影响。

2、hash, set,zset,list 中存储过多的元素,可以将这些元素分拆。(常见)

以hash类型举例来说,对于field过多的场景,可以根据field进行hash取模,生成一个新的key,例如 原来的 
hash_key:{filed1:value, filed2:value, filed3:value ...},可以hash取模后形成如下 key:value形式 
hash_key:1:{filed1:value} 
hash_key:2:{filed2:value} 
hash_key:3:{filed3:value} 
... 取模后,将原先单个key分成多个key,每个key filed个数为原先的1/N
复制代码

3、删除大key时不要使用del,因为del是阻塞命令,删除时会影响性能。
4、使用 lazy delete (unlink命令)

删除指定的key(s),若key不存在则该key被跳过。但是,相比DEL会产生阻塞,该命令会在另一个线程中 回收内存,因此它是非阻塞的。 这也是该命令名字的由来:仅将keys从key空间中删除,真正的数据删 除会在后续异步操作。

redis> SET key1 "Hello" 
"OK" 
redis> SET key2 "World" 
"OK" 
redis> UNLINK key1 key2 key3 
(integer) 2
复制代码

猜你喜欢

转载自juejin.im/post/7051117800324071461