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

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.消息队列

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

其他一致性策略

4.1.读写分离

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

4.2.版本控制

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

相关推荐
摇滚侠6 小时前
《On Java 中文版 基础卷》阅读笔记 实现隐藏 12
java·数据库·笔记
川石课堂软件测试6 小时前
学习软件测试需掌握的一些知识
数据库·python·sql·功能测试·网络协议·mysql·http
2501_937767167 小时前
自建 SQL 数据库定时备份踩坑复盘与落地思路
数据库·sql
silvamux_8 小时前
10款模型写种草笔记:可发布性、返工来源与成本数据缺口复盘
java·数据库·笔记
I Am a robert girl10 小时前
从 GameHorizon 拆解多时间尺度游戏评测:数据管线、离线基准与在线回放的三层设计
数据库·游戏·多时间尺度·数据管线·游戏评测·离线基准·在线回放
躺柒10 小时前
读数据安全架构设计与实践17安全架构案例(下)
数据库·安全·零信任·安全架构·防火墙·无边界
SelectDB10 小时前
实时更新排错现场:导入了却查到旧数据,和 FINAL 越加越慢的两条路
大数据·数据库·数据分析
SelectDB11 小时前
拿 Planet 的账单一行行对下来,我总结出三个可自查的校验点
大数据·数据库·数据分析
SelectDB11 小时前
Apache Doris + Vane:让多模态数据进入业务分析
大数据·数据库·数据分析
pjj1985413 小时前
ClickHouse 与 Doris 数据库技术梳理:看懂分析型数据库
数据库·clickhouse