缓存的读写策略-Cache Aside
Posted 程序技术分享
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了缓存的读写策略-Cache Aside相关的知识,希望对你有一定的参考价值。
今天总结一下缓存的读写策略,很多时候我们会觉得缓存的读写很简单,读取的时候,优先读取缓存,缓存不存在的时候读取数据库,然后对缓存一个补写,实际上针对不同的场景,缓存的读写策略也是不同的,
Cache Aside(缓存旁路策略)
一个简单的更新场景:如果ID系统里有一个用户表,其中有两个字段,id主键和age年龄。当我们要对数据id=1的用户更新年龄信息为20岁,怎么去操作?
最容易想到的解决方案是,我们先更新数据库,然后在更新缓存,,如下图
仔细思考一下,这个思路会造成数据库和缓存不一致的现象。比如两个独立的请求,都去更新数据库和缓存,A更新数据库id=1 的用户年龄为20,B更新数据库id=1的年龄为21,B执行的较快,B更新完数据库后,更新缓存,缓存更新成功为21,此时A再去更新缓存的时候,会把缓存更新为20,此时数据库是21,缓存是20,这里就出现了缓存不一致的现象。
为什么会产生这个现象呢?
因为变更数据库和缓存是两个独立的操作。我们并没有对操作做任何的并发控制。因此当两个线程并发的更新他们时候,就会因为写入顺序和快慢的原因造成数据不一致。
直接更新缓存还有另外一个问题,就是缓存更新丢失,例如,一个用户的账户金额的更新,如果有缓存,你在更新之前,会先缓存读用户现有的账户余额,用户充钱的时候,会对金额进行变更,如A线程,更新原有金额20,变为30,此时在更新之前如果又有另一个线程B也加余额5元,此时读取到未更新的数20,会更新成25,无论最终是更新成30 还是25,都不是正确的金额,因为用户是应该加15元,变为35.
上面的这些更新操作的问题,我们如何解决呢?其实我们在更新缓存时候,不是更新而是删除缓存。在读取数据的时候,发现缓存中没有数据的时候再从数据库中去取,回写到缓存
这个策略就是经常被使用的Cache Aside,它是以数据库的数据为准,缓存的数据是按需加载的,这个策略分为读策略和写策略。
读策略:
1 从缓存中国读数据,
2 如果缓存命中,直接返回缓存中的数据
3 如果缓存为命中,从数据库中查询数据
4从数据库中查询到数据后,写入缓存,并返回数据给客户端。
写策略:
1 更新数据库的记录
2 删除缓存中的数据
如果把写策略的顺序换一下,先删除缓存,在更新数据库这样可以吗?
这样会造成数据和缓存的不一致。比如,一个请求A,需要更新年纪由20变为21岁,如果先删除缓存,缓存删除成功,如果在更新数据库成功之前,有另一个线程B去查询用户的年纪,此时发现缓存没数据,会查询数据,由于此时数据库还没更新成功,查询到数据是20,写入到缓存,此时缓存数据为20,当线程A更新数据库成功后,数据库为21,这时候,缓存和数就不一致了,
其实像Cache Aside这样先更新数据库,后删除缓存在一个极端场景下也是有问题的,就是缓存中没数据,线程A查询得到20后回写到缓存中,在缓存回写前,被别的线程B更新了操作数据库更新为21,并且执行了一步删除操作,当线程A回写成功后,缓存中的数据是20,数据库中的数据是21,此时就造成了缓存和数据不一致。
如上面的流程,这也是个极端的场景,其实是不存在不会发生的,因为缓存的更新是极快的,不可能存在线程B数据库更新完了,线程A缓存还没更新完。
Cache Aside最大的问题在于,删除缓存,对于更新频繁的数据,侵犯删除缓存,会造成缓存命中率降低。对于命中率要求高的场景,可以扩展思维去解决:
1 更新数据时,更新缓存,只是在更新缓存时候加一把分布式锁,这样同一时间只允许一个线程更新缓存,对写入性能有一定的影响
2 更新数据时候,更新缓存,只是给数据加一个过期时间。这样即使有短暂的数据不一致,也不影响整体的最终一致。
以上是关于缓存的读写策略-Cache Aside的主要内容,如果未能解决你的问题,请参考以下文章
Cache Aside Pattern缓存+数据库读写模式的分析
三种缓存策略:Cache Aside 策略Read/Write Through 策略Write Back 策略