当前位置: 首页 > news >正文

使用数据库和缓存的时候,是如何解决数据不一致的问题的?

1.缓存更新策略

1.1. 缓存旁路模式(Cache Aside)

在应用里负责管理缓存,读取时先查缓存,如果命中了则返回缓存,如果未命中就查询数据库,然后返回缓存,返回缓存的同时把数据给写入缓存中。更新的时候则是先更新数据库,然后再删除缓存。

读: 先查缓存,缓存命中则之间返回,未命中则查数据库,数据库返回数据的同时写入缓存中。这样子下一次再查的时候就可以命中缓存了。

写: 先更新数据库,更新之后删除缓存。

优点是比较简单,对数据一致性要求不高的时候大多数场景能用,缺点就是可能会有短时间的数据不一致问题,主要是在写操作中删除缓存的时候。为什么会有数据不一致的时候呢?如果在把A改为B之后,缓存里还是A,但是在删除缓存之前,此时另一个线程已经进来了要读取缓存,那么它读取到的就是A(也就是旧的数据),它命中缓存了所以不会再访问数据库,但是此时数据库是B,明显出现了数据不一致的问题。这种情况一般是在并发情况出现。

1.2.写穿透模式(Write Through)

更新数据库的同时更新缓存,保障数据一致。

读: 直接查缓存。

写: 先更新数据库再更新缓存。

优点是数据一致性比较高,缺点是每次写操作都会更新缓存,缓存的压力可能会比较大。

1.3.写回模式(Write Back)

读: 直接查缓存。

写: 先更新缓存,然后异步更新数据库。

1.4.读写双删(属于缓存旁路模式的扩展)

在应用里负责管理缓存,读取时先查缓存,如果命中了则返回缓存,如果未命中就查询数据库,然后返回缓存,返回缓存的同时把数据给写入缓存中。更新的时候则是先更新数据库,然后再删除缓存,隔段时间再删除一次缓存,也就是一共删了两次缓存。

读: 先查缓存,缓存命中则之间返回,未命中则查数据库,数据库返回数据的同时写入缓存中。这样子下一次再查的时候就可以命中缓存了。

写: 先删除缓存,然后更新数据库,更新之后再次删除缓存。

要知道缓存旁路模式在并发情况下,可能会出现数据不一致问题,如果并发量比较高的话,出现问题的概率就会变大,所以有了读写双删这个扩展的方式。

具体实现:
1.先删除缓存(防止更新数据库后获取缓存的请求获取到旧数据)
2.然后更新数据库
3.然后删除缓存(防止此时的缓存里的数据和数据库里的不一致)

读写双删相对出现数据不一致的概率比较低,但也并不是一定的,如果在A线程删除缓存之后,更新数据库之前,此时有另一个线程B进来进行读操作,因为B的缓存未命中直接访问数据库,然后又会把数据写入缓存,此时缓存里的数据就是更新前的数据,但是A依然会进行更新数据库的操作,然后就导致数据库的数据和缓存不一致,当线程C来访问的时候,因为缓存命中,所以直接读到了旧数据。

1.5.读写双删(Double Delete)

具体实现:
1.先删除缓存(防止更新数据库后获取缓存的请求获取到旧数据)
2.然后更新数据库
3.等待一段时候后
4.然后删除缓存(防止此时的缓存里的数据和数据库里的不一致)

缓存失效策略

2.1.主动失效

在数据库更新的时候,立刻删除缓存或者更新缓存。
读: 直接查缓存。

写: 先写数据库,然后删缓存或者立马更新缓存。

2.2.被动失效

给缓存设置TTL(过期时间),过期后自动失效。
读: 直接查缓存。

写: 写入缓存时,设置TTL

优点是比较简单,缺点就是在缓存过期后、更新缓存前,可能会有短暂的数据不一致。

双写一致性策略

3.1.分布式锁

在更新数据库和缓存的时候,使用分布式锁,确保操作是原子性的(要么都成功要么都失败)。主要流程就是【获取分布式锁>>更新数据库>>更新缓存>>释放分布式锁】,优点是确保强一致性,缺点也很明显,因为加了锁所以没法支持并行操作,多线程到这里变成排队的单线程操作,会导致性能比较低。适用于对性能要求不高并且对数据一致性要求很高的场景。

3.2.消息队列

通过消息队列通知更新缓存,确保最终一致性(和强一致不一样)。主要流程就是【更新数据>>发送消息>>消费者读取消息更新缓存】,优点是异步操作,性能较好,缺点也是因为异步,所以缓存会有延迟,如果在更新缓存之前有其他请求获取缓存,可能就会出现数据不一致的情况。

3.3.

其他一致性策略

4.1.读写分离

顾名思义,就是把读和写分开处理,读操作是从缓存中读数据,写操作则是更新数据库,并且更新缓存(即时或者异步都可),优点就是读性能会很好,缺点就是写操作时可能会导致数据库和缓存出现数据不一致的问题。

4.2.版本控制

给缓存添加一个版本号,每次更新缓存都增加版本号,确保版本和数据库的版本一致。优点就是能保障数据的一致性,缺点是实现起来比较复杂。

相关文章:

  • android edittext 防止输入多个小数点或负号
  • 开发环境搭建-05.后端环境搭建-前后端联调-通过断点调试熟悉项目代码特点
  • 每日一题----------枚举的注意事项和细节
  • C/C++蓝桥杯算法真题打卡(Day3)
  • 江科大51单片机笔记【11】AT24C02(I2C总线)
  • 算法·搜索
  • 数据集笔记 LTA Traffic Count
  • VS2019,VCPKG - 为VS2019添加VCPKG
  • LInux 文件系统
  • Spring Boot 缓存最佳实践:从基础到生产的完整指南
  • 实时读取另一个串口发来的返回数据
  • Android 低功率蓝牙之BluetoothGattDescriptor详解
  • 装饰器模式--RequestWrapper、请求流request无法被重复读取
  • 基于GeoTools的GIS专题图自适应边界及高宽等比例生成实践
  • 【JavaSE-8】面向对象
  • 运动控制卡--固高实用
  • 软件信息安全性测试流程有哪些?专业软件测评服务机构分享
  • MySQL自定义序列数的实现
  • 【AIGC系列】6:HunyuanVideo视频生成模型部署和代码分析
  • 【Framework系列之Client】UIManager和UIPanel模块介绍
  • 商务部就美国商务部调整芯片出口管制有关表述答记者问
  • 国家统计局:要持续加大好房子建设供应力度,积极推动城市更新行动和保障房建设
  • 三件珍贵标本开箱!中国恐龙大展5月26日在沪开幕,明星标本汇聚一堂
  • 四大皆空!赛季还没结束,曼城已经吃上“散伙饭”了
  • 外交部驻港公署正告美政客:威胁恫吓撼动不了中方维护国家安全的决心
  • 用贝多芬八首钢琴三重奏纪念风雨并肩20年