转自公众号:捡蜗牛的小男孩前言在分布式系统中,当缓存和数据库同时存在时,如果有写操作,应该先操作数据库还是先操作缓存?先想一想,可能存在什么问题,再往下看。下面我描述了几个选项。缓存维护方案1假设有写(线程A)和读(线程B)操作,先操作缓存,再操作数据库,如下图1)线程A发起写操作,第一步delcache2)线程A先两步将新数据写入DB3)线程B发起读操作,cachemiss,4)线程B从DB获取最新数据5)请求B同时设置缓存时间这样看,没有问题。再看第二张流程图,如下:1)线程A发起写操作,第一步delcache2)此时线程B发起读操作,缓存未命中3)线程B继续读DB,读出一个旧数据4)然后旧数据放入缓存5)线程A写入最新数据OK,姜子,有问题,旧数据放入缓存,每次读取都是旧数据,缓存和数据与数据库数据不一致。缓存维护方案两次双写操作,先操作缓存,再操作数据库。1)线程A发起写操作,第一步设置缓存2)线程A第二步向DB写入新数据3)线程B发起写操作,设置缓存,4)线程B向DB写入新数据第二步这样看,也没什么不妥,但有时事与愿违,我们来看第二个流程图,如下:1)线程A发起写操作,第一步设置缓存2)线程B发起写操作,第一步设置缓存3)线程B写入thedatabaseToDB4)线程A写入数据库并在DB中执行后,缓存保存的是B操作后的数据,而数据库是A操作后的数据,缓存和数据库数据不一致。缓存维护方案三一写(线程A)一读(线程B)操作,先操作数据库,再操作缓存。1)线程A发起写操作,第一步写DB2)线程A第二步delcache3)线程B发起读操作,缓存未命中4)线程B从DB获取最新数据5)线程B同时设置缓存没有明显的并发问题,但有可能是第2步删除缓存失败。虽然概率比较小,但是比方案1和方案2要好,方案3在平时的工作中也是用的。综上所述比较,我们一般采用第三种方案,但是有什么办法可以完善第三种方案的缺点呢?缓存维护方案四这是方案三的改进方案,先操作数据库,再操作缓存。先看流程图:利用数据库的binlog异步淘汰key。以MySQL为例,可以使用阿里的canal将binlog日志集合发送到MQ队列,然后通过ACK机制对更新消息进行确认处理,同时删除缓存,保证一致性数据缓存。但是还有一个问题,如果是主从数据库呢?缓存维护方案五主从DB问题:由于主从DB同步存在延迟时间,如果删除缓存后,在数据同步到备库之前有请求,会从中读取脏数据备用数据库,如何解决呢?解决流程图如下:缓存维护总结综上所述,在分布式系统中,当缓存和数据库同时存在时,如果有写操作,则先操作数据库,再操作缓存.如下:(1)读取缓存中是否有相关数据(2)如果缓存中有相关数据值,则返回(3)如果缓存中没有相关数据,则从数据库中读取相关数据并返回放入缓存key->value,然后返回(4)如果有更新数据,先更新数据,再删除缓存(5)为了保证第四步删除缓存成功,使用binlog异步删除(6)如果是主从库,binglog是从从库取的如果库(7)是一主多从,每个从库都要收集binlog,然后删除缓存consumer收到最后的binlog数据。近期热点文章推荐:1.1000+Java面试题及答案(2021最新版)2.别再满脑子if/else了,试试策略模式,真香!!3.操!Java中xx≠null的新语法是什么?4、SpringBoot2.5发布,深色模式太炸了!5.《Java开发手册(嵩山版)》最新发布,赶快下载吧!感觉不错,别忘了点赞+转发!
