如何保证缓存和 MySQL 的双写一致 ?

1. 如何保证缓存和 MySQL 的双写一致 ?

什么叫做如何保证缓存和 MySQL 双写一致,这个问题就是指当应用程序执行写(增删改)操作时,如何保证 Redis 和 MySQL 的数据一致性。

当用户发送请求时,程序的执行流程如下:

要保证 Redis 和 MySQL 的数据一致性,从大的角度来说,无非就两种思路:

1.修改

  • 先修改数据库,再修改 Redis
  • 先修改 Redis,再修改数据库

2.删除

  • 先删除 Redis,再操作数据库
  • 先操作数据库,再删除 Redis

这四种方式在正常情况下,都可以保证 Redis 和 MySQL 的数据一致性,但是在异常情况下,就只有一种方式才能真正保证 Redis 和 MySQL 的双写一致。

当上述方式,每个方案都是执行了前半部分后,主机掉电了 >>

① 先修改数据库,再修改 Redis(掉电)

这种方式,Redis 中是旧数据,而 MySQL 中是新数据,当应用程序访问 Redis 时,发现有数据,直接就返回给用户了,显然这种方式是不合理的。

② 先修改 Redis,再修改数据库(掉电)

这种方式,Redis 中是新数据,而 MySQL 中是旧数据,当应用程序访问 Redis 时,发现有数据,直接就返回给用户了,此时 Redis 和数据库中的数据也不一致,下一次 Redis 还需要从数据库中同步数据的,显然这种方式也不合理。

③ 先删除 Redis,再操作数据库(掉电)

这种方式,不管主机有没有掉电,Redis 始终没数据,始终 都是要从数据库中同步数据的,所以这种方式 Redis 和 MySQL 的数据一定是一致的。

④ 先操作数据库,再删除 Redis(掉电)

这种方式也是行不通的,操作完数据库,主机掉电,此时 Redis 中还是有数据的,而且是旧数据,而 MySQL 中的数据已经更新了,导致双写不一致。

【并发场景下的问题】

虽然先删除 Redis,再操作数据库,这种方式看似解决了 Redis 和 MySQL 中的双写一致问题,但是它的前提是要在单线程的情况下。

在多线程的场景下,由于操作系统的随机调度,可能会出现以下情况:

  1. 线程 1 执行完删除 Redis,然后时间片用完了,
  2. 线程 2 执行查询操作,由于线程 1 把 Redis 删除了,所以线程 2 拿到了数据库的旧数据,然后时间片用完了,
  3. 线程 1 继续执行更新数据库,并把新数据缓存至 Redis,执行结束,
  4. 线程 2 苏醒,将刚刚查到的数据,缓存一份至 Redis.

此时 Redis 和数据库中的数据又变成不一致了,那该怎办 ?

【解决办法】延时双删

在线程 2 执行完之后后,再删除一次 Redis,以便下次查询还得走数据库,此时拿到的就是最新的数据了。(此时线程 1 的缓存 Redis 就可以不必执行了)

如果在面试中碰到这个问题,回到到这里其实已经差不多了,如果遇到了刁钻一点的面试官,那么我们以上看似美好的推理过程,其实还是存在问题的。

面试官问:延时双删是否彻底解决了 Redis 和 MySQL 双写一致的问题 ?

答案是不一定,它只是最大限度的解决双写一致性问题。因为延时,延长的时间是固定的,而操作系统的调度是随机的,极端情况下,还是可能会存在线程 1 第二次删除操作 处在线程 2 的将旧数据缓存至 Redis这个操作之前。(极端情况下,线程 2 是个饥饿线程)

那这个问题该怎么完美解决 ?

这个问题我也问过一些大佬,他们给出的结论是从理论上来说,目前没有更好的解决方案,但是前面也说了只有极端情况下,连延时双删都解决不了,因为它发生的概率也是比较小的,所以可以忽略不计了,并且使用延时双删几乎可以解决 99.99% 的双写一致问题了。

如果硬要追求完美主义,可以给它设置一个过期时间,等到键值过期了,自然会把数据库中新的数据给保存到 Redis 中;

另外,对于主机掉电,另一半操作没有执行的问题,可以通过加入消息队里欸来解决(保证业务的完整性),消费者再消费任务的时候,即使掉电,下一次还是会继续消费的。

相关推荐
风123456789~15 分钟前
【Oracle专栏】跨服务器调用ORA-02064: 不支持分布式操作
数据库·oracle
瀚高PG实验室16 分钟前
PostgreSQL libpq lo_* 函数允许服务器超级用户覆盖客户端栈内存HGVE-2026-E011
服务器·数据库·postgresql·瀚高数据库
Feynman’s boom1 小时前
PDF解析复盘
数据库·人工智能·算法·pdf解析
程序员小八7779 小时前
MySQL 事务:一文把 ACID、隔离级别、MVCC、锁全串起来
数据库·mysql
lzhdim9 小时前
提高 SQL 语句执行速度的方法
java·开发语言·数据库·sql·oracle
ltl9 小时前
ClickHouse Distributed 引擎与分布式查询路由
数据库
Wang's Blog9 小时前
PostgreSQL笔记60: 权限与角色管理——从层级体系到最佳实践
数据库·笔记·postgresql
whcyhhh10 小时前
头歌实践教学平台:大数据存储2023(六)
大数据·数据库·python
Elastic 中国社区官方博客12 小时前
让大模型思考,让小模型执行:在 Elastic Workflows 中拆分 LLM 成本
大数据·运维·数据库·人工智能·elasticsearch·ai
Faith_xzc12 小时前
一条 SQL 顶一条 Flink 链路?Doris Streaming Job 持续导入全景解析
大数据·数据库·sql·flink