IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> Mysql与Redis双写一致性问题 -> 正文阅读

[大数据]Mysql与Redis双写一致性问题

在更新完数据库以后,必然需要对缓存中的键值对进行修改。而这个过程涉及到了各种各样的不一致性。大致有以下四种双写策略:

(1)先更新缓存,再更新数据库;
(2)先更新数据库,再更新缓存;
(3)先删除缓存,再更新数据库;
(4)先更新数据库,再删除缓存;

(1)先更新缓存,再更新数据库

这种策略会涉及到几个问题:

问题一:如果缓存更新成功,而数据库抽风了,因为各种原因更新失败,此时出现缓存与数据库的数据不一致;

问题二:假设有两个线程A和B,都对数据库进行修改,但执行顺序如下:(1)A对缓存进行更新;(2)B对缓存进行更新;(3)B对数据库进行更新;(4)A对数据库进行更新。此时发现缓存的数据是B线程写的,而数据库中的数据是A更新的。由于网络原因,这类现象在高并发场景下很可能出现,因此也会造成不一致。

(2)先更新数据库,再更新缓存

这种策略也会涉及到(1)中ABBA的问题:

问题一:假设有两个线程A和B,都对数据库进行修改,但执行顺序如下:(1)A对数据库进行更新;(2)B对数据库进行更新;(3)B对缓存进行更新;(4)A对缓存进行更新。此时发现数据库的数据是B线程写的,而缓存中的数据是A更新的。

问题二:如果在写数据库较多的业务场景下,这种方式会频繁的向缓存中写入。如果写入数据库的值,要经过复杂运算才能计算出写入缓存中的值,无疑是浪费性能的。

(3)先删除缓存,再更新数据库

这种策略也会导致ABBA不一致的问题:

假设有一个更新线程A,和一个查询线程B,进行了如下操作:(1)A进行写操作,删除了缓存中的数据;(2)B读缓存,发现没有;(3)B去数据库中找到了旧值,并将旧值写入缓存;(4)A对数据库进行更新。此时缓存与数据库又不一致了。

为了解决上述问题,可以采用延迟双删的策略:

public void write(String key,Object data){
        redis.delKey(key);
        db.updateData(data);
        Thread.sleep(1000);
        redis.delKey(key);
    }

可以看到,一个写操作在更新完数据库后,sleep一段时间,再对缓存中的数据(高并发下很可能是脏数据)进行删除。

延迟一段时间,是因为要确保读到脏数据的请求结束,来删除写入缓存的脏数据。这个时间的设置要根据读业务逻辑的时间,在此基础上加几十ms。

如果数据库采用读写分离的架构,那么有可能A已经在主库中写好了,但还没有同步到从库上。而B此时读了从库上的数据,也会造成数据不一致。此时可以在主从同步时间的基础上,加个几百ms。

但是最大的问题就是,第二次删除可能会导致删除失败。关于这个问题的解决策略放在最后

(4)先更新数据库,再删除缓存

这种策略也可能涉及到ABBA的不一致问题:

假设A是读线程,B是写线程:(1)缓存中的键值对失效;(2)A线程读缓存发现没有,从数据库中读到一个旧值;(3)B将新值写入缓存;(4)B删缓存;(5)A将旧值写入到缓存。

但是上述写操作(3)比读操作(2)慢很多,所以发生上述顺序的概率较低。如果真的发生了,也可以采用延迟双删的策略,间隔一段时间后,再删一次缓存。

但是这种策略也面临着和上面一样的问题,最后一次删除失败咋整?

如何解决最后一次缓存删除失败

思想很简单,设计一个有保障的重试机制即可,以上述第四种策略为例,有下面两种方法:

方案一:
在这里插入图片描述
(1)更新数据库;(2)尝试删除缓存,失败;(3)将需要删除的key生产到一个消息队列;(4)业务代码自身消费要删除的key;(5)重复尝试删除,直到删除成功。

但上述方案一显然有悖业务的设计哲学,让业务代码变得冗余,不如另外起一个非业务代码来进行处理。

方案二:
在这里插入图片描述
binlog日志可以记录数据库的操作,通过一个程序来监听binlog的修改状况,以提取出操作的数据以及key。这样就可以利用非业务代码来重复地尝试删除缓存,实现了与业务代码的解耦。

参考:https://www.zhihu.com/search?type=content&q=redis%20mysql双写

  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2022-02-24 15:21:56  更:2022-02-24 15:22:04 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 11:04:50-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码