什么是缓存一致性
缓存一致性是指在分布式系统中,确保多个节点上的缓存数据与数据库之间保持一致的状态。
为什么有缓存一致性
在高并发的业务场景下,数据库大多数情况都是用户并发访问最薄弱的环节。
如下图所示:

如果同时有1万个用户来访问,所有的压力都转移道数据库,数据库承受不住。
所以,就需要缓存做一个缓冲操作,让请求先访问到缓存,比如:redis,而不是直接访问MySQL等数据库。
由于缓存的引入,系统中可能存在多个节点上的缓存副本,而这些缓存副本需要保持与底层数据存储的同步,以避免读取到过期、错误或不一致的数据。
缓存一致性的目标是确保系统中各个节点上的缓存数据在任何时刻都是正确、一致的。
缓存一致性的解决方案
读取缓存步骤一般没有什么问题,但是一旦涉及到数据更新,就容易出现缓存和数据库间的数据一致性问题。
如下图所示:
不管是先写MySQL数据库,再删除Redis缓存,还是先删除缓存再写库,都有可能出现数据不一致的情况。
怎么来解决?
第一种方案:采用延时双删策略
在一些分布式系统中,为了保证数据的一致性,特别是在高并发的情况下,采用了类似”延时双删”的策略。
下面是”延时双删”策略的一般实现步骤:
- 数据更新: 当底层数据发生变化时,首先更新底层数据,然后立即将缓存中对应的数据标记为过期(失效)。
- 缓存查询: 当有新的请求到达时,首先检查缓存中的数据是否过期,如果过期则触发一个异步任务。
- 异步任务: 异步任务会在一定的延时之后真正地删除缓存中的数据。
- 缓存删除: 异步任务执行后,真正删除缓存中的过期数据。
这种策略的优势在于,通过标记数据过期,可以保证在缓存中的数据尽快变为过期状态,从而避免了缓存中过期数据的读取。
第二种方案:异步更新缓存
异步更新缓存是一种常见的缓存一致性策略,特别适用于需要提高写操作性能的场景。
在这种策略中,写操作不会直接阻塞等待缓存更新完成,而是通过异步方式在后台进行。
以下是异步更新缓存的一般实现步骤:
- 写操作触发: 当数据发生写操作时,首先更新底层数据存储(例如数据库)。
- 异步任务启动: 同时,启动一个异步任务,该任务负责更新缓存。这可以通过消息队列、线程池等机制实现。
- 返回结果: 不等待异步任务完成,立即返回写操作的结果。这确保写操作不会因为等待缓存更新而变得缓慢。
- 异步任务执行: 异步任务在后台执行,负责将新的数据加载到缓存中,替换旧的缓存数据。
这种策略的优势在于:
- 提高写操作性能: 由于写操作不需要等待缓存更新完成,可以显著提高写操作的性能和吞吐量。
- 降低响应时间: 客户端能够更快地得到写操作的返回结果,而不必等待缓存更新。
- 异步任务解耦: 缓存更新的异步任务与写操作解耦,不会因为缓存更新失败而影响写操作的成功。
第三种方案:cache aside+延迟双删
cache aside:这是一种公认的经典缓存一致性处理模式,也叫旁路缓存模型。
cache aside:采用先写库,再删缓存的操作。
如下图所示:
这个模式也会发生数据不一致的情况,例如:
- 读线程A在读数据,缓存失效,读到数据库后;
- 此时写线程B更新数据,更新完数据库,删除缓存;
- 此时读线程A写缓存,便会将之前读到的旧值写到缓存中,导致数据的不一致。
所以针对上面的模式,衍生出来延迟双删来解决某些场景下数据不一致的情况,就变成了cache aside+延迟双删结合的模式了。
综合应用这些技术手段,可以在分布式系统中实现较好的缓存一致性,保障系统的稳定性和可靠性。
mikechen
mikechen睿哥,10年+大厂架构经验,资深技术专家,就职于阿里巴巴、淘宝、百度等一线互联网大厂。
关注「mikechen」公众号,获取更多技术干货!

后台回复【架构】即可获取《阿里架构师进阶专题全部合集》,后台回复【面试】即可获取《史上最全阿里Java面试题总结》