数据库和缓存如何保证一致性?

📢📢📢📣📣📣

哈喽!大家好 ,我是【一心同学 】,一位上进心十足的【**Java领域博主】!**😜😜😜

✨【一心同学 】的写作风格 :喜欢用【通俗易懂 】的文笔去讲解每一个知识点,而不喜欢用【高大上】的官方陈述。

✨【一心同学 】博客的领域 是【面向后端技术 】的学习,未来会持续更新更多的【后端技术 】以及【学习心得】。

✨如果有对【后端技术 】感兴趣的【小可爱 】,欢迎关注一心同学】💞💞💞

❤️❤️❤️感谢各位大可爱小可爱!❤️❤️❤️


目录

前言

一、先更新数据库,还是先更新缓存?

[🌴 先更新数据库,再更新缓存](#🌴 先更新数据库,再更新缓存)

[🌴 先更新缓存,再更新数据库](#🌴 先更新缓存,再更新数据库)

[🚀 小结](#🚀 小结)

二、先更新数据库,还是先删除缓存?

[🌵 旁路缓存策略](#🌵 旁路缓存策略)

[🌵 先删除缓存,再更新数据库](#🌵 先删除缓存,再更新数据库)

[🌵 先更新数据库,再删除缓存](#🌵 先更新数据库,再删除缓存)

三、数据一致性解决方案

[🌴 先更新数据库,再更新缓存](#🌴 先更新数据库,再更新缓存)

[🌴 先删除缓存,再更新数据库](#🌴 先删除缓存,再更新数据库)


前言

在我们的项目开发中,为了提高数据的访问速度以及降低数据库负载,我们通常会将热点数据存储在缓存当中,像Redis缓存是基于内存的缓存系统,读取数据的速度非常快,通常可以在微秒级别内进行响应数据,但是由于缓存的存在,也同时引入了缓存与数据库的一致性问题,本文将进行解析如何保证其一致性。

本文针对数据不一致性提供四种策略进行解析,一心在这里就不卖关子了,先直接展开:

  1. 先更新缓存,再更新数据库。
  2. 先更新数据库,再更新缓存。
  3. 先删除缓存,再更新数据库。
  4. 先更新数据库,再删除缓存。

四种策略无非就是先后顺序,一心把他总结为两个问题:

问题一:先更新数据库,还是先更新缓存?

问题二:先更新数据库,还是先删除缓存?

在这里,我们带着这两个问题来进行逐一展开。

一、先更新数据库,还是先更新缓存?

🌴 先更新数据库,再更新缓存

流程 :【请求A】先将数据库的数据更新为 1 ,然后在更新缓存前,【请求 B】 将数据库的数据更新为 2 ,紧接着也把缓存更新为 2 ,然后 【请求A】更新缓存为 1

此时,数据库中的数据是 2,而缓存中的数据却是 1,出现了缓存和数据库中的数据不一致的现象。(此方案在实际中不建议采用)

🌴 先更新缓存,再更新数据库

流程 :【请求A】先将缓存的数据更新为 1 ,然后在更新数据库前,【请求B】来了, 将缓存的数据更新为 2 ,紧接着把数据库更新为 2 ,然后 【请求A】将数据库的数据更新为 1

此时,数据库中的数据是 1,而缓存中的数据却是 2,出现了缓存和数据库中的数据不一致的现象。(此方案在实际中不建议采用)

🚀 小结

无论是【先更新数据库,再更新缓存】,还是【先更新缓存,再更新数据库】,这两个方案都存在并发问题,当两个请求并发更新同一条数据的时候,可能会出现缓存和数据库中的数据不一致的现象。

二、先更新数据库,还是先删除缓存?

🌵 旁路缓存策略

对于不更新缓存,而是删除缓存中的数据。然后读取数据时,发现缓存中没了数据之后,再从数据库中读取数据,更新到缓存中。我们将这种策略称为Cache Aside 策略,中文是叫旁路缓存策略。

旁路缓存策略又可以细分为「读策略 」和「写策略」。

写策略的步骤:

  • 更新数据库中的数据;
  • 删除缓存中的数据。

读策略的步骤:

  • 如果读取的数据命中了缓存,则直接返回数据;
  • 如果读取的数据没有命中缓存,则从数据库中读取数据,然后将数据写入到缓存,并且返回给用户。

🌵 先删除缓存,再更新数据库

流程 :现有一个数据初始值为20 ,【请求A】此时要将数据更新为21 ,先执行删除缓存操作,此时,另一个【请求B】要读取这个数据,查询缓存未命中后,会从数据库读取到该数据为20 ,并写入到缓存中,这时【请求A】继续更改数据库,将数据进行更新为21

此时,缓存中的数据是20 (旧值),而数据库中却是21(新值),缓存和数据库的数据不一致。

所以先删除缓存,再更新数据库,在「读 + 写」并发的时候,还是会出现缓存和数据库的数据不一致的问题。(此方案在实际中不建议采用)

🌵 先更新数据库,再删除缓存

流程 :假如某个数据在缓存中不存在,【请求 A】 读取数据时从数据库中查询到其为 20 ,在未写入缓存中时另一个【请求 B】 更新数据。它更新数据库中的数据为 21 ,并且清空缓存。这时【请求 A 】把从数据库中读到的 数据20写入到缓存中。

最终,该数据在缓存中是 20 (旧值),在数据库中是 21(新值),缓存和数据库数据不一致。

从理论上分析,先更新数据库,再删除缓存也是会出现数据不一致性的问题,但是在实际中,这个问题出现的概率并不高

因为缓存的写入通常要远远快于数据库的写入,所以在实际中很难出现请求 B 已经更新了数据库并且删除了缓存,请求 A 才更新完缓存的情况。

而一旦【请求 A】 早于【请求 B】 删除缓存之前更新了缓存,那么接下来的请求就会因为缓存不命中而从数据库中重新读取数据,所以不会出现这种不一致的情况。

所以,「先更新数据库 + 再删除缓存」的方案,是可以保证数据一致性的

三、数据一致性解决方案

🌴 先更新数据库,再更新缓存

先更新缓存,再更新数据库 也同理。

方案:

  • 在更新缓存前先加个分布式锁,保证同一时间只运行一个请求更新缓存,就会不会产生并发问题了,当然引入了锁后,对于写入的性能就会带来影响。
  • 在更新完缓存时,给缓存加上较短的过期时间,这样即时出现缓存不一致的情况,缓存的数据也会很快过期,对业务还是能接受的。

🌴 先删除缓存,再更新数据库

方案:

伪代码:

bash 复制代码
#删除缓存
redis.delKey(X)
#更新数据库
db.update(X)
#睡眠
Thread.sleep(N)
#再删除缓存
redis.delKey(X)

「延迟双删」:

加个睡眠时间,主要是为了确保【请求 A】 在睡眠的时候,【请求 B】 能够在这这一段时间完成「从数据库读取数据,再把缺失的缓存写入缓存」的操作,然后【请求 A】 睡眠完,再删除缓存。

所以,【请求 A】 的睡眠时间就需要大于【请求 B】 「从数据库读取数据 + 写入缓存 」的时间。但具体睡眠多久,很难评估出来,所以这个方案也只是尽可能保证一致性而已,极端情况下,依然也会出现缓存不一致的现象。

故以上的所有策略中,最建议使用的还是「先更新数据库,再删除缓存」。

相关推荐
装不满的克莱因瓶1 分钟前
【Redis经典面试题六】Redis的持久化机制是怎样的?
java·数据库·redis·持久化·aof·rdb
梦想平凡2 小时前
PHP 微信棋牌开发全解析:高级教程
android·数据库·oracle
TianyaOAO2 小时前
mysql的事务控制和数据库的备份和恢复
数据库·mysql
fpcc2 小时前
跟我学c++中级篇——C++中的缓存利用
c++·缓存
Ewen Seong2 小时前
mysql系列5—Innodb的缓存
数据库·mysql·缓存
安全二次方security²2 小时前
SMMU软件指南SMMU编程之虚拟机结构和缓存
缓存·cache·smmu·arm安全架构·系统mmu·虚拟机结构·vms
码农老起3 小时前
企业如何通过TDSQL实现高效数据库迁移与性能优化
数据库·性能优化
夏木~4 小时前
Oracle 中什么情况下 可以使用 EXISTS 替代 IN 提高查询效率
数据库·oracle
W21554 小时前
Liunx下MySQL:表的约束
数据库·mysql
黄名富4 小时前
Redis 附加功能(二)— 自动过期、流水线与事务及Lua脚本
java·数据库·redis·lua