Redis学习【八】 问题汇总

一、Redis数据过期策略和内存回收策略


     针对已经过期的数据Redis采用定期删除和延迟删除结合的策略,但是两者都有缺陷;由于定期检查所有的key是否过期会带来性能问题,因此定期删除策略使用的是随机抽查,另外在操作Key前会判断是否已经过期,如过期则立即删除;这样的策略会导致一些已经过期的key还堆积在内存里,使得redis server内存占用率居高不下,因此需要结合redis.conf中的maxmemory-policy配置使用,也就是当redis server的内存不足以写入新数据时的内存回收策略,
#1 noeviction:表示直接报错;
#2 allkeys-lur:表示在所有keys中根据LRU删除key;
#3 allkeys-random:表示在所有keys中随机删除key;
#4 volatile-lru/volatile-random/volatile-ttl用于当redis server既充当cache又当DB的时候,表示在设置了expire date的keys中进行删除,ttl表示删除拥有更早过期时间的key。

 

二、解决Redis缓存穿透和缓存雪崩问题


     缓存穿透和雪崩可以看做一个问题,只是严重程度不同;当一个请求到达redis之后发现没有对应的缓存数据,然后向DB发送数据请求,如果能获取到数据那问题就停留在了缓存穿透上,DB获取到的数据会缓存到redis上;如果DB中也没有对应的数据,并且当这样的请求达到一定数量级并且耗用完所有的DB资源,最终导致DB连接异常就出现了缓存雪崩问题。
解决缓存穿透问题的思路有下述几种,不管是否从DB中查找到对应的值(没有值就为null),都在redis中记录一条缓存记录;在Dao层维护一张BitMap,用bit记录对应的key是否有对应值,从而避免冗余的DB操作;后台线程专门用于更新即将过期的Redis数据,从而避免缓存穿透。
解决缓存雪崩问题的思路有下述几种,在DB Connection上添加互斥锁,这样当大量缓存请求失效的时候需要排队去DB请求数据;对设置了相同过期时间的数据设置一个随机值,避免数据集体失效;使用双缓存或者多层缓存策略,需要配合缓存预热。

 

2.1、什么是缓存雪崩?

如果缓存集中在一段时间内失效,发生大量的缓存穿透,所有的查询都落在数据库上,造成了缓存雪崩

由于原有缓存失效,新缓存未到期间所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机

2.2)、有什么解决方案来防止缓存雪崩?

1)加锁排队

mutex互斥锁解决,Redis的SETNX去set一个mutex key,当操作返回成功时,再进行加载数据库的操作并回设缓存,否则,就重试整个get缓存的方法

2)数据预热

缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题。用户直接查询事先被预热的缓存数据。可以通过缓存reload机制,预先去更新缓存,在即将发生大并发访问前手动触发加载缓存不同的key

3)双层缓存策略

C1为原始缓存,C2为拷贝缓存,C1失效时,可以访问C2,C1缓存失效时间设置为短期,C2设置为长期

4)定时更新缓存策略

实效性要求不高的缓存,容器启动初始化加载,采用定时任务更新或移除缓存

5)设置不同的过期时间,让缓存失效的时间点尽量均匀

 

三、缓存击穿

1)、什么是缓存击穿?

在平常高并发的系统中,大量的请求同时查询一个key时,此时这个key正好失效了,就会导致大量的请求都打到数据库上面去。这种现象我们称为缓存击穿

2)、会带来什么问题

会造成某一时刻数据库请求量过大,压力剧增

3)、如何解决

上面的现象是多个线程同时去查询数据库的这条数据,那么我们可以在第一个查询数据的请求上使用一个互斥锁来锁住它

其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后做缓存。后面的线程进来发现已经有缓存了,就直接走缓存

 

四、缓存穿透

1)、什么是缓存穿透?

缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,在缓存中找不到对应key的value,每次都要去数据库再查询一遍,然后返回空(相当于进行了两次无用的查询)。这样请求就绕过缓存直接查数据库

2)、有什么解决方案来防止缓存穿透?

1)缓存空值

如果一个查询返回的数据为空(不管是数据不存在,还是系统故障)我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过5分钟。通过这个设置的默认值存放到缓存,这样第二次到缓存中获取就有值了,而不会继续访问数据库

2)采用布隆过滤器BloomFilter

优势:占用内存空间很小,位存储;性能特别高,使用key的hash判断key存不存在

将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力

在缓存之前在加一层BloomFilter,在查询的时候先去BloomFilter去查询key是否存在,如果不存在就直接返回,存在再去查询缓存,缓存中没有再去查询数据库

五、常见的几种缓存模式

1、Cache Aside

应用在查询数据的时候,先从缓存Cache中读取数据,如果缓存中没有,则再从数据库中读取数据,得到数据库的数据之后,将这个数据也放到缓存Cache中

如果应用要更新某个数据,也是先去更新数据库中的数据,更新完成之后,则通过指令让缓存Cache中的数据失效。

1)这里为什么不让更新操作在写完数据库之后,紧接着去把缓存Cache中的数据也修改了呢?

主要是因为这样做的话,就有2个写操作的事件了,担心在并发的情况下会导致脏数据,举个例子:假如同时有2个请求,请求A和请求B,并发的执行。请求A是要去读数据,请求B是要去更新数据。初始状态缓存中是没有数据的,当请求A读到数据之后,准备往回写的时候,此刻,请求B正好要更新数据,更新完了数据库之后,又去把缓存更新了,那请求A再往缓存中写的就是旧数据了,属于脏数据

2)那么Cache Aside模式就没有脏数据问题了吗?

在极端情况下也可能会产生脏数据。例如,同时有2个请求,请求A和请求B,并发的执行。请求A是要去读数据,请求B是要去写数据。假如初始状态缓存中没有这个数据,那请求A发现缓存中没有数据,就会去数据库中读数据,读到了数据准备写回缓存中,就在这个时候,请求B是要去写数据的,请求B在写完数据库的数据之后,又去设置了缓存失效。这个时候,请求A由于在数据库中读到了之前的旧数据,开始往缓存中写数据了,此时写进入的就也是旧数据。那么最终就会导致,缓存中的数据与数据库的数据不一致,造成了脏数据

2、Read/Write Through

应用要读数据和更新数据都直接访问缓存服务

缓存服务同步地将数据更新到数据库

出现脏数据的概率较低,但是就强依赖缓存,对缓存服务的稳定性有较大要求

3、Write Behind模式

应用要读数据和更新数据都直接访问缓存服务

缓存服务异步地将数据更新到数据库(通过异步任务)

速度快,效率会非常高,但是数据的一致性比较差,还可能会有数据的丢失情况,实现逻辑也较为复杂

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值