👑作者主页:Java冰激凌
什么是Redis双写一致性问题
所谓的双写一致问题是指,当有数据修改时,要保证缓存和数据都能一起及时的更新,否则就会出现双写不一致的问题
如何解决?
首先我们可以思考 如果要解决这个问题 无非要从两个大类来出发 无非就是 “修改” || “删除”
对应的方案如下
修改
1. 先修改数据库数据,再去修改Redis数据
2. 先修改Redis数据,再修改数据库数据
删除
1. 先更新数据库数据,再删除Redis数据
2. 先删除Redis数据,再更新数据库数据
然而,这些方法看似都是可以解决问题的,但是~ 因为我们的操作是分为了两步的,也就是代表 这并非是一个原子性的操作,所以可能会出现掉电的问题或者是服务器崩溃的问题
修改
1. 先修改数据库数据,再去修改Redis数据(掉电) -》 会造成数据双写不一致的问题
2. 先修改Redis数据,再修改数据库数据(掉电) -》 会造成数据双写不一致的问题
删除
1. 先更新数据库数据,再删除Redis数据(掉电) -》 会造成数据双写不一致的问题
2. 先删除Redis数据,再更新数据库数据(掉电) -》 不会造成不一致问题
具体分析一下
- 先修改数据库数据,然后在修改Redis数据中的时候发生了掉电,导致Redis中的数据是未发生修改的,所以当服务器再次重启的时候,就会造成Redis与数据库中的数据已不一致的问题
- 先修改Redis数据,再修改数据库数据中的时候发生了掉电,导致数据库中的数据是未更新的状态,这也造成了数据不一致的问题
- 先更新数据库数据,再删除Redis数据的时候发生了掉电,那么服务器重启后,Redis中存储的仍旧是旧数据,数据库中存储的是新数据,如此还是会导致数据不一致的问题
- 先删除Redis数据,再更新数据库数据的时候发生了掉电,此时重启服务器后,Redis中是没有数据的,所以客户端请求该数据的时候就会去数据库中查询,然后缓存到Redis中,此时是可以保证我们双写一致的,也就可以解决我们的数据双写不一致的问题
但是!!!
使用第四种方案仍旧存在问题的,在我加入并发一起去思考之后,发现了新的问题
假设我们现在有两个线程
首先线程1去删除缓存,在删除缓存后到达了下一个时间片,线程2发现Redis中并没有数据(因为此时数据已经被删除了)所以会直接去数据库中查询,那么此时线程2会得到旧值,然后准备将数据库中的数据更新到缓存中,此时很不巧,线程2的时间片用完了,然后线程1再次拿到了时间片,线程1继续执行更新数据库中的值,然后再去添加缓存到Redis中,然后线程1完成工作,放弃时间片,线程2拿到时间片后,最后讲旧值更新到缓存中,此时就出现了问题 最后的结果是 Redis中存储的仍旧是旧值,但是在数据库中存储的是新值,此时又再次导致了双写不一致的问题
解决方案 延迟双删
解决这个并发问题,可以使用延迟双删的策略,也就是需要删除两次缓存,并在第二次删除是需要稍微等一会儿再执行删除策略,这样能够尽量的避免并发问题
但是????
尊嘟假嘟,保证数据库和缓存的双写一致的最终方案是使用:消息队列 + 延迟双删策略
尊嘟 但是还有细节需要考虑
提一个问题
延迟双删是否可以完全解决双写一致问题呢?
答案:其实是不一定的,这样只能最大限度的去解决双写不一致的问题
因为CPU是操作系统做随机分配执行的,并且在并发中,存在了更多的不确定因素,这些种种原因导致了可能会出现更多的执行过程,但是我们可以肯定的是,以上的这些方案,可以解决98%以上的双写不一致的问题