MySQL与Redis的默契协作:解析数据一致性难题与解决方案

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: MySQL与Redis的默契协作:解析数据一致性难题与解决方案


直接先抛一下结论:在满足实时性的条件下,不存在两者完全保存一致的方案,只有最终一致性方案。 根据网上的众多解决方案,总结出 6 种,直接看目录:

不好的方案

  1. 先写 MySQL,再写 Redis

图解说明:

  • 这是一副时序图,描述请求的先后调用顺序;
  • 橘黄色的线是请求 A,黑色的线是请求 B;
  • 橘黄色的文字,是 MySQL 和 Redis 最终不一致的数据;
  • 数据是从 10 更新为 11;
  • 后面所有的图,都是这个含义,不再赘述。

请求 A、B 都是先写 MySQL,然后再写 Redis,在高并发情况下,如果请求 A 在写 Redis 时卡了一会,请求 B 已经依次完成数据的更新,就会出现图中的问题。

这个图已经画的很清晰了,我就不用再去啰嗦了吧,不过这里有个前提,就是对于读请求,先去读 Redis,如果没有,再去读 DB,但是读请求不会再回写 Redis。 大白话说一下,就是读请求不会更新 Redis。

  1. 先写 Redis,再写 MySQL

同 “先写 MySQL,再写 Redis”,看图可秒懂。

  1. 先删除 Redis,再写 MySQL

这幅图和上面有些不一样,前面的请求 A 和 B 都是更新请求,这里的请求 A 是更新请求,但是请求 B 是读请求,且请求 B 的读请求会回写 Redis。

请求 A 先删除缓存,可能因为卡顿,数据一直没有更新到 MySQL,导致两者数据不一致。

这种情况出现的概率比较大,因为请求 A 更新 MySQL 可能耗时会比较长,而请求 B 的前两步都是查询,会非常快。

好的方案

  1. 先删除 Redis,再写 MySQL,再删除 Redis

对于 “先删除 Redis,再写 MySQL”,如果要解决最后的不一致问题,其实再对 Redis 重新删除即可,这个也是大家常说的 “缓存双删”。

为了便于大家看图,对于蓝色的文字,“删除缓存 10”必须在 “回写缓存 10” 后面,那如何才能保证一定是在后面呢?网上给出的第一个方案是,让请求 A 的最后一次删除,等待 500ms。

对于这种方案,看看就行,反正我是不会用,太 Low 了,风险也不可控。

那有没有更好的方案呢,我建议异步串行化删除,即删除请求入队列

异步删除对线上业务无影响,串行化处理保障并发情况下正确删除。

如果双删失败怎么办,网上有给 Redis 加一个缓存过期时间的方案,这个不敢苟同。个人建议整个重试机制,可以借助消息队列的重试机制,也可以自己整个表,记录重试次数,方法很多。

简单小结一下:

  • “缓存双删” 不要用无脑的 sleep 500 ms;
  • 通过消息队列的异步 & 串行,实现最后一次缓存删除;
  • 缓存删除失败,增加重试机制。
  1. 先写 MySQL,再删除 Redis

对于上面这种情况,对于第一次查询,请求 B 查询的数据是 10,但是 MySQL 的数据是 11,只存在这一次不一致的情况,对于不是强一致性要求的业务,可以容忍。(那什么情况下不能容忍呢,比如秒杀业务、库存服务等。)

当请求 B 进行第二次查询时,因为没有命中 Redis,会重新查一次 DB,然后再回写到 Reids。

这里需要满足 2 个条件:

  • 缓存刚好自动失效;
  • 请求 B 从数据库查出 10,回写缓存的耗时,比请求 A 写数据库,并且删除缓存的还长。

对于第二个条件,我们都知道更新 DB 肯定比查询耗时要长,所以出现这个情况的概率很小,同时满足上述条件的情况更小。

  1. 先写 MySQL,通过 Binlog,异步更新 Redis

这种方案,主要是监听 MySQL 的 Binlog,然后通过异步的方式,将数据更新到 Redis,这种方案有个前提,查询的请求,不会回写 Redis。

这个方案,会保证 MySQL 和 Redis 的最终一致性,但是如果中途请求 B 需要查询数据,如果缓存无数据,就直接查 DB;如果缓存有数据,查询的数据也会存在不一致的情况。

所以这个方案,是实现最终一致性的终极解决方案,但是不能保证实时性。

方案比较

我们对比上面讨论的 6 种方案:

  1. 先写 Redis,再写 MySQL
  • 这种方案,我肯定不会用,万一 DB 挂了,你把数据写到缓存,DB 无数据,这个是灾难性的;
  • 我之前也见同学这么用过,如果写 DB 失败,对 Redis 进行逆操作,那如果逆操作失败呢,是不是还要搞个重试?
  1. 先写 MySQL,再写 Redis
  • 对于并发量、一致性要求不高的项目,很多就是这么用的,我之前也经常这么搞,但是不建议这么做;
  • 当 Redis 瞬间不可用的情况,需要报警出来,然后线下处理。
  1. 先删除 Redis,再写 MySQL
  • 这种方式,我还真没用过,直接忽略吧。
  1. 先删除 Redis,再写 MySQL,再删除 Redis
  • 这种方式虽然可行,但是感觉好复杂,还要搞个消息队列去异步删除 Redis。
  1. 先写 MySQL,再删除 Redis
  • 比较推荐这种方式,删除 Redis 如果失败,可以再多重试几次,否则报警出来;
  • 这个方案,是实时性中最好的方案,在一些高并发场景中,推荐这种。
  1. 先写 MySQL,通过 Binlog,异步更新 Redis
  • 对于异地容灾、数据汇总等,建议会用这种方式,比如 binlog + kafka,数据的一致性也可以达到秒级;
  • 纯粹的高并发场景,不建议用这种方案,比如抢购、秒杀等。

个人结论:

  • 实时一致性方案:采用 “先写 MySQL,再删除 Redis” 的策略,这种情况虽然也会存在两者不一致,但是需要满足的条件有点苛刻,所以是满足实时性条件下,能尽量满足一致性的最优解。
  • 最终一致性方案:采用 “先写 MySQL,通过 Binlog,异步更新 Redis”,可以通过 Binlog,结合消息队列异步更新 Redis,是最终一致性的最优解。


相关文章
|
2天前
|
SQL Kubernetes 关系型数据库
实时计算 Flink版产品使用合集之怎么解析 MySQL DDL 语句
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
17 2
|
4天前
|
缓存 NoSQL 关系型数据库
【Redis】Redis 缓存重点解析
【Redis】Redis 缓存重点解析
13 0
|
4天前
|
SQL 监控 关系型数据库
MySQL 如何保证主备的数据一致性的?
MySQL通过使用主从复制(Master-Slave Replication)来实现主备的数据一致性。主从复制是一种常见的数据复制技术,它将一个MySQL数据库服务器(主服务器)的数据复制到一个或多个其他MySQL数据库服务器(从服务器),以实现数据的冗余备份、读写分离等目的。以下是MySQL保证主备数据一致性的一些关键点: 1. **二进制日志(Binary Log)**:主服务器将所有的数据更改操作(如INSERT、UPDATE、DELETE)以二进制日志的形式记录下来,并定期将这些日志发送给从服务器。从服务器收到二进制日志后,按照主服务器的执行顺序逐条应用这些日志,从而保持数据的一致性
|
4天前
|
负载均衡 关系型数据库 MySQL
MySQL读写分离技术深度解析
在高并发、大数据量的互联网应用环境中,数据库作为数据存储的核心组件,其性能直接影响着整个系统的运行效率。MySQL作为最常用的开源关系型数据库之一,虽然功能强大,但在处理大量并发读写请求时,单点服务器的性能瓶颈逐渐显现。为了解决这一问题,MySQL读写分离技术应运而生,成为提升数据库性能、实现负载均衡的有效手段。
|
4天前
|
缓存 NoSQL 搜索推荐
Redis缓存雪崩穿透等解决方案
本文讨论了缓存使用中可能出现的问题及其解决方案。首先,缓存穿透是指查询数据库中不存在的数据,导致请求频繁到达数据库。解决方法包括数据校验、缓存空值和使用BloomFilter。其次,缓存击穿是大量请求同一失效缓存项,可采取监控、限流或加锁策略。再者,缓存雪崩是大量缓存同时失效,引发数据库压力。应对措施是避免同一失效时间,分散缓存过期。接着,文章介绍了Spring Boot中Redis缓存的配置,包括缓存null值以防止穿透,并展示了自定义缓存过期时间的实现,以避免雪崩效应。最后,提供了在`application.yml`中配置不同缓存项的个性化过期时间的方法。
|
4天前
|
缓存 NoSQL 搜索推荐
Redis缓存雪崩穿透等解决方案
本文讨论了缓存使用中的三个问题:缓存穿透、缓存击穿和缓存雪崩。为解决这些问题,提出了相应策略。对于缓存穿透,建议数据校验和缓存空值;缓存击穿可采用监控扩容、服务限流或加锁机制;缓存雪崩则需避免大量缓存同时过期,可设置随机过期时间。此外,文章还介绍了Spring Boot中Redis缓存配置,包括全局设置及自定义缓存过期时间的方法。
|
4天前
|
缓存 NoSQL PHP
【PHP 开发专栏】Redis 作为 PHP 缓存的解决方案
【4月更文挑战第30天】本文探讨了Redis作为PHP缓存的优势,如高性能、丰富数据结构、数据持久化和分布式支持。通过安装配置Redis、选择PHP客户端、执行读写操作及制定缓存策略实现缓存。应用场景包括页面、数据和会话缓存。但需注意数据一致性、过期时间、容量和安全问题,以确保应用稳定和安全。Redis能有效提升PHP应用响应速度和处理能力。
|
4天前
|
缓存 NoSQL Java
Redis7的10大应用场景和案例解析
你在项目中使用 Redis 实现了什么应用场景,欢迎一起跟 V 哥讨论。同时也做个小调查,朋多少兄弟是需要了解 Redis 核心源码的,人多的话,下一篇 V 哥写 Redis7的源码分析,人少的话就算了,感谢。
|
4天前
|
关系型数据库 MySQL 数据库
【MySQL】:约束全解析
【MySQL】:约束全解析
25 0
|
4天前
|
分布式计算 DataWorks 关系型数据库
DataWorks产品使用合集之在DataWorks中,使用JSON解析函数将MySQL表中的字段解析成多个字段将这些字段写入到ODPS(MaxCompute)中如何解决
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。
29 3

推荐镜像

更多
http://www.vxiaotou.com