在应用层如何保障缓存的一致性
一般在做应用的时候,我们都会在DB层上面架一个缓存层,或者直接把缓存集成在业务代码里。anyway,无论把缓存放到哪一层,目的都是为了减少DB层的压力,因为应用层我们很容易做成集群,但是DB做成集群的复杂度比较大。用缓存架在DB上层,既可以减轻DB的负载,也可以增加应用的业务吞吐率。
但是,缓存和DB之间如何同步数据,是一个比较棘手的问题,如果缓存和DB数据不一致,访问缓存的效率确实提高了,但是业务可能会出错,在某些重要的业务场景,是没办法容忍这种错误出现的。所以今天来聊聊如何能够优雅的做好缓存和DB之间的数据一致性。
缓存的更新策略
当一条数据需要更新时,我们是先更新DB还是先更新缓存呢?这里涉及到了缓存的更新策略,不同的缓存更新策略,在保障数据的一致性方面有不同的差异。
Write Through:用同步的方式,先更新DB,再更新Cache
Write Behind:先更新Cache,然后用异步的方式更新DB
Write Invalidate:以同步的方式,先更新DB,然后让Cache失效(和Write Through的区别是这次不直接更新Cache)
Refresh Ahead:预测热点数据,提前把热点数据Cache起来,然后定期更新Cache
我们可以分析一下这几种方式的优缺点。
Write Through:非常安全,不会导致数据丢失,因为是先更新DB,但是安全的代价是同步操作的性能比较差,因为这种场景还得需要配合锁一起来使用,防止其他线程读取到了旧的Cache数据,要等到DB和Cache都更新完,这中间可能会因为 磁盘I/O或者Socket I/O 导致花费非常多的时间,高并发场景一般很难接受这种方案。
Write Behind:性能确实比Write Behind好一些,但是这种方案不够安全,例如,如果更新完Cache之后,程序直接挂掉了,那DB就没办法同步到这条数据的更新操作。
Write Invalidate:Write Through的优化方案,同步等待的时间更短一些,因为少了更新Cache这一步,但是这意味着如果更新操作比较频繁,那么每次读取的缓存命中率就很低,因为要经常去数据库里读取最新的数据,在更新次数多的场景下,这种方案不完美。
Refresh Ahead:适合小批量数据,并且需要提前知道热点数据,这个策略的使用场景有一点苛刻,只能针对某些固定数据做缓存。
没有完美的方案
在了解了这几种缓存策略之后, 我们可以发现,没有任何一种方案总是完美的,不同的策略都有不同的优缺点。
在大部分场景下Refresh Ahead 是和 Write Through | Write Behind | Write Invalidate 配合一起用的,那这3种写方案,具体用什么,更多的是根据业务场景来选择。