聊聊缓存模式
引言
在系统设计中,为了提高数据的处理速度缓存的使用无处不在。比如为了弥补CPU处理速度和数据IO的差距,依次有寄存器、CPU缓存、内存、硬盘金字塔模式的不同存储介质。而在上层的业务应用中,为了提高请求处理速度,通常会增加一层缓存来存储热点数据。而对于缓存数据的处理方式有如下几种模式。
旁路缓存 Cache-Aside
旁路缓存模式是开发中最常使用的一种缓存模式,它的核心思路在于仅当一个对象被请求时才将它加入缓存。该模式下缓存数据的读写流程如下:
文章图片
- 数据读取
- 业务侧发起数据查询读取请求
- 处理服务首先尝试从缓存读取加载数据
- 判断缓存中数据是否存在,如果存在则直接返回缓存数据
- 否则,从数据库等主存加载数据,并将数据写入缓存
- 返回最终查询结果
- 数据更新
- 业务侧发起数据写入/更新请求
- 处理服务首先对数据库等主存进行更新写入操作
- 完成后将对应缓存数据删除失效
- 模式分析
但该模式下数据的更新操作因为需要处理数据和缓存,且不是原子操作,所以很容易出现数据不一致的情况。需要根据实际的业务对数据一致性的要求进行处理。具体可参考《一文搞定缓存和数据库一致性》[链接]
通读/通写 Read-Through/Write-Through 通读通写模式是将数据的缓存处理操作统一进行了封装处理,增加一个缓存层,对应用屏蔽底层数据处理的细节。在旁路缓存模式中,应用需要自行处理缓存命中或未命中的处理逻辑,增加复杂度。而通过增加缓存层,业务服务仅和缓存打交道,不去关心具体数据的来源。数据读写流程如下:
文章图片
【聊聊缓存模式】=======================================
- 数据读取
- 数据更新
- 模式分析
缓存回写 Write-Behind 缓存回写和Write-Through类似,只是在Write-Though模式下会在处理请求时同时处理主存和缓存的数据,而回写模式只对缓存的数据进行更新,然后采用异步的方式将缓存中的数据回写到主存中。数据写入更新流程如下:
文章图片
缓存数据的异步回写可以采用多种方式,比如:按固定的时间频率定时回写,或者统计数据更新的次数(或固定大小)并在达到一定次数(大小)时进行回写。还可以两者结合:时间或者更新次数任何一个达到指定值则触发回写操作。
在MySQL中对于数据的更新操作即采用了类似的模式。MySQL将存储的数据按照页的方式读取到内存中,当更新数据时只会更新内存中加载的数据,这时数据的缓存页和磁盘中数据不一致,被更改的数据页称为“脏页”。在脏页被淘汰、或者数据库空闲、关闭等情况下,触发对脏页的数据回写。
- 模式分析
总结 没有任何一种模式是完美的,具体选择哪一种缓存的策略需要结合实际的业务情况,并针对所选模式的劣势或缺陷进行额外的补偿处理。
另外缓存的设计及其他很多设计模式,硬件层面和软件层面会在解决某些问题时出现一样的方案,所以了解和熟悉底层系统或硬件的优秀设计,对于上层应用和现在微服务情况下方案设计有很大的参考意义。
推荐阅读
- 不废话,代码实践带你掌握|不废话,代码实践带你掌握 强缓存、协商缓存!
- --木木--|--木木-- 第二课作业#翼丰会(每日一淘6+1实战裂变被动引流# 6+1模式)
- 想聊聊SA,聊聊手帐,也想和你们分享自己
- 设计模式-代理模式-Proxy
- 15、IDEA学习系列之其他设置(生成javadoc、缓存和索引的清理等)
- springboot使用redis缓存
- 缓存有关的配置和属性
- 【译】Rails|【译】Rails 5.0正式发布(Action Cable,API模式等)
- java静态代理模式
- VueX(Vuex|VueX(Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式)