缓存和数据库数据一致性解决方案

一、最终一致性

在前文缓存更新中讲到,更新缓存较好的一种方式是使用 Cache Aside 模式,即先更新数据库,再失效缓存。但是若更新数据库成功,失效缓存由于服务异常、系统异常、网络异常等原因失败,则会导致数据不一致。

根据CAP理论,完美的一致性和可用性难以同时满足,通常会选择保证A和P,接受最终一致性。最终一致性强调的是系统中所有的数据副本,在经过一段时间的同步后,最终能够达到一致的状态。

使用缓存最主要的原因是追求数据访问速度,而如果要追求强一致性,按照目前的技术,必然会损失访问速度这一优势,因此在需要保证数据强一致性的场景下,根本不适合使用缓存。如果选择使用缓存,则必然基于一个重要前提,即可接受弱一致性或最终一致性。

二、补偿机制

在操作失败后,通过保存足够的信息可进行补偿操作,实现最终一致性。常用的补偿机制:

  • 同步重试:操作失败后在当前线程中继续重试,直至成功或达到重试次数上限。缺点是可靠性不足,如缓存服务短期不可用,则重试可能不成功
  • 异步重试:将重试任务交给线程池处理,缺点是可靠性不足,如发生服务器重启等异常,则无法补偿
  • 数据库重试:将重试数据记录到数据库,由业务系统定时任务进行重试。可靠性高,但实现成本高,对业务系统侵入严重
  • 消息队列重试(推荐):将重试数据发给消息队列,由业务系统自行消费并进行重试。该方案在可靠性、系统侵入、响应时延、达到最终一致性的速度等方面可以取的折中

消息队列重试步骤:

  1. 更新数据库
  2. 删除缓存
  3. 若失败,将需要删除的key发送给消息队列
  4. 业务系统接收到消息,根据消息内容重试删除操作

三、延时双删

Cache Aside模式可能会存在一种概率极低的读写时序问题:读操作缓存未命中,同时伴随一个并发写操作;读操作在写操作完成数据库更新前读取了旧数据,且在写操作删除缓存后更新了缓存,从而导致数据库中的为最新数据,而缓存中的仍为旧数据

延时双删 方案可以解决这一问题:删除缓存、更新数据库、休眠一段时间、再次删除缓存

延时双删实现简单,但是因延迟等待会影响写请求性能,可以通过异步删除缓存来解决,典型的方案有:基于binlog 删除缓存和消费消息删除缓存

四、异步删除缓存

  • 基于binlog异步删除缓存:通过Canal监听binlog变更,当识别到涉及缓存的数据更新操作时,将对应缓存删除
  • 消息队列异步删除缓存:Canal收到消息后,将binlog数据直接投递到MQ,从而实现多个客户端同时消费数据。删除缓存成功后,通过MQ的ACK机制确定消费成功

该方式实现复杂,一般不会使用,绝大多数场景 Cache Aside + 自动过期 + 失败补偿 策略已能够满足要求

相关推荐
搬码后生仔2 分钟前
SQLite 是一个轻量级的嵌入式数据库,不需要安装服务器,直接使用文件即可。
数据库·sqlite
码农君莫笑4 分钟前
Blazor项目中使用EF读写 SQLite 数据库
linux·数据库·sqlite·c#·.netcore·人机交互·visual studio
江上挽风&sty5 分钟前
【Django篇】--动手实践Django基础知识
数据库·django·sqlite
向阳12189 分钟前
mybatis 动态 SQL
数据库·sql·mybatis
胡图蛋.11 分钟前
什么是事务
数据库
小黄人软件13 分钟前
20241220流水的日报 mysql的between可以用于字符串 sql 所有老日期的,保留最新日期
数据库·sql·mysql
张声录118 分钟前
【ETCD】【实操篇(三)】【ETCDCTL】如何向集群中写入数据
数据库·chrome·etcd
无为之士24 分钟前
Linux自动备份Mysql数据库
linux·数据库·mysql
小汤猿人类37 分钟前
open Feign 连接池(性能提升)
数据库
阳冬园1 小时前
mysql数据库 主从同步
数据库·主从同步