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

一、最终一致性

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

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

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

二、补偿机制

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

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

消息队列重试步骤:

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

三、延时双删

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

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

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

四、异步删除缓存

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

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

相关推荐
Hello.Reader1 小时前
Redis 延迟排查与优化全攻略
数据库·redis·缓存
简佐义的博客2 小时前
破解非模式物种GO/KEGG注释难题
开发语言·数据库·后端·oracle·golang
爬山算法2 小时前
MySQL(116)如何监控负载均衡状态?
数据库·mysql·负载均衡
老纪的技术唠嗑局4 小时前
OceanBase PoC 经验总结(二)—— AP 业务
数据库
阿里云大数据AI技术5 小时前
OpenSearch 视频 RAG 实践
数据库·人工智能·llm
m0_623955667 小时前
Oracle使用SQL一次性向表中插入多行数据
数据库·sql·oracle
阿蒙Amon8 小时前
C#读写文件:多种方式详解
开发语言·数据库·c#
东窗西篱梦8 小时前
Redis集群部署指南:高可用与分布式实践
数据库·redis·分布式
就是有点傻9 小时前
C#如何实现中英文快速切换
数据库·c#
1024小神10 小时前
hono框架绑定cloudflare的d1数据库操作步骤
数据库