聊聊缓存模式

引言 在系统设计中,为了提高数据的处理速度缓存的使用无处不在。比如为了弥补CPU处理速度和数据IO的差距,依次有寄存器、CPU缓存、内存、硬盘金字塔模式的不同存储介质。而在上层的业务应用中,为了提高请求处理速度,通常会增加一层缓存来存储热点数据。而对于缓存数据的处理方式有如下几种模式。
旁路缓存 Cache-Aside 旁路缓存模式是开发中最常使用的一种缓存模式,它的核心思路在于仅当一个对象被请求时才将它加入缓存。该模式下缓存数据的读写流程如下:
聊聊缓存模式
文章图片

  • 数据读取
  1. 业务侧发起数据查询读取请求
  2. 处理服务首先尝试从缓存读取加载数据
  3. 判断缓存中数据是否存在,如果存在则直接返回缓存数据
  4. 否则,从数据库等主存加载数据,并将数据写入缓存
  5. 返回最终查询结果
  • 数据更新
  1. 业务侧发起数据写入/更新请求
  2. 处理服务首先对数据库等主存进行更新写入操作
  3. 完成后将对应缓存数据删除失效
  • 模式分析
旁路缓存的设计模式主要适用于对数据的读取频率远大于写入更新频率的场景下,即数据一旦写入后数据不再变换或很少变化的情况,并通过采用LRU等数据淘汰策略,缓存热点数据。
但该模式下数据的更新操作因为需要处理数据和缓存,且不是原子操作,所以很容易出现数据不一致的情况。需要根据实际的业务对数据一致性的要求进行处理。具体可参考《一文搞定缓存和数据库一致性》[链接]
通读/通写 Read-Through/Write-Through 通读通写模式是将数据的缓存处理操作统一进行了封装处理,增加一个缓存层,对应用屏蔽底层数据处理的细节。在旁路缓存模式中,应用需要自行处理缓存命中或未命中的处理逻辑,增加复杂度。而通过增加缓存层,业务服务仅和缓存打交道,不去关心具体数据的来源。数据读写流程如下:
聊聊缓存模式
文章图片

【聊聊缓存模式】=======================================
  • 数据读取
数据读取流程和旁路缓存类似,只是将缓存逻辑由缓存层统一处理。
  • 数据更新
数据更新时,由缓存层作为一个事务同时更新主存和缓存数据。
  • 模式分析
通读/通写模式适用于对相同数据频繁读写的情况,并且对主存和缓存数据有强一致性要求的情况,例如银行的业务系统。
缓存回写 Write-Behind 缓存回写和Write-Through类似,只是在Write-Though模式下会在处理请求时同时处理主存和缓存的数据,而回写模式只对缓存的数据进行更新,然后采用异步的方式将缓存中的数据回写到主存中。数据写入更新流程如下:
聊聊缓存模式
文章图片

缓存数据的异步回写可以采用多种方式,比如:按固定的时间频率定时回写,或者统计数据更新的次数(或固定大小)并在达到一定次数(大小)时进行回写。还可以两者结合:时间或者更新次数任何一个达到指定值则触发回写操作。
在MySQL中对于数据的更新操作即采用了类似的模式。MySQL将存储的数据按照页的方式读取到内存中,当更新数据时只会更新内存中加载的数据,这时数据的缓存页和磁盘中数据不一致,被更改的数据页称为“脏页”。在脏页被淘汰、或者数据库空闲、关闭等情况下,触发对脏页的数据回写。
  • 模式分析
缓存回写的模式一方面因为业务请求仅需要对换粗数据更新即可完成操作,极大的降低了数据写入的时间,提高了处理效率;另一方面因为缓存数据和主数据的不一致,会导致出现读取到老数据的情况。另外由于写入主存储是有延时的,在异常情况下可能出现数据丢失。因此采用该模式需要能容忍一定的数据不一致,并且对可能的数据丢失可以接受或者补偿。
总结 没有任何一种模式是完美的,具体选择哪一种缓存的策略需要结合实际的业务情况,并针对所选模式的劣势或缺陷进行额外的补偿处理。
另外缓存的设计及其他很多设计模式,硬件层面和软件层面会在解决某些问题时出现一样的方案,所以了解和熟悉底层系统或硬件的优秀设计,对于上层应用和现在微服务情况下方案设计有很大的参考意义。

    推荐阅读