前言

MySQL和Redis数据一致性算是个很经典的问题,在之前也看到过很多相关的文章,最近心血来潮,想把一致性问题的解决方案和存在问题都总结一下。

不推荐方案

1 先更新MySQL,再更新Redis。

如上图有两个请求要同时进行更新操作,在并发情况下,B请求虽然更新时间晚于A请求,但是可能因为网络延迟问题,导致本来A请求要先更新Redis的操作晚于B请求更新Redis的操作,最终导致了MySQL出现数据不一致。

2 先更新Redis,在更新MySQL。

这种情况其实等同于第一种情况。

3 先删除Redis缓存,再更新MySQL。

A请求对数据的更新操作晚于请求B的读取操作,导致B请求将数据库的旧值又写回缓存,删除缓存在这种情况下没有意义。

推荐方案

1 先删除Redis缓存,再更新MySQL,再删一次Redis缓存(延迟双删)

在第三种情况中,出现了删除缓存后被其他请求更新为旧值的情况,那么在这种情况下,再删除一遍缓存不就可以解决问题了。这里第二次删除缓存的时间必须在B请求回写旧值之后,所以要社招好第二次删除缓存的等待时间,根据业务实际耗时来定,假设B请求回写缓存要300ms,那么A请求可以设置等待500ms再进行缓存删除。

但是上面这种情况也会出现问题,比如延迟双删的时候删除缓存失败怎么办。

这个时候可以借助MQ重试机制。如下图:

将删除的请求放到MQ队列里面,然后系统再从MQ里面取出删除请求的操作,由于MQ支持失败重试,删除失败后会继续投递消息。

2 先更新MySQL,再删除Redis缓存。

在上面这种情况下,请求B出现了读取了一次旧值,如果对于业务是一致性要求没那么强的话(比如秒杀,减库存),这种方案也是可以的,误差范围是可以接收的,只存在这么一次数值是旧的情况。

当然还有特殊情况如下:

当B请求先查询Redis,这个时候redis刚好缓存失效,B请求就会去MySQL查询旧值,后续B请求回写旧值的请求又晚于A请求删除缓存的请求,导致缓存里面放的是旧值。

但是这种情况出现需要 同时满足以下两个条件:

(1)缓存刚好失效

(2)读请求回写缓存的时间晚于写请求回写缓存的时间

上述两个条件同时成立的概率是极小的,综上来说,这种方案还是不错的,复杂度也不高,但同时也是可能存在删除缓存失败的特殊情况导致误差。

3 先更新MySQL,通过 Binlog,异步更新 Redis

A请求更新完MySQL,借助Canal进行监听并把相关的修改记录推送到MQ,MQ经过消费系统拉取消息对Redis进行更新,如果在Redis更新之前,有新的读请求,依然会导致数据不一致性的问题,但是这种方案能够实现最终一致性。

在这里Canal作为一个组件,监听binlog和发送消息到MQ都由Canal完成。

方案总结

前三种方案都是不推荐使用的。对于推荐使用的方案,从实时性和技术复杂度来说,先写数据库再删除缓存是比较好的选择。如果要确保最终一致性的话,可以用binlog异步更新缓存的方案。

【经典问题】mysql和redis数据一致性问题的更多相关文章

  1. 【面试普通人VS高手系列】Redis和Mysql如何保证数据一致性

    今天分享一道一线互联网公司高频面试题. "Redis和Mysql如何保证数据一致性". 这个问题难倒了不少工作5年以上的程序员,难的不是问题本身,而是解决这个问题的思维模式. 下面 ...

  2. 电商中的库存管理实现-mysql与redis

        库存是电商系统的核心环节,如何做到不少卖,不超卖是库存关心的核心业务问题.业务量大时带来的问题是如何更快速的处理库存计算. 此处以最简模式来讨论库存设计. 以下内容只做分析,不能直接套用,欢迎 ...

  3. Mysql和Redis数据如何保持一致

    先阐明一下Mysql和Redis的关系:Mysql是数据库,用来持久化数据,一定程度上保证数据的可靠性:Redis是用来当缓存,用来提升数据访问的性能. 关于如何保证Mysql和Redis中的数据一致 ...

  4. linux安装和配置 mysql、redis 过程中遇到的问题记录

    linux下部署mysql和redis网上的教程很多,这里记录一下我部署.配置的过程中遇到的一些问题和解决办法. mysql ①安装完成后启动的时候报错 Starting MySQL.The serv ...

  5. Mysql与Redis的同步实践

    一.测试环境在Ubuntu kylin 14.04 64bit 已经安装Mysql.Redis.php.lib_mysqludf_json.so.Gearman. 点击这里查看测试数据库及表参考 本文 ...

  6. 通过Gearman实现MySQL到Redis的数据同步

    对于变化频率非常快的数据来说,如果还选择传统的静态缓存方式(Memocached.File System等)展示数据,可能在缓存的存取上会有很大的开销,并不能很好的满足需要,而Redis这样基于内存的 ...

  7. 一步完成 MySQL 向 Redis 迁移

    从mysql搬一个大表到redis中,你会发现在提取.转换或是载入一行数据时,速度慢的让你难以忍受.这里我就要告诉一个让你解脱的小技巧.使用“管道输出”的方式把mysql命令行产生的内容直接传递给re ...

  8. MySQL to Redis

    [TOC] 简介 使用mysql2redis可以非常便捷的将mysql中的数据导出到redis中去, 通常是需要一个select语句即可实现. 软件安装 // 安装apr + apr-util $ w ...

  9. Gearman + Nodejs + MySQL UDF异步实现 MySQL 到 Redis 的数据同步

    [TOC] 1, 环境 CentOS, MySQL, Redis, Nodejs 2, Redis简介 Redis是一个开源的K-V内存数据库,它的key可以是string/set/hash/list ...

  10. mysql常见优化,更多mysql,Redis,memcached等文章

    mysql常见优化 http://www.cnblogs.com/ggjucheng/archive/2012/11/07/2758058.html 更多mysql,Redis,memcached等文 ...

随机推荐

  1. Oracle中数据的约束

  2. ProcessingJS

    ProcessingJS 图形 rect(x, y, w, h)(在新窗口中打开) ellipse(x, y, w, h) triangle(x1, y1, x2, y2, x3, y3) line( ...

  3. 在 Rust 中 gRPC 使用的 protobuf 实现条件编译服务器和客户端(tonic)

    前言 Rust 中 gRPC 最优秀的库是 tonic.tonic-build 的默认生成方式是生成一个带有数据类型和客户端与服务端源码,而对于分层应用,客户端尽可能不要知道服务端的代码,同时服务端也 ...

  4. HDU4841 AHOI1999 圆桌问题 题解

    朴素的约瑟夫问题,用vector处理即可 #include <iostream> #include <vector> using namespace std; //AHOI19 ...

  5. Java面向对象(高级)

    1.类变量 类变量是被类的所有实例共享的. 类变量具体放的位置在哪?在内存中的那个区域,这和jdk的版本是有关的 静态变量在类加载的时候就生成了,即使没有创建类实例也能访问,当然通过实例来实现 类变量 ...

  6. Codeforces Round 856 (Div. 2)C

    C. Scoring Subsequences 思路:我们想要找到满足的最大值的长度最长的的区间,因为单调不减,所以更大的数一定在最大值的里面包含,所以我们用两个指针维护这样一个满足当前i的最大值区间 ...

  7. 【数字图像处理】Matlab实现-图像增强-灰度图像增强

    灰度图像增强的大致原理以及操作方法 面向作业编程 上图是大概对灰度图的增强处理 就是想让图片变得更好看 那么如何具体操作就看下面的代码了 这是一个简单的线性分段处理,相信在注释的帮助下应该能看懂. % ...

  8. `.NET Web`新人入门必学项目`EarthChat`

    .NET Web新人入门必学项目EarthChat EarthChat是一个基于.NET 7的实战项目,EarthChat提供了很多的最佳实践,EarthChat的目标也是成为一个很多人都喜欢的大型聊 ...

  9. [NOIP 考前备战] 线段树刷题

    备战线段树 T1 AcWing .1275. 最大数 查询最大值 + 单点修改 #include <bits/stdc++.h> #define int long long using n ...

  10. 二、RHEL8操作系统安装

      一.如何安装rhel的操作系统?   必要的前提条件:硬件(CPU.内存.硬盘--) + 安装介质(操作系统的安装文件)   会不会把自己的笔记本装成rhel的操作系统呢? 不会   使用虚拟机软 ...