龙空技术网

高并发场景下的缓存+数据库双写不一致问题分析与解决方案设计

百科程序员 109

前言:

今天兄弟们对“数据库双写不一致”大约比较关切,我们都想要分析一些“数据库双写不一致”的相关文章。那么小编也在网上网罗了一些有关“数据库双写不一致””的相关文章,希望朋友们能喜欢,兄弟们快快来学习一下吧!

1.数据库与缓存更新与读取操作进行异步串行化

问题分析

数据发生了变更,先删除了缓存,然后要去修改数据库,此时还没修改

一个请求过来,去读缓存,发现缓存空了,去查询数据库,查到了修改前的旧数据,放到了缓存中

数据变更的程序完成了数据库的修改,数据库和缓存中的数据不一样了。

解决方案

更新数据的时候,根据数据的唯一标识,将操作路由之后,发送到一个jvm内部的队列中

读取数据的时候,如果发现数据不在缓存中,那么将重新读取数据+更新缓存的操作,根据唯一标识路由之后,也发送同一个jvm内部的队列中

一个队列对应一个工作线程

每个工作线程串行拿到对应的操作,然后一条一条地执行

这样的话,一个数据变更的操作,先执行删除缓存,然后再去更新数据库,但是还没完成更新

此时如果一个读请求过来,读到了空的缓存,那么可以先将缓存更新的请求发送到队列中,此时会在队列中积压,然后同步等待缓存更新完成

待那个队列对应的工作线程完成了上一个操作的数据库的修改之后,才会去执行下一个操作,也就是缓存更新的操作,此时会从数据库中读取最新的值,然后写入缓存中

如果请求还在等待时间范围内,不断轮询发现可以取到值了,那么就直接返回; 如果请求等待的时间超过一定时长,那么这一次直接从数据库中读取当前的旧值

2.先更新数据库,再异步删缓存(利用消息队列不断重试删除缓存)失效:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中。命中:应用程序从cache中取数据,取到后返回。更新:先把数据存到数据库中,成功后,再让缓存失效。

问题分析

假设这会有两个请求,一个请求A做查询操作,一个请求B做更新操作,那么会有如下情形产生

(1)缓存刚好失效

(2)请求A查询数据库,得一个旧值

(3)请求B将新值写入数据库

(4)请求B删除缓存

(5)请求A将查到的旧值写入缓存

解决方案

提供一个保障的重试机制即可,这里给出两套方案。

方案一:

(1)更新数据库数据;

(2)缓存因为种种问题删除失败

(3)将需要删除的key发送至消息队列

(4)自己消费消息,获得需要删除的key

(5)继续重试删除操作,直到成功

该方案有一个缺点,对业务线代码造成大量的侵入

方案二:

(1)更新数据库数据

(2)数据库会将操作信息写入binlog日志当中

(3)订阅程序提取出所需要的数据以及key

(4)另起一段非业务代码,获得该信息

(5)尝试删除缓存操作,发现删除失败

(6)将这些信息发送至消息队列

(7)重新从消息队列中获得该数据,重试操作。

备注说明:上述的订阅binlog程序在mysql中有现成的中间件叫canal,可以完成订阅binlog日志的功能。另外,重试机制,博主是采用的是消息队列的方式。如果对一致性要求不是很高,直接在程序中另起一个线程,每隔一段时间去重试即可,这些大家可以灵活自由发挥,只是提供一个思路。

标签: #数据库双写不一致