一、背景
只要用缓存,就可能涉及到Redis缓存和数据库存储双写,只要是双写,就一定会有数据一致性的问题,如何解决双写一致性是重要的问题。
双写不可能做到强一致性,即不可能做到两边同时被更新,所以我们的目标是为了保障最终一致性。
二、双写一致性的方法讨论
重要的写在前面:双检加锁
多线程情况下发生查询时,如果缓存里没数,这些请求都会打到数据库上,为了防止数据库承受太大的压力,需要有一种机制保障同时只有一个线程去查数据库,如果能查出来数,就写入到缓存里,其他线程查询缓存即可。这种机制就是双检加锁机制,即先检查缓存,缓存没数的话再加锁,进入锁后再检查一遍缓存。

2.1 先更新数据库,再更新缓存
异常问题1
1 先更新mysql的某商品的库存,当前商品的库存是100,更新为99个。 2 先更新mysql修改为99成功,然后更新redis。 3 此时假设异常出现,更新redis失败了,这导致mysql里面的库存是99而redis里面的还是100。 4 上述发生,会让数据库里面和缓存redis里面数据不一致,读到redis脏数据
异常问题2
【先更新数据库,再更新缓存】﹐A、B两个线程发起调用 【正常逻辑】 1 A update mysql 100 2 A update redis 100 3 B update mysql 80 4 B update redis 80 ============================= 【异常逻辑】 多线程环境下,A、B两个线程有快有慢,有前有后有并行 1 A update mysql 100 3 B update mysql 80 4 B update redis 80 2 A update redis 100 ============================= 最终结果,mysql和lredis数据不一致,o(T_T)o, mysql80,redis100
2.2 先更新缓存,再更新数据库
不推荐,业务上一般把MySQL作为底单数据库,保证最后解释
异常问题
[先更新缓存,再更新数据库],A、B两个线程发起调用 [正常逻辑] 1 A update redis 100 2 A update mysql 100 3 B update redis 80 4 B update mysql 80 ==================================== [异常逻辑]多线程环境下,A. B两个线程有快有慢有并行 A update redis 100 B update redis 80 B update mysq| 80 A update mysql 100 mysql100,redis80
2.3 先删除缓存,再更新数据库
异常问题
1 请求A进行写操作,删除redis缓存后,工作正在进行中,更新mysql..... A还么有彻底更新完mysql,还没commit 2 请求B开工查询,查询redis发现缓存不存在(被A从redis中删除了) 3 请求B继续,去数据库查询得到了mysq中的旧值(A还没有更新完) 4 请求B将旧值写回redis缓存 5 请求A将新值写入mysql数据库
2.4 先更新数据库,再删除缓存
目前业界用的比较多的是这种方法
这种方法存在的异常就是数据库更新完后,缓存还没来得及删除,另一个线程过来读缓存时会读到老的数据。但是下次来读时就会拿到新数据了,可以保障最终一致性。
2.5 监听binlog来删除缓存
上述策略是在写数据库和更新缓存发生在同一程序中的,但很多时候我们缓存在redis里的数据并不是由我们自己写入到mysql的,此时我们为了感知到mysql中数据变化,需要监听mysql的binlog。流程如下


2112

被折叠的 条评论
为什么被折叠?



