缓存一致性策略
概念
业务中通常使用MySQL作为持久化数据库,并使用缓存来提升系统性能,同时使用缓存和数据库会存在数据一致性问题,而缓存一致性策略,或者也可以叫做缓存读写策略,是指在写数据或读数据时,数据库与缓存之间如何去维护数据一致性的方案。
换一个视角,从CAP理论视角来看,缓存属于AP系统,即保证分区容错和高可用,一致性采用最终一致性策略,所以,对于追求绝对一致性的场景,不适合引入缓存。
CAP理论:简单来说,一个分布式系统只能同时满足AP或者CP,不存在CAP系统,CA系统只存在于单机环境
- Consistency:强一致性,任何节点在任何时间返回的数据相同
- Availability:高可用,所有请求能够在有限时间内被响应,即使某些节点故障,依然能够返回有效数据(不一定是最新数据)
- Partition Tolerance:分区容错性,发生网络分区后能够继续提供服务
三大策略
旁路缓存模式
写场景
- 更新DB
- 删除cache

| 策略 | 一致性风险 | 推荐度 |
|---|---|---|
| 先更新缓存,再更新数据库 | ⚠️ 高风险:缓存更新成功,数据库更新失败 → 缓存新值,数据库旧值 | ❌ 不推荐 |
| 先更新数据库,再更新缓存 | ⚠️ 高风险:数据库更新成功,缓存更新失败 → 数据库新值,缓存旧值 | ❌ 不推荐 |
| 先删除缓存,再更新数据库 | ⚠️ 高并发下风险高:读请求可能读到旧值并写入缓存 | ❌ 不推荐 |
| 先更新数据库,再删除缓存 | ⚠️ 风险低:理论上存在不一致,但概率极小 | ✅ 推荐 |
为什么先更新DB,再删除Cache? 先删除Cache,再更新DB可以吗,当然不可以,数据一致性风险很高,考虑下边这个场景:
- 客户端A发起写请求,删除Cache
- 客户端B发起读请求,发现Cache不在,查询DB并写入缓存(缓存旧值)
- 客户端A写DB(DB新值)
所以,采用先删后写的策略,那先删后写一定可以保证数据一致性嘛?不能够完全保证,但是!出现一致性问题的概率很低
- 客户端A发起读请求,没有命中缓存,查询DB
- 客户端B发起写请求,没有命中缓存,写DB(DB新值)
- 客户端A将查询到的数据写缓存(缓存旧值)
以上场景会出现数据一致性问题,但是为什么说概率很小呢?
- 两个请求需要命中同一份数据且数据在缓存中不存在
- 13执行时间远小于2执行时间,但是13是读DB+写缓存,2是写DB,缓存写的时间要远小于硬盘写,前者纳秒级,后者毫秒级
那这么说的话,为什么是删缓存而不是直接写缓存呢?删除操作执行速度更快,在多写情况下,频繁更新容易出现瓶颈
读场景
- 读Cache,未命中读DB
- 数据加载到Cache

旁路缓存模式下,DB和Cache操作是同步的,因为需要保证缓存写和DB写的顺序一致,因此旁路缓存不适合多写场景,而且频繁的删除会影响缓存命中率;此外,首次请求的数据一定不在Cache中
有杠精要问了,那我一定要先删缓存再写DB呢?怎么办?延时双删!!!!
先说一句,如果是强一致场景,必须先写DB,再删缓存,先删再写适合于能容忍一定程度不一致的场景,延时双删是指删除缓存之后,间隔一小段时间再次进行删除,中间的这段间隔存在数据不一致风险,因此这个间隔需要尽可能地小,但同时也要保证间隔时间大于写DB的时间
读写穿透
读写穿透和旁路缓存一样,区别只在于将操作DB的任务交给了缓存去做,不需要RD去操作DB,完全面向缓存
写场景

读场景

异步写入
之前也说了,旁路缓存和读写穿透DB和缓存属于同步操作,在高并发写场景下性能极差,异步缓存写入思想很简单,就是同步更新缓存,异步写DB,但是这种方案的一致性风险很高,因此需要额外的机制保证异步写DB一定成功,分布式系统主从同步、MySQL的Buffer Pool刷盘都用到了异步写入
删除缓存重试机制
上边说的三种一致性策略,无论是删除还是写入,其实都存在由于各种各样原因导致失败的情况,比如删除缓存失败,一旦失败,除非后续有新的写请求,否则缓存中的一直是旧值,可以通过缓存删除重试来解决这个问题。
一种无侵入式一致性解决方案
通过监听MySQL binlog,自动触发缓存删除,本质上属于异步写入策略,但是其对业务代码没有侵入性,高并发性能很好

浙公网安备 33010602011771号