问题
关于【数据库和缓存】一致性,下面哪几项是在线上生产环境中相对合理的处理方式?
A. 对于查询操作,先查缓存,如果为空则查 DB,然后将数据带入缓存;
B. 对于插入操作,只写 DB 即可;
C. 对于删除操作,先删除缓存,再删 DB;
D. 对于更新操作,先更新 DB,再更新缓存。
解析
需要先明确两个问题:
1、引入缓存的目的是什么
引入缓存是为了提供高性能的数据读取;也就是缓存是为了抗高吞吐的读操作,而非写操作。
2、数据库和缓存的适用场景是什么
首先,业务如果是追求数据的强一致性,则不用缓存;也就是数据库和缓存之间实现的是最终一致性,只是不同业务对数据不一致的时间窗口大小要求不同而已;然后,对于写多读少的场景,不用缓存;根据引入缓存的目的分析,数据库和缓存配合适合的是读多写少的业务场景。
为了方便实现数据库和缓存之间的最终一致性,从实现成本和降低复杂性考虑,我们从整体上设计完整的实现思路,如下:
1、 插入操作:只写数据库即可
如果写完数据库后,再写缓存,在并发场景下,会产生ABBA问题;
所谓ABBA问题是这样的:客户端A 要对变量X写入5, 同时客户端B要对X写入6;因为是并发,所以写入的时序很可能为【数据库X=5(A 操作)】【数据库X=6(B操作)】【缓存X=6(B操作)】【缓存X=5(A操作)】,结果就是 数据库中的X=6而缓存中的X=5。
2、查询操作:先查询 缓存,如果为空则查询 数据库,然后将查询到的数据写入缓存
在并发场景下,最多会导致对缓存的重复写入,浪费一点算力而已,但不会导致数据库和缓存的不一致性。
3、删除操作:先删除数据库,再删除缓存
釜底抽薪,所以先删除数据库中的数据;如果先删除缓存,会很容易因为查询操作导致数据再次写入缓存;这个地方需要注意的是,删除缓存操作有可能失败,我们后面集中解决这个问题。
4、更新操作:先更新数据库,再删除缓存
为什么是删除缓存,而非更新缓存呢?如果是更新缓存,在并发场景下仍然会很容易出现ABBA的问题;所以删除缓存简单粗暴,让数据为空(在查询操作中让数据补齐即可),非常有效地避免了数据库和缓存的不一致性;这个地方也需要注意的是,删除缓存操作有可能失败,同样我们后面集中解决它。
整体来看,对于CRUD操作有了一个完整的数据库和缓存一致性的解决方案;唯一需要解决的问题就是:删除缓存失败时,该如何处理?
1. 通过缓存的过期策略删除key
2. 基于事务消息对缓存进行删除
3. 基于binlog方式对缓存进行删除
4. 通过离线任务对删除失败的缓存key进行清理
5. 基于分布式锁解决ABBA问题
答案
AB