【Redis系列笔记】双写一致性

简介: 本文讨论了缓存不一致问题及其后果,如价格显示错误和订单计算错误。问题主要源于并发和双写操作的异常。解决方案包括使用分布式锁(但可能导致性能下降和复杂性增加)、延迟双删策略(通过延迟删除缓存来等待数据同步)以及异步同步方法,如通过Canal和MQ实现数据的最终一致性。面试中,可以提及这些策略来确保数据库和缓存数据的一致性。

1. 概念

缓存不一致问题是指当发生数据变更后该数据在数据库和缓存中是不一致的,此时查询缓存得到的并不是与数据库一致的数据。

1.1. 缓存不一致会导致什么后果?

比如:查看商品信息的价格与真实价格不一致,影响用户体验,如果直接使用缓存中的价格去计算订单金额更会导致计算结果错误。

造成缓存不一致的原因可能是在写数据库和写缓存两步存在异常,也可能是并发所导致。

写数据库和写缓存导致不一致称为双写不一致,比如:先更新数据库成功了,更新缓存时失败了,最终导致不一致。并发导致缓存不一致举例如下:

执行流程(后来的线程先执行完):

  • 线程1先写入数据库X,当去写入缓存X时网络卡顿
  • 线程2先写入数据库Y
  • 线程2再写入缓存Y
  • 线程1 写入缓存旧值X覆盖了新值Y

即使先写入缓存再写数据在并发环境也可能存在问题,如下图:

流程:

线程1先写入缓存X,当去写入数据库X时网络卡顿

线程2先写入缓存Y

线程2再写入数据库Y

线程1 写入数据库旧值X覆盖了新值Y

2. 解决方案

2.1. 使用分布式锁

流程:

  • 线程1申请分布式锁,拿到锁。此时其它线程无法获取同一把锁。
  • 线程1写数据库,写缓存,操作完成释放锁。
  • 线程2申请分布锁成功,写数据库,写缓存。
  • 对双写的操作每个线程顺序执行。

对操作异常问题仍需要解决:写数据库成功写缓存失败了,数据库需要回滚,此时就需要使用分布式事务组件。

使用分布式锁解决双写一致性不仅性能低下,复杂度增加。

2.2. 延迟双删

既然双写操作存在不一致,我们把写缓存改为删除缓存呢?

先写数据库再删除缓存,如果删除缓存失败了缓存也就不一致了,那我们改为:先删除缓存再写数据库,如下图:

执行流程:

  • 线程1删除缓存
  • 线程2读缓存发现没有数据此时查询数据库拿到旧数据写入缓存
  • 线程1写入数据库

即使线程1删除缓存、写数据库操作后线程2再去查询缓存也可能存在问题,如下图:

线程1向主数据库写,线程2向从数据库查询,流程如下:

  • 线程1删除缓存
  • 线程1向主数据库写,数据向从数据库同步
  • 线程2查询缓存没有数据,查询从数据库,得到旧数据
  • 线程2将旧数据写入缓存

解决上边的问题采用延迟双删:

线程1先删除缓存,再写入主数据库,延迟一定时间再删除缓存。

上图线程1的动作简化为下图:

延迟多长时间呢?

延迟主数据向从数据库同步的时间间隔,如果延迟时间设置不合理也会导致数据不一致。

延迟时间设置的不合理,可能会带来更多问题

  • 过短:脏数据
  • 过长:更新失效不及时

2.3. 异步同步

延迟双删的目的也是为了保证最终一致性,即允许缓存短暂不一致,最终保证一致性。

保证最终一致性的方案有很多,比如:通过MQ、Canal、定时任务都可以实现。

Canal是一个数据同步工具,读取MySQL的binlog日志拿到更新的数据,再通过MQ发送给异步同步程序,最终由异步同步程序写到redis。此方案适用于对数据实时性有一定要求的场景。

通过Canal加MQ异步任务方式流程如下:

流程如下:

  • 线程1写数据库
  • canal读取binlog日志,将数据变化日志写入mq
  • 同步程序监听mq接收到数据变化的消息
  • 同步程序解析消息内容写入redis,写入redis成功正常消费完成,消息从mq删除。

定时任务方式流程如下:

专门启动一个数据同步任务定时读取数据同步到redis,此方式适用于对数据实时性要求不强更新不频繁的数据。

线程1写入数据库(业务数据表,变化日志表)

同步程序读取数据库(变化日志表),根据变化日志内容写入redis,同步完成删除变化日志。

3. 面试话术

3.1. 如何实现数据库与缓存数据一致?

实现方案有下面几种:

  1. 本地缓存同步:当前微服务的数据库数据与缓存数据同步,可以直接在数据库修改时加入对Redis的修改逻辑,保证一致。
  2. 跨服务缓存同步:服务A调用了服务B,并对查询结果缓存。服务B数据库修改,可以通过MQ通知服务A,服务A修改Redis缓存数据(推荐)
  3. 通用方案:使用Canal框架,伪装成MySQL的salve节点,监听MySQL的binLog变化,然后修改Redis缓存数据
目录
相关文章
|
2月前
|
缓存 NoSQL 关系型数据库
MySQL 与 Redis 如何保证双写一致性?
我是小假 期待与你的下一次相遇 ~
396 7
|
canal 缓存 NoSQL
Redis缓存与数据库如何保证一致性?同步删除+延时双删+异步监听+多重保障方案
根据对一致性的要求程度,提出多种解决方案:同步删除、同步删除+可靠消息、延时双删、异步监听+可靠消息、多重保障方案
Redis缓存与数据库如何保证一致性?同步删除+延时双删+异步监听+多重保障方案
|
存储 缓存 NoSQL
Redis常见面试题(二):redis分布式锁、redisson、主从一致性、Redlock红锁;Redis集群、主从复制,哨兵模式,分片集群;Redis为什么这么快,I/O多路复用模型
redis分布式锁、redisson、可重入、主从一致性、WatchDog、Redlock红锁、zookeeper;Redis集群、主从复制,全量同步、增量同步;哨兵,分片集群,Redis为什么这么快,I/O多路复用模型——用户空间和内核空间、阻塞IO、非阻塞IO、IO多路复用,Redis网络模型
Redis常见面试题(二):redis分布式锁、redisson、主从一致性、Redlock红锁;Redis集群、主从复制,哨兵模式,分片集群;Redis为什么这么快,I/O多路复用模型
|
9月前
|
消息中间件 缓存 NoSQL
缓存与数据库的一致性方案,Redis与Mysql一致性方案,大厂P8的终极方案(图解+秒懂+史上最全)
缓存与数据库的一致性方案,Redis与Mysql一致性方案,大厂P8的终极方案(图解+秒懂+史上最全)
|
NoSQL Java 关系型数据库
【Redis系列笔记】分布式锁
分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁。 分布式锁的核心思想就是让大家都使用同一把锁,只要大家使用的是同一把锁,那么我们就能锁住线程,不让线程进行,让程序串行执行,这就是分布式锁的核心思路
1468 2
|
canal 缓存 NoSQL
Redis常见面试题(一):Redis使用场景,缓存、分布式锁;缓存穿透、缓存击穿、缓存雪崩;双写一致,Canal,Redis持久化,数据过期策略,数据淘汰策略
Redis使用场景,缓存、分布式锁;缓存穿透、缓存击穿、缓存雪崩;先删除缓存还是先修改数据库,双写一致,Canal,Redis持久化,数据过期策略,数据淘汰策略
Redis常见面试题(一):Redis使用场景,缓存、分布式锁;缓存穿透、缓存击穿、缓存雪崩;双写一致,Canal,Redis持久化,数据过期策略,数据淘汰策略
|
缓存 NoSQL 关系型数据库
MySQL与Redis缓存一致性的实现与挑战
在现代软件开发中,MySQL作为关系型数据库管理系统,广泛应用于数据存储;而Redis则以其高性能的内存数据结构存储特性,常被用作缓存层来提升数据访问速度。然而,当MySQL与Redis结合使用时,确保两者之间的数据一致性成为了一个重要且复杂的挑战。本文将从技术角度分享MySQL与Redis缓存一致性的实现方法及其面临的挑战。
390 2
|
存储 缓存 NoSQL
Redis问题之一致性Hash是如何解决哈希+取余方法中的稳定性问题的
Redis问题之一致性Hash是如何解决哈希+取余方法中的稳定性问题的
211 10
|
缓存 NoSQL 数据库
Redis问题之在高并发场景下,保证Redis缓存和数据库的一致性如何解决
Redis问题之在高并发场景下,保证Redis缓存和数据库的一致性如何解决
382 3
|
NoSQL 关系型数据库 MySQL
实时计算 Flink版产品使用问题之如何确保多并发sink同时更新Redis值时,数据能按事件时间有序地更新并且保持一致性
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。

相关产品

  • 云数据库 Tair(兼容 Redis)