本文转载自微信公众号《爱笑的建筑师》,作者雷佳。转载本文,请联系LoveSmile的架构师公众号。在大型系统中,为了减轻数据库的压力,通常会引入缓存机制。一旦引入缓存,很容易造成缓存和数据库数据不一致,导致用户看到的是旧数据。为了减少数据的不一致性,更新缓存和数据库的机制显得尤为重要,接下来我就带领大家踩坑。CacheasideCacheaside,也称为旁路缓存,是一种比较常用的缓存策略。(1)读请求的常见流程缓存一旁读请求应用会先判断缓存是否有数据,缓存直接返回数据。如果缓存未命中,缓存会穿透到数据库。数据会从数据库中查询出来,然后写回缓存,最后返回。数据给客户端。(2)写请求的一般流程缓存一旁一个写请求首先更新数据库,然后从缓存中删除数据。看到写请求的图片,可能有同学会问:为什么要删除缓存,直接更新呢?这里涉及到几个坑,我们一步一步来踩。Cacheaside踩坑的步骤如果使用错误的Cacheaside策略,就会遇到很深的坑。让我们一一踩踏。第一步:先更新数据库,再更新缓存。如果两个写请求需要同时更新数据,每个写请求先更新数据库,再更新缓存。在并发场景下,可能会出现数据不一致的情况。先更新数据库,再更新缓存如上图所示:(1)写请求1更新数据库,将age字段更新为18;(2)写请求2更新数据库,将age字段更新为20;(3)写请求2更新缓存,设置缓存年龄为20;(4)写请求1更新缓存,设置缓存年龄为18;执行后预期结果是数据库age为20,cacheage为20,结果cacheage为18,导致缓存数据不是最新的,出现脏数据。第二步:先删除缓存,再更新数据库。如果写请求的处理流程是先删除缓存,再更新数据库,在读请求和写请求并发的场景下,可能会出现数据不一致的情况。先删除缓存,再更新数据库如上执行过程所示:(1)写入删除缓存数据的请求;(2)读取请求查询缓存未命中(HitMiss),然后查询数据库,将返回的数据写回到缓存中;(3)写一个更新数据库的请求。整个过程下来,发现数据库中的age是20,缓存中的age是18,缓存和数据库数据不一致,缓存中出现脏数据。践行三:先更新数据库,再删除缓存在实际系统中,对于写请求,建议先更新数据库,再删除缓存,但理论上还是存在问题,如下例所示。先更新数据库,再删除缓存。执行过程如上图所示:(1)读请求先查询缓存,缓存未命中,查询数据库返回数据;(2)写请求更新数据库,删除缓存;(3)读请求写回Cache;整个流程运行后,发现数据库age为20,cacheage为18,即数据库与缓存不一致,导致应用从缓存中读取的数据为旧数据。但是我们仔细想想,出现上述问题的概率其实很低,因为通常数据库更新操作比内存操作要长几个数量级。在更新数据库之前完成。出现这种极端情况怎么办?我们不得不想一个解决办法:给缓存数据设置过期时间。通常在系统中,可以允许少量的数据在短时间内不一致。通读在CacheAside更新模式下,应用代码需要维护两个数据源:一个是缓存,一个是数据库。在Read-Through策略下,应用程序不需要管理缓存和数据库,只需要将数据库的同步委托给缓存提供者CacheProvider即可。所有的数据交互都是通过抽象缓存层完成的。Read-Through流程如上图所示。应用只需要和CacheProvider交互,不管是从缓存还是数据库中获取。在做大量读取时,Read-Through可以减轻数据源的负载,同时对缓存服务的故障也有一定的恢复能力。如果缓存服务宕机,缓存提供者仍然可以直接到数据源进行操作。Read-Through适用于多次请求同一个数据的场景,这和Cache-Aside策略很相似,但是两者还是有一些区别的,这里再说一遍:在Cache-Aside中,应用负责从数据源获取数据并更新到缓存中。在Read-Through中,这种逻辑通常由一个独立的缓存提供者(CacheProvider)来支持。在Write-throughWrite-Through策略下,当发生数据更新(Write)时,由缓存提供者CacheProvider负责更新底层数据源和缓存。缓存与数据源保持一致,写入总是经过抽象缓存层到数据源。缓存提供程序充当代理。Write-Through过程WritebehindWritebehind有些地方也叫Writeback。简单的理解就是:应用程序更新数据时,只更新缓存,CacheProvider每隔一段时间就将数据刷新到数据库中。说白了就是延迟写。Writebehind流程如上图所示。当应用程序更新两条数据时,CacheProvider会立即将其写入缓存中,但会在一段时间后分批写入数据库中。这种方式有优点也有缺点:优点是数据写入速度很快,适合频繁写入的场景。缺点是缓存和数据库不强一致,在一致性要求高的系统中慎用。综上所述,学习了这么多,相信大家对缓存更新策略有了一个清晰的认识。最后,做一个小总结。缓存更新策略主要分为三种:CacheasideRead/WritethroughWritebehindCacheaside通常先更新数据库,然后删除缓存,通常会为数据设置缓存时间,以覆盖底线。Read/Writethrough一般由CacheProvider提供,对外进行读写操作,应用不需要感知操作的是缓存还是数据库。Writebehind简单理解就是延迟写入。CacheProvider会定时批量输入数据库。优点是应用写入速度非常快。好了,今天就到这里了,你学会了吗?
