对于缓存和数据库双写,其存在着数据一致性的问题。对于数据一致性要求较高的业务场景,咱们一般会选择使用分布式事务(2pc、paxos等)来保证缓存与数据库之间的数据强一致性,但分布式事务的复杂性与对资源的占用问题,使得该处理方式会形成系统性能的下降。对于数据一致性要求没那么高的业务场景,选择分布式事务的处理方式就会显得不是那么必要。为此,在通常状况下,对于数据一致性要求没那么高的业务场景,会选择使用cache-aside-pattern方案来保证缓存与数据库之间,数据的最终一致性,如下文章即是介绍并整理该cache-aside-pattern方案的内容。数据库
对于缓存中的数据,咱们提出三个目标:缓存
对于缓存与数据库的双写问题,无外乎“增删改查” 这四个过程,再考虑进并发的“读读,写写,读写”状况,全部可能的状况组合并很少。为此,咱们能够采用穷举的方式对该方案进行说明。网络
对于数据的查找,该方案的过程与cpu中查找数据的过程是一致的,其过程以下:并发
其示意图以下:分布式
在考虑高并发查询的状况下,对于该处理过程,以下示意图所示。ide
当客户端一、客户端二、....、客户端n查询同一数据,且该数据不存在于缓存中时,全部的客户端请求都会去访问数据库,此时会出现缓存击穿的状况,对于缓存击穿的相关问题及解决方案,咱们留到下一篇文章再进行讲解。高并发
对于新增数据的状况,咱们将数据直接添加进数据库中,且不将新增的数据加载入缓存中,其过程以下示意图所示:性能
在考虑高并发之时,其过程以下示意图所示:3d
经过示意图,咱们了解到,当采用该种方式新增数据时,在并发状况下,并未出现与咱们的目标相违背的问题。blog
当须要对数据进行删除时,咱们有两种删除的方案可供选择。
第一种: 先删除缓存中的数据,再删除数据库中的数据
第二种: 先删除数据库中的数据,再删除缓存中的数据
咱们对这两种方案分别进行分析:
先删缓存:
对于先删除缓存中的数据,后删除数据库中的数据这种方案,其示意图以下:
考虑进高并发的状况,当存在读请求和删除数据的请求并发时,因为网络的不可靠和延时问题,其可能出现以下示意图所示的状况:
经过示意图咱们了解到,存在着缓存中缓存进已删除的旧数据的状况,这违背了咱们提出的第一个目标。
那么,先删除数据库中的数据的状况呢?会出现这样的问题吗?咱们接着分析。
先删数据库:
对于先删除数据库中的数据,后删除缓存中的数据这种方案,其示意图以下。
一样考虑进高并发的状况,当存在读请求和删除数据的请求并发时,其发生的状况以下示意图所示:
此时,并未出现与咱们的目标相违背的问题。综上,咱们在删除数据时,应选择先删除数据库中的数据再删除缓存中的数据的这一方案。
可是该方案仍存在着问题。咱们再往下思考,当删除了缓存中的相关数据,此时来了大量读取该数据的请求。这时,就会致使“缓存穿透”问题的出现(该问题一样在下一篇文章中进行讲解)。
当须要对数据进行变动的时候,咱们有三种方案可供选择。
第一种: 先更新数据库,后更新缓存
第二种: 先删除缓存,后更新数据库
第三种: 先更新数据库,后删除缓存
咱们对这三种方案逐一进行分析。
先更新数据库,后更新缓存:
对于先更新数据库,后更新缓存这种方案,其示意图以下:
考虑高并发的状况,当存在两个更新操做的并发请求时,因为网络的延时问题,其可能会出现以下这种状况:
由上述示意图可知,当存在两个更新操做时,其有将数据库中的旧数据存入缓存中的状况,这违背了咱们的第一个目标。那么,有没有办法解决呢?答案是有的,借助乐观锁的相关思想,咱们能够给每一个数据加上一个版本号,当数据要存入缓存时,比较一下数据的版本号便可。到目前为止,更新了数据库中的相关数据以后,再更新缓存中的数据这个方案看起来是能够的,可是这个方案存在着一个问题,就是须要去更新缓存中的数据,更新缓存中的数据这个操做会影响系统的性能。特别是在写多读少,且缓存的数据不是直接从数据库中存入,而是通过计算以后再进行缓存的业务场景中时,对系统性能形成的影响会更加明显。为此咱们能够借助“懒加载”的思想,在更新数据之时,将缓存中的数据进行删除,等到须要用到的时候,才将数据加载进缓存中。下面咱们将讨论在更新时删除缓存的两个可能的方案。
先删除缓存,后更新数据库:
对于先删除缓存,后更新数据库的方案,其示意图以下:
考虑高并发的状况,当存在读请求和更新请求并发的状况,因为存在网络延时,其可能出现以下示意图中的状况:
由示意图中的状况可知,在读写并发的状况下,其存在着将数据库中的旧数据存入缓存中的问题。这违背了咱们的第一个目标。
先更新数据库,后删除缓存:
对于先更新数据库,后删除缓存的状况,其以下示意图所示:
考虑并发的状况,当存在读请求和更新请求并发,且此时缓存中的数据刚好失效时,再加上因为网络的延迟问题,则有可能会出现以下示意图所示的状况。
由示意图可知,当缓存中的数据刚好失效时,其是可能存在着缓存中存入数据库中的旧数据的可能性的。可是,发生这种状况的几率是比较低的,由于让该种状况出现的条件较为“苛刻”,须要刚好缓存中的数据失效,且要求“读操做”慢于“写操做”。但在一般状况下,“写操做”是慢于“读操做”的,由于写操做通常会涉及到数据库锁的相关操做。
综上,在对比了以上三种方案以后,对于数据更改的状况,咱们采用先更新数据库,后删除缓存的方式。