一致性
强一致性:系统写入的数据可以立即被读到,用户体验好,但是系统性能影响大。
弱一致性:不能保证什么时候能读到写入的值,只是尽可能在某段时间后能达到一致的状态。
最终一致性:系统保证在一定时间内能达到一致性状态。
经典缓存模式
Cache-Aside
读数据时,先查找缓存,如果缓存中有对应的数据直接返回,如果没有就到数据库查找然后更新到缓存中,再返回数据。
写数据时,先更新数据库,然后删除缓存。
Read-Throught/Write-Throught
这种模式下读写类似于Cache-Aside模式,只是在于缓存和数据库的读写时中间有个Cache-provider。
Write-behind
Write-behind跟Read-Through/Write-Through 一样通过Cache Provider进行缓存和数据库读写,但是更新数据时是直接更新缓存,数据库是异步更新的方式。
这种方式适用于对一致性要求较低的系统,适合频繁写的场景。
更新数据时应该删除缓存还是更新缓存
上面的例子中,线程A更新完数据库之后,线程B将数据库和缓存都更新了,然后线程A再去更新缓存,此时缓存中的数据是历史数据,数据不一致。如果是删除缓存则不会,因为删除之后如果有读的操作的话会从数据库读出当前的数据更新到缓存,保证了一致性。同样如果是A先更新缓存B再将缓存和数据库都更新,A最后更新数据库,一样会导致数据不一致。
更新缓存还有两个缺点:
1、如果写入缓存的值需要复杂的计算且更新频繁的话,会浪费系统性能。
2、写多读少时,也会浪费性能,数据可能没被读到就更新了。写多读少的场景用缓存本身也不是很划算。
更新数据时先操作数据库还是先操作缓存
以上是先操作缓存再操作数据库的例子。(通过之前的分析,缓存的操作应该是删除缓存更合理)线程A先删除缓存,然后线程B读数据,发现缓存没有对应的数据,到数据库读取数据更新缓存,然后A再更新数据库,此时缓存中的数据和数据库中的数据不一致。
而先更新数据库再删除缓存正常情况下不会造成数据不一致,只会在短时间内造成不一致,该时间为更新数据库到删除缓存这段时间。导致不一致一般是因为删除缓存失败了。
删除缓存失败的解决方案
延时双删
该策略就是在删除缓存并更新数据库一段时间之后再次删除缓存,但是如果第二次删除失败,那么就会进入先删缓存再删数据库的情形,不能避免数据不一致。
重试机制
删除缓存失败后,将对应的key放到消息队列,然后从消息队列获取要删除的key,重试删除的操作。
读取binlog异步删除
重试机制的缺点在于更新数据时需要将消息队列的代码嵌入到业务代码中。
获取binlog异步删除:将binlog日志采集放到队列里,然后从消息队列里消费消息删除缓存。
参考文章:公众号:捡田螺的小男孩的文章《美团二面:Redis与MySQL双写一致性如何保证?》