Redis常见问题探析

1.Redis脑裂问题

1.什么是集群脑裂 

 Redis的集群脑裂是指因为网络问题,导致Redis Master节点跟Redis slave节点和Sentinel集群处于不同的网络分区,此时因为sentinel集群无法感知到master的存在,所以将slave节点提升为master节点。

注意:

此时存在两个不同的master节点,就像一个大脑分裂成了两个。集群脑裂问题中,如果客户端还在基于原来的master节点 继续写入数据,那么新的Master节点将无法同步这些数据,当网络问题解决之后,sentinel集群将原先的Master节点降为slave节点,此时再从新的master中同步数据,将会造成大量的数据丢失。

2.解决方案

 redis.conf配置参数:

min-replicas-to-write 1
min-replicas-max-lag 5

参数:

  • 第一个参数表示最少的slave节点为1个

  • 第二个参数表示数据复制和同步的延迟不能超过5秒

 2.Redis缓存预热问题

1.缓存冷启动 

 缓存中没有数据,由于缓存冷启动一点数据都没有,如果直接就对外提供服务了,那么并发量上来Mysql就裸奔挂掉了。

 

 2.解决思路

  • 提前给redis中灌入部分数据,再提供服务

  • 如果数据量非常大,就不可能将所有数据都写入redis,因为数据量太大了,第一是因为耗费的时间太长了,第二根本redis容纳不下所有的数据

  • 需要根据当天的具体访问情况,实时统计出访问频率较高的热数据

  • 然后将访问频率较高的热数据写入redis中,肯定是热数据也比较多,我们也得多个服务并行读取数据去写,并行的分布式的缓存预热

 3.Redis缓存穿透问题

 缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。

1.解决方案 

  • 对空值缓存:如果一个查询返回的数据为空(不管数据是否存在),我们仍然把这个空结果缓存,设置空结果的过期时间会很短,最长不超过5分钟。

  • 布隆过滤器:如果想判断一个元素是不是在一个集合里,一般想到的是将集合中所有元素保存起来,然后通过比较确定。

2.布隆过滤器 

 布隆过滤器是一种数据结构,比较巧妙的概率型数据结构(probabilistic data structure),特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”。

注意:

布隆说不存在一定不存在,布隆说存在你要小心了,它有可能不存在

使用布隆过滤器必须引入依赖

<dependency>
   <groupId>cn.hutool</groupId>
   <artifactId>hutool-all</artifactId>
   <version>5.7.17</version>
</dependency>

 4.Redis缓存击穿问题

 某一个热点 key,在缓存过期的一瞬间,同时有大量的请求打进来,由于此时缓存过期了,所以请求最终都会走到数据库,造成瞬时数据库请求量大、压力骤增,甚至可能打垮数据库。

 1.解决方案

  • 互斥锁:在并发的多个请求中,只有第一个请求线程能拿到锁并执行数据库查询操作,其他的线程拿不到锁就阻塞等着,等到第一个线程将数据写入缓存后,其他线程直接查询缓存。

  • 热点数据不过期:直接将缓存设置为不过期,然后由定时任务去异步加载数据,更新缓存(不咋靠谱)

2.代码实现 

 理念是对的,但是满足不了的企业的实际需求

public String get(String key) throws InterruptedException {
       String value = jedis.get(key);
       // 缓存过期
       if (value == null){
           // 设置3分钟超时,防止删除操作失败的时候 下一次缓存不能load db
           Long setnx = jedis.setnx(key +"mutex", "1");           
           jedis.pexpire(key + "mutex", 3 *60);
           // 代表设置成功
           if (setnx == 1){
               // 数据库查询
               //value = db.get(key);
               //保存缓存
               jedis.setex(key,3*60,"");
               jedis.del(key + "mutex");
               return value;
           }else {
               // 这个时候代表同时操作的其他线程已经load db并设置缓存了。 需要重新重新获取缓存
               Thread.sleep(50);
               // 重试
               return get(key);
           }
       }else {
           return value;
       }
}

5.Redis缓存雪崩问题 

缓存雪崩是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB(数据库),DB瞬时压力过重雪崩。

1.解决方案 

  • 过期时间打散:既然是大量缓存集中失效,那最容易想到就是让他们不集中生效。可以给缓存的过期时间时加上一个随机值时间,使得每个 key 的过期时间分布开来,不会集中在同一时刻失效。

  • 热点数据不过期:该方式和缓存击穿一样,也是要着重考虑刷新的时间间隔和数据异常如何处理的情况。

  • 加互斥锁: 该方式和缓存击穿一样,按 key 维度加锁,对于同一个 key,只允许一个线程去计算,其他线程原地阻塞等待第一个线程的计算结果,然后直接走缓存即可

  • 开启二级缓存:让nginx作为一级缓存·,另外使用其它缓存工具作为二级缓存,让他们存相同的数据,存储时间不同,这样就解决了。

6.Redis数据一致性问题 

 缓存已经在项目中被广泛使用,在读取缓存方面,大家没啥疑问, 都是按照下图的流程来进行业务操作。

 

缓存说明:

从理论上来说,给缓存设置过期时间,是保证最终一致性的解决方案。

三种更新策略

  1. 先更新数据库,再更新缓存

  2. 先删除缓存,再更新数据库

  3. 先更新数据库,再删除缓存

 先更新数据库,再更新缓存

线程安全角度 ,同时有请求A和请求B进行更新操作,那么会出现

(1)线程A更新了数据库

(2)线程B更新了数据库

(3)线程B更新了缓存

(4)线程A更新了缓存

这就出现请求A更新缓存应该比请求B更新缓存早才对,但是因为网络等原因,B却比A更早更新了缓存。这就导致了脏数据,因此不考虑。

先删缓存,再更新数据库

该方案会导致不一致的原因是。同时有一个请求A进行更新操作,另一个请求B进行查询操作。那么会出现如下情形:

(1)请求A进行写操作,删除缓存

(2)请求B查询发现缓存不存在

(3)请求B去数据库查询得到旧值

(4)请求B将旧值写入缓存

(5)请求A将新值写入数据库

 注意: 该数据永远都是脏数据。

 先更新数据库,再延时删缓存

 

(1)请求A进行删除缓存,然后去更新数据库

(2) 请求B查询发现缓存不存在了

(3)请求B去数据库查询得到新值

(4)请求B或请求A将新值写入缓存

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值