分布式数据库缓存双写一致性方案深度优化(延迟双删+订阅canal)
2022-11-05 14:14:38 0 举报
讨论了各种数据库与缓存的双写一致性方案,逐步探讨,解析
作者其他创作
大纲/内容
写入timestamp版本号小于时,消息丢弃
写入
读请求
路由网关
三、缓存数据的更新策略(不足),解决方案一:1、出现该问题的原因在于我们的读请求对数据库的操作,跑到了更新数据库的操作之前去了,解决方案可以让操作保持串行化,解决上述问题:更新:在缓存和数据库更新前,新建一个库存服务,该服务新建多个内存队列。更新时,根据相同的商品id,进行hash取值,再对队列数量取模,路由到对应的队列中。读取:读取时按照相同的规则进行路由,将请求也发送到相同的队列中,此时会在队列中积压,然后等待缓存更新完成。 单个队列使用一个线程进行消费,实现消息消费的串行化,以此确保,不会出现数据不一致情况。
按数据id来路由读写操作
分布式缓存1GB LRU
补充2:双写一致性-订阅变更日志方案
第一步:我们新增一个缓存代理客户端,对缓存的操作进行统一代理封装;以此封装以后,旧数据覆盖缓存的问题依然存在;
第五步:MQ消息绝对有序时的吞吐量问题?缓存代理服务端,为了保证消息消费的顺序性,对MQ中每个分区的消费是单线程消费的,所以在这里吞吐量会有问题;
读取timestamp
Stp2:更新数据库
内存队列
后发起
删除缓存
用户请求
先发起
读取速度快&并发高
直接请求
缓存代理服务端
Stp1:更新数据库
读取
Cache
解决:数据新增版本号:1、我们在数据写入数据库时,由数据库生成一个类似timestamp的版本号,生成后该版本号需要持久化到数据库;2、在缓存中,也需要类似在原来的value上拼上这个timestamp;3、此时在数据库写入时,异步将新值以及版本号投递到MQ中,MQ消费到后对缓存进行写入,写入前需要读取原来的缓存值,如果当前版本大于原缓存中值的版本,数据才进行更新,否则将消息丢弃。以此,实现旧的数据再次投递到缓存进行更新时,版本号比新值的小,数据被丢弃,不进行更新;
缓存代理客户端
关系型数据库100GB
业务系统
写入自动timestamp
消息中间件RocketMQ
查询DB旧值
线程
Stp5:缓存命中
app
删除
更新结果
设置缓存旧值
线程2的写请求
MQ
更新DB
每个分区单线程有序消费
第六步:为了在缓存代理服务端提升消费者的吞吐量,可以在服务端开启多个内存队列:1、一个线程将消息分发到多个内存队列;2、每个内存队列绑定一个线程进行消费;3、同时为了确保消费没有丢失,需要将消费结果投递到再手动ACK到RocketMQ;4、对于多次尝试更新缓存失败的,消息投递到重试队列,多次重试成功的,投递到死信队列;5、在缓存代理服务端需要启动一个线程对死信队列数据进行处理;
DB
线程1的写请求
第二步:我们引入一个MQ,可以采用RocketMQ,缓存数据的写入操作,首先投递到MQ中,RocketMQ天然支持数据进行分区处理;数据按Key分区投递到MQ中,确保一个线程消费一个分区中的数据,实现对数据的有序消费
查询缓存
缓存数据更新结果
Stp4:更新缓存
线程C
一、缓存数据的更新策略:1、数据读取时,先读取缓存,如果缓存中没有数据,则读取数据库,读取数据库后,将值放入缓存;2、数据变更时,先删除缓存,再写入数据库;
重试队列死信队列
事务写入timestamp按Key分区
查询缓存旧值
Canal
投递
Redis
补充:缓存延迟双删延迟问题:
四、针对以上内存队列串行化解决方案,提出以下思考:针对上述方案提出的内存队列解决缓存一致性方案,在绝大多数情况下,完全可以解决数据在缓存和数据库之间的一致性问题,但是该方案依然存在以下问题,供探讨:1、内存队列中的数据,随时会丢失;2、该种同步阻塞模式串行化处理方案,内存队列中的消息会不断积压;3、业务系统自己需要去实现内存队列的管理,太麻烦;
每个分区一个线程有序消费
MySQL
手动ACK
第三步:第二步后依然存在一种情况,一个线程数据更新,它更新数据库的时,异步的将新值的缓存写入投递到MQ中,以后进行DB数据的更新。此时另一个线程来读取,还是会直接读取到数据库中修改完成前的旧值,读取后将旧的数据投递到MQ中,以此对缓存进行重新设值,此时会出现缓存中旧值覆盖新值的情况;
第四步:如何保证缓存代理客户端投递消息一定是可靠的被服务端消费到不丢消息的?建议使用RocketMQ的事务消息。
二、缓存数据的更新策略(不足):1、以上方案会存在当一个写请求在将缓存中数据删除后,去写数据库的过程中,此时如果有一个读请求过来,由于缓存中没有值所以会去读数据库,如果读取数据库的请求先执行完成返回,此时会去重新设置缓存,此时会将旧值设置到缓存中,造成数据的不一致;
BinLog
写入按Key分区
线程A
线程B
Stp3:更新缓存
处理死信队列
0 条评论
下一页