如何保证缓存和 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 中;

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

相关推荐
苹果酱056742 分钟前
Java设计模式:探索编程背后的哲学
java·vue.js·spring boot·mysql·课程设计
五花肉村长1 小时前
Linux-读者写著问题和读写锁
linux·运维·服务器·开发语言·数据库·visualstudio
五步晦暝3 小时前
【Excel 支持正则的方法】解决VBA引入正则的方法和步骤
数据库·mysql·excel
卡戎-caryon3 小时前
【MySQL】07.表内容的操作
linux·网络·数据库·mysql·存储引擎
一只fish3 小时前
MySQL 8.0 OCP 1Z0-908 题目解析(11)
数据库·mysql
麓殇⊙3 小时前
黑马点评--基于Redis实现共享session登录
数据库·redis·firefox
zhutoutoutousan4 小时前
解决 Supabase “permission denied for table XXX“ 错误
javascript·数据库·oracle·个人开发
泽韦德4 小时前
【MySQL】第8节|Innodb底层原理与Mysql日志机制深入剖析(一)
数据库·mysql
vvilkim4 小时前
MongoDB 数据库迁移:完整指南与最佳实践
数据库·mongodb
vvilkim4 小时前
MongoDB索引:原理、实践与优化指南
数据库·mongodb