在互联网架构中,面对高流量的业务场景,数据库+缓存是一个经常被使用的组合策略。而在使用数据库+缓存的业务场景中,数据库缓存数据一致性
无疑是最重要的一个点。
接下我们将分析一些不同的缓存策略,以及各方案的优缺点。业务场景主要基于数据读取
、数据更新
两大场景。
缓存更新方式
1.cache aside
在cache aside模式下,是由Application层去做缓存数据的刷新操作。
在数据更新场景下,也可以选择在更新完数据库后,直接删除缓存。然后在下次读取时,按照数据查询场景去加载缓存。
2.read through / write through
在cache aside模式下,我们Application需要同时去处理数据库逻辑和缓存逻辑。
Read/Write through模式下,Application只和cache交互,由cache自己去处理从数据库加载数据、更新数据库工作。
3.write back(write behind)
write back和write through运行模式一致,但是数据库更新操作采用的是异步模式,因此可以极大的提高写数据的效率。但是带来的问题是数据更新不能及时的更新到数据库,另外cache也需要记录数据更新历史并进行异步刷新数据库,带来了编程的复杂性。
常见问题及解决方案
上文讲述的是redis的数据更新策略。在实际高并发、分布式环境下。缓存的使用会面临着不同的挑战。
延迟双删
延迟双删主要是为了解决数据发生更新时,数据库缓存一致性问题。
- 如果先删除缓存,再更新数据库。此时如果先删除有并发的查询请求,就可能在两个操作之间,读取缓存miss并加载旧的数据到缓存中。
- 如果先更新数据库,再删除缓存。可能会有删除缓存失败的场景(可以通过重试解决)。以及极端时序问题(在更新数据库前缓存已失效,此时查询线程A查询出旧数据,并在更新线程删除缓存后,才更新缓存,导致缓存里是旧数据)
-
同时对于主从数据库,还需要考虑主从延迟问题。
较为简单的方案是:删缓存 --> 更新数据库 --> 延迟一段时间再次删除缓存(解决并发场景及主从问题)。
如果要保证数据库缓存的完全一致,则需要考虑引入分布式事务。
2.缓存穿透
缓存穿透,是指大量请求缓存miss,导致请求打到数据库上。
这种情况,可以针对数据库中不存在的值,缓存空对象,避免缓存穿透。
如果是有大量恶意的请求,上述方式依然不能够解决问题,此时可以考虑使用布隆过滤器,如果缓存miss,先用过滤器判断,如果数据一定不存在,则不请求数据库。
参考资料
https://blog.bluzelle.com/things-you-should-know-about-database-caching-2e8451656c2d
https://vladmihalcea.com/a-beginners-guide-to-cache-synchronization-strategies/
https://codeahoy.com/2017/08/11/caching-strategies-and-how-to-choose-the-right-one/
https://coolshell.cn/articles/17416.html