数据库缓存一致性问题

文章探讨了在数据库和缓存操作中如何处理一致性问题,包括先更新缓存还是数据库、并发场景下的数据不一致,以及使用分布式锁和消息队列来确保操作的原子性和重试机制。提出了先更新数据库再删除缓存的策略作为较优解,并强调了实际环境中数据不一致发生的概率较低。
摘要由CSDN通过智能技术生成

本文是完全参考 缓存和数据库一致性问题,看这篇就够了 进行整理的,用于巩固对数据库缓存一致性的认识

在实际开发中,对缓存比较简单的操作是

  1. 读数据先查缓存,缓存有也从缓存取,缓存没有再查数据库,并添加到缓存中去
  2. 更新数据则需要刷新数据库和缓存

这里我们关注两个问题

  1. 缓存利用率
  2. 数据库缓存的一致性

对于上述的读数据

随着时间的推移,缓存中的数据会越来越多,最极端的情况可能就是与数据库全量同步。对于一些访问频率小的数据而言,存在于缓存无疑是对内存的浪费。因此我们可以针对缓存设置过期时间,使得热点数据能常驻缓存,不被经常访问的数据从缓存中剔除,提高缓存的利用率。

对于上述的写数据

我们考虑以下几种情况

  1. 并发与非并发
  2. 非并发下,数据库和缓存刷新的顺序以及第二步出错的情况

假设X=1 更新为 X=2

先刷新缓存,后刷新数据库

非并发

情景:先刷新缓存,后刷新数据库出错
结果:缓存中 X=2,数据库 X=1,数据不一致

并发

情景:先刷新缓存,后刷新数据库,两步都成功
A:更新缓存 X=2
B:更新缓存 X=1
B:更新数据库 X=2 变为 X=1
A:更新数据库 X=1 变为 X=2

结果:缓存X=1,数据库X=2,数据不一致
解决方法:添加分布式锁,保证刷新数据库和缓存为一个原子性操作,但是这样做缓存利用率

先刷新数据库,后刷新缓存

非并发

情景:先刷新数据库,后刷新缓存出错
结果:缓存X=1,数据库X=2,数据不一致

并发

情景:先刷新数据库,后刷新缓存,两者都成功
A:更新数据库 X=1 变为 X=2
B:更新数据库 X=2 变为 X=1
B:更新缓存 X=1
A:更新缓存 X=2
结果:缓存X=2,数据库X=1,数据不一致
解决方法:添加分布式锁,保证刷新数据库和缓存为一个原子性操作,但是这样做缓存利用率

先删除缓存,后刷新数据库

非并发

情景:先删除缓存,后刷新数据库出错
结果:缓存中没有X,数据库X=1,重新读取X时,缓存中X=1,数据一致,但更新失败

并发读写

情景:A写B读,先删除缓存,后刷新数据库
A:删除缓存
B:缓存不存在,读数据库,X=1
A:更新数据库 X=1 变为 X=2
B:更新缓存 X=1

结果:缓存 X=1,数据库X=2,数据不一致

先刷新数据库,后删除缓存

非并发

情景:先刷新数据库,后删除缓存失败
结果:数据库X=2,缓存中X=1,数据不一致

并发读写

情景:缓存不存在,A写B读,先刷新数据库,后删除缓存
B:读缓存,缓存不存在,B读数据库,得到X=1
A:更新数据库 X=1 变为 X=2
A:删除缓存
B:更新缓存X=1

结果:数据库X=2,缓存中X=1,数据不一致

这种情况「理论」来说是可能发生的,但实际真的有可能发生吗?

其实概率「很低」,这是因为它必须满足 3 个条件:

 1. 缓存刚好已失效
 2. 读请求 + 写请求并发
 3. 更新数据库 + 删除缓存的时间,要比读数据库 + 写缓存时间短

仔细想一下,条件 3 发生的概率其实是非常低的。

因为写数据库一般会先「加锁」,所以写数据库,通常是要比读数据库的时间更长的。

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

所以,我们应该采用这种方案,来操作数据库和缓存。

解决了并发问题,我们继续来看前面遗留的,第二步执行「失败」导致数据不一致的问题。

保证两步成功执行

从前面的分析可以看出,只要第二步失败,都会导致数据不一致,那如何保证第二步可以执行成功呢?

第一时间想到就是:重试

这时就需要考虑以下问题:

  1. 重试时机,立即重试很可能会再次失败
  2. 重试次数,多少次才算合理
  3. 重试方式(同步或异步),同步会阻塞,异步更好

异步重试可以考虑消息队列,这时又要考虑消息队列相关的问题:

  1. 消息队列的可靠性:写到队列中的消息,成功消费之前不会丢失(重启项目也不担心)
  2. 消息队列保证成功投递:下游从队列拉取消息,成功消费后才会删除消息,否则还会继续投递消息给消费者(符合我们重试的需求)

情景概览

非并发并发
先刷新缓存,后刷新数据库情景:先刷新缓存,后刷新数据库出错

结果:缓存中 X=2,数据库 X=1,数据不一致
情景:先刷新缓存,后刷新数据库,两步都成功

A:更新缓存 X=2
B:更新缓存 X=1
B:更新数据库 X=2 变为 X=1
A:更新数据库 X=1 变为 X=2

结果:缓存X=1,数据库X=2,数据不一致

解决方法:添加分布式锁,保证刷新数据库和缓存为一个原子性操作,但是这样做缓存利用率
先刷新数据库,后刷新缓存情景:先刷新数据库,后刷新缓存出错

结果:缓存X=1,数据库X=2,数据不一致
类似上述
先删除缓存,后刷新数据库情景:先删除缓存,后刷新数据库出错

结果:缓存中没有X,数据库X=1,重新读取X时,缓存中X=1,数据一致,但更新失败
情景:A写B读,先删除缓存,后刷新数据库

A:删除缓存
B:缓存不存在,读数据库,X=1
A:更新数据库 X=1 变为 X=2
B:更新缓存 X=1

结果:缓存 X=1,数据库X=2,数据不一致
先刷新数据库,后删除缓存情景:先刷新数据库,后删除缓存失败

结果:数据库X=2,缓存中X=1,数据不一致
情景:缓存不存在,A写B读,先刷新数据库,后删除缓存

B:读缓存,缓存不存在,B读数据库,得到X=1
A:更新数据库 X=1 变为 X=2
A:删除缓存
B:更新缓存X=1

结果:数据库X=2,缓存中X=1,数据不一致

理论会发生,但概率很低

参考

【1】缓存和数据库一致性问题,看这篇就够了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值