目录
1. 先删 Redis,再写 MySQL, 再删 Redis
3. 先写MySQL,通过BinLog,异步更新 Redis
好了先来个图,大概解释一下有哪些方法(这里给出的方法只是基于M/R读写删除相关的,其它中间件或者锁等方法,等俺下次补充):
我们来一个个的看,先说这个不实用的方案,之所以说它不实用并不是否定这个方法的作用,毕竟存在即合理嘛,只是说它们在一些特定的场景中才会发挥出作用。
不实用的方案
1. 先写 MySQL , 再写 Redis
相信大家从图中就可以看出为什么说这是一个不推荐的方法了,两个请求都是先写MySQL,再写Redis,再并发的场景下,就可能会发生MySQL中的数据更新了,但Redis中还是旧值的情况。值得注意的一点是,再这个场景下,对于读请求,一般的处理都是先读Redis,没有的话再去数据库中查询,但是这个读请求并不会更新Redis中的数据。即,读请求不会更新Redis。
2. 先写 Redis , 再写MySQL
和前一种的方法一样,这里就不多做解释了,看图想一想就会明白。同样是在高并发的场景下会出现一些问题。
3. 先删 Redis,再写 MySQL
因为更新操作可能耗时会比较久,所以在请求A 删除了缓存后,请求B的读请求可能会在这期间执行,而且查询操作还是很快的。剩下的看图就可以理解了。
实用的方案
1. 先删 Redis,再写 MySQL, 再删 Redis
这就是我们经常可用听到的“缓存双删”、“延迟双删除”等词。
这种方法看起来好像真的可以解决缓存不一致的问题,但是对于这个延迟删除缓存,我们到底该什么时间删呢?
以下有几个可供参考的方法:
对于第一个方法,可能是一个“好用”,但不实用的方法,这种只需要一个sleep xxx ms
,就能完成的功能,可不就是好用吗(doge)。说它不实用是因为它不可控,具体的就不说了。
然而,尽管存在这些不可控因素,定时任务仍然是实现延迟双删等策略的一种常见方法。毕竟哪有那么多的高并发的场景啊,还有就是我们程序员“懒”,功能实现了就行了
异步处理这个方法感觉是以上几个方法中,较好的,实现了功能的同时还保证了稳定。但是麻烦。。。因为需要添加一个新的组件。
下面是这个方法需要注意的几个点:
具体消息对列的使用,等之后再说吧。
对于第三种方法,我感觉还没第一种好,他的一下问题如下:
扯远了,回到正轨。。
2. 先写 MySQL , 再删 Redis
对于一些可以允许系统出现短时间不一致的场景还是可以使用的,但对于一些强一致的场景就不太适用了,比如秒杀、库存扣减等业务。
对于这个方法,还有一种特殊的场景,就是请求发出时,缓存刚好失效,可能会引发的错误。
实际上这种遇到的概率是非常小的,一是缓存刚好失效,二是请求B从数据库查到数据,且回写缓存的耗时,要比请求A写数据库且删除缓存的时间要长。这在实际开发中遇到的概率极低。
3. 先写MySQL,通过BinLog,异步更新 Redis
对于这个方法,会保证MySQL和Redis的最终一致性,但是如果中途请求B需要查询数据,如果缓存无数据,就直接查DB;如果缓存有数据,查涧的数据也会存在不一致的情况。
所以这个方案,是实现最终一致性的终极解决方案,但是不能保证实时性。
几种方案的对比
1. 先写 Redis , 后写MySQL
数据没有落到数据库中你们安心吗,hxd,万一数据库宕机了,我们只靠缓存那不是开玩笑吗,正式环境中使用这种方法,就要提桶跑路了
编辑
2. 先写 MySQL, 后写 Redis
对于并发量不大、不要求强一致的使用还是挺不错的,但缺点是怕Redis突然挂了。。
3. 先删 Redis, 再写 MySQL
呃,这个的话好像没什么可以用的场景,一般都没人考虑这种吧(如有,欢迎补充!!)
4. 先删 Redis,再写 MySQL ,再删 Redis
好,但想要优雅的话,还需要支出额外的维护成本(消息队列。。)。
5. 先写 MySQL, 再删 Redis
推荐,高并发场景适用。
6. 先写 MySQL,通过 BinLog 异步更新 Redis
说使用,没试过,但这种对异地容灾、数据汇总等场景可能会较好一些。
总结一下
对于实时性要求高的,可以使用“先写 MySQL, 再删 Redis”。
对于要求强一致的,可以使用“先写 MySQL,通过 BinLog 异步更新 Redis”。