如果我们更新了mysql中的数据,那我们在缓存(如redis)中拿到的就是一个旧数据,这就牵扯到一个问题:缓存里面的数据如何和数据库保持一致?即缓存数据一致性
解决这一问题目前用的最多的两种模式:
- 双写模式
- 失效模式
双写模式
流程:如果我们使用了双写模式,则如果我们对db中的数据做了修改,比如修改了一个菜单,那么redis里面的数据要变,那我们在改完菜单以后同时将缓存中数据也改一下即可
当然如果我们更改了某个菜单,而缓存中缓存的是全部菜单,那我们在双写模式下就需要把所有菜单全部查一遍再往缓存中放,听着都很麻烦
双写模式在大并发下还会出现漏洞
双写模式问题如上图,模拟2个并发请求进来修改同一条数据,如修改id为5的菜单,第一个请求我们起名叫A,第二个请求我们起名叫B
A先执行,A先去修改数据库,把id为5的菜单的pid修改为1,数据库修改完后,A准备去改redis缓存,但是因为各种原因,比如cpu时间片轮转,没转到它这里,或者它卡顿了一下,反正它暂时停在了这里
此时B请求进来,它准备将id为5的菜单的pid改为2,然后它的写数据库操作和写缓存操作都咔的一下成功了
此时A请求才慢慢腾腾的5号菜单的pid改为1这个行为同步到缓存中,那这样缓存中就保存了一个旧的数据,因为id为5的菜单实际上它最后的pid应该是2不是1,这就相当于缓存中出现了一个脏数据
针对上面提到的脏数据的风险,如何解决该问题呢?
方案1:加锁
如我们并发写的场景,因为要写数据库,同时要改缓存,那我们就对整个操作加一个锁。如果1号先进来了,那1号得到锁, 1号把它的整个流程全执行完了,2号才能得到锁。写它的整个流程,这就不会产生数据不一致的问题
方案2:看我们业务允许不允许有暂时性的数据不一致问题(推荐)
如商品的菜单数据改了,比如京东在后台改了,5min或是20min或者1天以后我们首页展示的才是改完的数据,如果我们允许这个操作,那我们就不用管这个事情
不管的话怎么办?
给缓存的数据设置过期时间,如1天,然后到期自己删了,下一个请求将新的写进去,因此我们称之为暂时性的脏数据(目前我们公司用的就是这个)
无论我们怎么操作,写数据库,然后写缓存,缓存里面更新,我们再来读数据,读到最新数据肯定跟数据库刚存最新数据的那一刻有一段延迟时间,就看大家对这段延迟时间容忍有多大
如10秒的容忍,10秒完我必须看到新数据,还是我们容忍1天,还是1毫秒
我们可以将这个叫做最终一致性(数据库改后的值,到我们最终看到的值,他们之间有个大的延迟时间,但不论怎么延迟,我们最终都能看到数据库最新修改的值)
失效模式
流程:我们把数据库改完,直接将缓存删除掉,那这样做的好处是等待下一次主动查询来更新缓存即可
但是这种模式在大并发下仍然会出现问题,如下图
失效模式问题等待补充。。。
网友评论