缓存和数据库的一致性问题

缓存和数据库的双写一致性

参考:https://www.cnblogs.com/yanglang/p/9098661.html
前言

最近在实习,为了能在项目中写出简洁的代码,正在学习Google Guava这个工具包,看到了Cache这一块,就想起了这个面试常问的有关缓存和数据库的题,现在做一个温故知新,查阅了网络上相关文档,整理一下。

读取数据流程

读请求过来会首先请求缓存,如果缓存中存在数据,那么就将数据返回给客户端;否则,去请求数据库服务器,如果数据库中也没有数据,将空值返回给客户端;如果数据库中存在数据,将数据值写入缓存,将缓存中的值返回给客户端。

三种更新策略

• 先更新数据库,再更新缓存
• 先删缓存,再更新数据库
• 先更新数据库,再删除缓存

先更新数据库,再更新缓存

存在如下2个问题:

一、线程安全问题

线程A更新了数据库—>线程B更新了数据库—>线程B更新了缓存—>线程A更新了缓存。
此时,数据库中存在的数据是B中数据,缓存中存在数据为A中数据。

二、业务场景问题

写多读少的情况下,缓存频繁更新,浪费性能。
更新数据时,每次都需要先写入数据库,再随后写入缓存,浪费性能。

先删缓存,再更新数据库

请求A进行写操作,删除缓存—>请求B查询发现缓存中没有数据—>请求B查询数据库得到旧值—>请求B将旧值写入缓存—>请求A将新值写入数据库。
此时,数据库中和缓存数据不一致。

不设置过期时间的情况下,如何解决该问题。

采用延时双删策略。
步骤:先删缓存—>再写数据库—>延时,再删缓存。
注意:这里的关键就是这个延时时间如何选取。基本要求就是写数据的休眠时间要在读数据业务逻辑耗时的基础之上,加几百ms。

此时,出现了一个新的场景:使用mysql的读写分离架构

前提:读写分离,主库负责写,从库负责读。
流程:请求A写操作,删除缓存—>请求A将数据写入主库—>请求B查询缓存,缓存中无数据—>请求B去从库中查询,主从同步尚未完成,B获得旧值—>请求B将旧值写入缓存—>数据库完成主从同步,数据库中写入新值。
此时,数据库和缓存数据不一致。
如果使用延时双删,必须保证写入数据休眠时间大于主从同步的时间。

延时双删,吞吐量降低怎么办?

第二次删除,另起一个线程,异步删除。
第二次删除失败怎么办?
采用 先更新数据库,再删缓存
先更新数据库,再删缓存

以下场景会有问题:

缓存失效的情况下—>请求A查询数据库,得到旧值—>请求B将新值写入数据库—>请求B删除缓存—>请求A将查询的写入缓存。
此时,数据库和缓存数据不一致。
但是,这种情况发生的概率很小,原因是读数据速度远快于写操作。
该方案的并发问题如何解决:1.给缓存设置有效时间。2.异步延时双删。
仍然会存在,缓存删除失败。

解决方案:重试机制

方案一:(对业务代码存在大量侵入)
流程:更新数据库—>缓存删除失败—>将需要删除的key发送到消息队列—>自己消费消息,获得需要删除的key—>继续重试删除操作,直到成功。
缺点:会对业务代码造成侵入。
方案二:(避免对业务代码造成侵入)
流程:更新数据库—>数据库会将操作信息写入binlog日志当中—>订阅程序提取出所需要的数据已经key—>起另一段非业务代码,获得该信息—>尝试删除缓存,发现删除失败—>将信息发送到消息队列—>重新从消息队列中获得该数据,重试操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值