文章目录
一、程序运行读取缓存流程
获取缓存流程及访问数据库流程。
对于先更新数据库、还是先更新缓存、后删除缓存之间的顺序存在不同,不同的顺序会出现不同的情况。
这些问题考虑的出发点的来源于redis、数据库双写一致性,和读取的数据是否准确。
二、redis、数据库双写一致性
一致性又分为强一致性和最终一致性。
强一致性:这个主要使用场景是高并发是需要注意,例如:双十一商品数量问题,这个就需要做到强一致性。就是做到实时更新数据。
最终一致性:这个对数据的实时准确性不做过高的要求,可以一段时间之后同步数据,只要保证最后的数据是准确的就行。
1、那么如何保持redis、数据库双写一致?
理论上来说,我们给缓存设置过期时间,只要数据库更新成功,不管缓存是否更新成功,或者删除失败。是可以保证数据最终一致性的。接下来的策略介绍是基于不设置缓存过期时间探讨。
那我们先来介绍一下缓存的更新策略。
1、先更新数据库、在更新缓存
2、先删除缓存、在更新数据库
3、先更新数据库、在删除缓存
1、先更新数据库、在更新缓存
这种策略不推荐,会出现脏读数据。
原因:
事务A在更新数据库未完成时(可能由于网络原因)
事务B更新数据,更新缓存
导致本来是事务A
脏读: 所谓的脏读,其实就是读到了别的事务回滚前的脏数据。比如事务A执行过程中修改了数据X,在未提交前,事务B读取了X,而事务A却回滚了,这样事务B就形成了脏读。
2、先删除缓存、在更新数据库
这种策略也有问题,先删除缓存,后删除数据库
如果缓存删除失败,更新数据库成功,那么事务B读取的就是旧数据,造成读取的数据不一致。
3、先更新数据库、在删除缓存
首先,先说一下。老外提出了一个缓存更新套路,名为《Cache-Aside pattern》。其中就指出
- 失效:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中。
- 命中:应用程序从cache中取数据,取到后返回。
- 更新:先把数据存到数据库中,成功后,再让缓存失效。
另外,知名社交网站facebook也在论文《Scaling Memcache at Facebook》中提出,他们用的也是先更新数据库,再删缓存的策略。
这种策略依旧无法解决并发问题。
原因:我们可以根据时间线判断,事务A中的缓存依然是旧值,导致别的事务获取的数据不准确。
不过这种情况很少出现,原因就是事务B中的更新数据库耗时比事务A中更新数据所耗费的时间还要短。那我们从数据库方面分析,如果你做的是读写分离。首先要明白为啥要做读写分析,是因为你读的操作耗时短,消耗的资源少。
那么我如何解决以上出现的问题?网络上有很多解决方法
大部分使用的延时双删的策略,
4、什么是延时双删除?
延时双删,就是我们先删除缓存,然后在更新数据库,更新完数据库后,在延时一会,在删除数据库。
那我们来解释一下为什么要采用延时双删,解决了什么问题?
1、事务A删除缓存、假如删除失败
2、事务A更新数据库
3、这是事务B进来了,它已更新数据库
4、这是事务A完成了更新数据,删除了缓存
5、事务B在事务A删除缓存后,又更新了缓存
那以上操作会带了什么结果,导致缓存中存在存储的是事务B中更新的数据。
我们最终目的是下次访问的时候读到的是事务A中更新的数据,那么现在我们读到的事务B中的数据,数据不准确。
**延时:**我们可以在2、4之间做一步操作,睡眠,让4操作比5操作后执行,这样就能保证在事务B执行完成后,事务A删除了事务B中的缓存,下一次请求的获取数据的时候就需要走“失效”。上已经解释了缓存更新套路(Cache-Aside pattern)
三、最终解决数据一致性问题
就像第二大点我们介绍的三种策略,想第二种,第三种,如果换成删除失败如何解决,第三种策略也给出了延时双删的策略,那么如果第二次删除换成失败,我们改如何解决?
1、先更新数据库、在更新缓存
2、先删除缓存、在更新数据库
3、先更新数据库、在删除缓存
1、在业务代码中消息队列
1、更新数据库
2、删除缓存失败
3、将删除失败的key发送至消息队列
4、消费消息队列中的信息,获取到未删除成的key
5、再次重新删除缓存。
这样就可以确保,我们每次请求获取到的数据都是准确的数据了。但是这个方案有一个缺点,就是和主线业务代耦合度太高,那么我们来看下第二种方案。
2、使用消息队列+订阅
这种方案比较第一种方案的优点在于,启动一个订阅程序去订阅数据库的binlog,获取到要操作的数据,在另外一个程序中去获取订阅程序传递过来的数据,然后通过利用消息队列去删除缓存,确保缓存会被删除。
ps:上述的订阅binlog程序在mysql中有现成的中间件叫canal,可以完成订阅binlog日志的功能。
去订阅数据库的binlog,获取到要操作的数据,在另外一个程序中去获取订阅程序传递过来的数据,然后通过利用消息队列去删除缓存,确保缓存会被删除。
ps:上述的订阅binlog程序在mysql中有现成的中间件叫canal,可以完成订阅binlog日志的功能。
文章编写参考了:https://blog.csdn.net/chen134225/article/details/81669047