十一)使用 Redis 存在的问题

部分参考mooc网课程

基本上使用 Redis 都会碰到一些问题,常见的也就几个。

◆缓存和数据库双写一致性问题
在这里插入图片描述
实际业务场景中是先更新(删除)缓存还是先更新数据库呢?
https://blog.csdn.net/qq_33999844/article/details/81531461

不管怎么选都会有问题。
先更新(删除)缓存再更新数据库容易产生脏数据,容易无法恢复(数据库没有被及时更新,后续是始终都是数据库中的旧数据,而且在缓存过期后数据库的旧值会去更新缓存,从而二者都使用旧值)。

因此,使用较多的是先更新数据库再删除缓存。
此时产生脏数据的概率较低,但是还是有一致性问题。在并发情况下,一个线程A在更新数据库还没有来得及删除缓存,故其他线程查到的依然是缓存中的旧值,,当然在后续线程A删除缓存后就不影响了,此时就是去数据库中取值并产生缓存,没有后续问题了。
这也有产生脏数据的情况,线程A执行读操作没有命中缓存,就去数据库读取数据,它还没有来得及放入缓存;然后线程B执行了写操作,写完数据库后会再让缓存失效;然后线程A再把老的数据放入缓存中,也就覆盖了线程B的操作。这种场景发生概率极低。
但是不管如何,即使是考虑一致性问题,我们都需要在缓存上设置过期时间,从而只影响一段时间的结果
解决这个问题,实际上有2种方式
1) 强一致性:通过@PC或者Paxos保证一致性;
2) 降低并发脏数据的概率,因为2PC太慢,Paxos台复杂,并通过为缓存设置过期时间保证最终一致性。这个实际使用更常见。

阿里那边提供的方式通过日志来保证一致性。还有一种解决方案是先更新缓存,然后再执行一个异步任务,异步的根据redis去更新mysql。

◆缓存雪崩问题
大量的请求打到数据库,导致数据库崩溃的现象。
大量的请求打到数据库,导致数据库崩溃的现象。

1) 系统预加载的缓存周期性失效,故建议对缓存施加不同的过期时间,错峰避免集中失效。
2) 缓存预热
3) 多级缓存
4) 服务降级

◆缓存击穿问题
缓存穿透:也叫缓存击穿。
某个key被高并发的访问,没有被命中。处于对容错性的考虑,会尝试从后端数据库中获取。从而导致大量的请求打到了数据库。而当该key对应的数据本身就是空的情况下(我理解是key对应value为null,所以正常逻辑下即使存到缓存读到的也是null,null的含义不明:到底是真的就是null还是缓存中没有存),这就导致了对数据库执行了过多不必要的查询操作,从而导致了巨大的访问压力。
解决方案:
1) 缓存空对象,对于空对象也缓存空,对于集合对象就缓存empty 集合,如果是单个对象就通过缓存字段标识来区分(我猜比如对于integer就缓存-1之类的吧)。这样避免请求穿透到后端数据库。这种方案比较简单,适合缓存命中不高,但是可能频繁更新的数据。
2) 单独过滤处理,所以所有可能为空的数据做单独处理,并在请求时做统一的拦截。这种方式实现比较复杂。适合更新不频繁的数据。
它不像缓存雪崩,是一种非常不正常的现象,甚至是恶意攻击。

◆缓存的并发竞争问题
在通常流程下,一个请求过来,先去缓存中查数据。如果没有查到就去数据库中查找并更新缓存。这个流程没有问题。但是在高并发情况下,可能多个请求实际请求的是同一个数据,如果都让去数据库中查找会对数据库性能产生影响,甚至雪崩。
另外,一个缓存key(对应的结果)如果被更新时,同时也可能有多个读请求,这也会产生一致性的问题。
解决方案:在缓存更新或者过期的情况时先加锁。当更新或者从数据库读取完成后再释放锁。其他请求只需要牺牲一定的等待时间就可以直接从缓存中拿到数据了。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/xiaohesdu/article/details/87906464