缓存常用于读多写少的场景,用于缓存结果数据,降低响应时间,提高服务性能。
通常缓存与数据库一起使用,数据库负责持久化,缓存负责高性能。
数据库无法同时满足持久化与高性能,所以引入缓存解决高性能问题。
缓存与数据一起使用时,何时操作缓存,是在操作数据库之前还是之后,如何操作缓存,是更新还是删除,操作失败是否会产生较大影响,都是我们需要考虑的重点问题。
在讨论具体问题前需要明确一下关键点:
缓存更新策略目标是?
目标是尽可能读取到正确数据。
何为正确数据?
以数据库数据为准,读取到当前数据库的最新数据即为正确数据。
要求读强一致吗?
不要求。该场景要求尽可能读取到正确数据。不使用复杂的2PC或Paxos多数派协议技术,尽可能降低并发时脏数据的概率。
与多副本数据一致性技术场景相似吗?
不相似。多副本数据一致性解决的是可用性问题,要求多数副本全量数据一致。数据库与缓存解决的高性能的问题,要求尽可能读取到正确数据即可,不要求缓存中有全量数据。
将缓存和数据库看成一个单一存储?
不将缓存和数据库看成一个单一存储。本场景中由应用协调缓存与数据库异构存储来满足服务高性能场景需求。
更新策略重点
缓存更新策略目标是尽可能读取到正确数据。
应用协调操作 缓存 和 数据库。
缓存操作分为 更新 和 删除。
缓存和数据库操作都存在失败情况。
应用并发操作 缓存 和 数据库。
缓存更新策略设计,需综合考虑以上问题与方法,实现业务正确性与高性能。
更新策略分析
抛开已有的缓存更新模式,考虑缓存更新策略。
缓存操作分为更新与删除,更新缓存因存在以下严重问题,所以不考虑:
并发写更新缓存,存在数据不一致的问题
A更新缓存 B更新缓存 B更新数据库 A更新数据库(数据库值为a,缓存值为b)
A更新数据库 B更新数据库 B更新缓存 A更新缓存(数据库值为b,缓存值为a)
先更新缓存后更新数据库,存在读取未更新到数据库的数据
A更新缓存,B读取缓存,A更新数据库(B读取到未更新到数据库的数据)
以下讨论删除缓存策略:
- 先删除缓存后更新数据库
该缓存更新策略操作过程为:
请求A进行写操作,删除缓存
请求B进行读操作,读取缓存,未读取到数据
请求B从数据库读取数据
请求B保存数据到缓存中
请求A更新数据库
该更新策略:
请求B读取的数据为正确数据,此时数据库中数据未更新
请求A更新数据库成功,缓存中为脏数据。如不设置ttl,则缓存中永远是脏数据
解决办法是采用延时双删策略:
删除缓存
更新数据库
延迟x秒,删除缓存
延迟时间取决于具体业务场景,根据业务写操作时间、主从延时等合理设置。
该场景下写数据库与延迟缓存删除时间间隙,存在读取脏数据情况。

- 先更新数据库后删除缓存
缓存更新策略操作1:
请求A进行写操作,更新数据库
请求B进行读操作,读取缓存
请求A删除缓存
该更新策略中请求B读取到脏数据。该情况需要需要在请求A写数据成功与缓存删除时间间隙读取数据,出现概率较小。
暂无较好方法应对,需业务容忍该情况。

缓存设置了ttl。如果没有设置ttl没有该情况。
缓存更新策略操作2:
缓存ttl到期,缓存失效
请求B进行读操作,读取缓存,未读取到数据
请求B从数据库读取数据
请求A进行写操作,更新数据库
请求A删除缓存
请求B保存数据到缓存中
该更新策略数据库数据与缓存中数据不一致,产生脏数据。
产生该情况要求比较苛刻,要求在请求B读数据库与缓存数据时间间隙完成请求A的写数据库与缓存删除操作,正常数据库写操作耗时比读操作大一个数量级,该情况出现概率较小。
但分布式系统环境复杂如网络延时等,业务逻辑特殊如读多列,读大对象都可能导致以上问题。那如何处理呢?与缓存更新策略1相同,采用延时双删策略。
延时双删策略为:
更新数据库
删除缓存
延迟x秒,删除缓存
同样该场景在请求B缓存数据与延迟缓存删除时间间隙,存在脏数据情况。

- 更新策略共性问题
以上两种更新策略都存在共同的问题,就是最后删除缓存失败如何处理?
更新策略1中,延迟删除缓存失败,则导致缓存脏数据。
更新策略2中,操作1删除缓存与操作2延迟删除缓存失败,同样导致缓存脏数据。
解决方案:
引入消息中间件,使用消息重试缓存删除。该方案引入了消息中间件,并且同样需要数据一致性问题。
采用数据库CDC,对应用无侵入,在原有更新策略的基础上多一次删除。对数据准确性要求高的应用可以采用该方案。
数据库与消息中间件的数据一致性是另外一个话题。
- 两种缓存更新策略对比
我们采用定性分析,分别分析场景出现概率与脏数据持续时间。
先删除缓存后更新数据库,出现概率中,脏数据持续时间中
先更新数据库后删除缓存,出现概率小,装书局持续时间中短
对比结论:缓存更新策略采用先更新数据库后删除缓存。

总结
缓存更新满足读多写少应用场景
缓存读取不要求强一致性,但尽可能读取到正确数据,即数据库的最新数据
缓存更新策略选择 先更新数据库后删除缓存 更能降低读取脏数据的概率
缓存根据应用场景可设置缓存ttl
如想进一步提供数据的准确性,可采用 先更新数据后删除缓存 + CDC 延迟删除 策略,此时应用可不做延时删除。
参考
分布式之数据库和缓存双写一致性方案解析
缓存与数据库一致性保证
缓存更新的套路