缓存模式(Cache Aside、Read Through、Write Through、Write Behind)
概览
缓存是一个有着更快的查询速度的存储技术,这里的更快是指比起从初始的数据源查询(比如数据库,以下都称作数据库)而言。我们经常会把频繁请求的或是耗时计算的数据缓存起来,在程序收到请求这些数据的时候可以直接从缓存中查询数据返回给客户端来提高系统的吞吐量,现在我们来看看有哪些缓存模式可以考虑。
Cache-Aside
Cache-Aside是最广泛使用的缓存模式之一,如果能正确使用Cache-Aside的话,能极大的提升应用性能,Cache-Aside可用来读或写操作。
读操作
我们先来看下读操作的数据流:
- 1、程序接收数据查询的请求
- 2、程序检查要查询的数据是否在缓存上
- 如果存在(cache hit),从缓存上查询出来
- 如果不存在(cache miss),从数据库中检索数据并存入缓存中
- 3、程序返回要查询的数据

在Spring中,可如下实现,当getRecordForSearch()方法被调用的时候,如果缓存中存在对应key的数据,那就会自动的从缓存中获取(此时方法体不会被执行),当缓存中不存在key对应数据的时候,会执行方法体从数据库中查询数据并设置到缓存中去。
@Cacheable("default", key="#search.keyword)
public Record getRecordForSearch(Search search)
更新操作
如果程序需要更新数据库中的数据且该数据也在缓存上,此时缓存中的数据也需要做相应的处理。为了解决这个不同步的问题来确认数据的一致性和操作性能,有两个方式可按需使用。
缓存失效
该情况下,当请求需要更新数据库数据的时候,缓存中的值需要被删除掉(删除掉就表示旧值不可用了),当下次该key被再次查询到就去数据库中查出最新的数据,在Spring中可实现如下:
@CacheEvict("default", key="#search.keyword)
public Record updateRecordForSearch(Search search)
缓存更新
缓存数据也可以在数据库更新的时候被更新,从而在一次操作中让之后的查询有更快的查询体验和更好的数据一致性,在Spring中可实现如下:
@CachePut("default", key="#search.keyword)
public Record updateRecordForSearch(Search search)
为了应对不用类型的数据需要,有以下缓存加载策略可被选择:
- 使用时加载缓存:当需要使用缓存数据时,就从数据库中把它查询出来,第一次查询之后,接下来的请求都能从缓存中查询到数据。
- 预加载缓存:在项目启动的时候,预加载类似“国家信息、货币信息、用户信息,新闻信息”等不是经常变更的数据。
Read-Through
Read-Through和Cache-Aside很相似,不同点在于程序不需要再去管理从哪去读数据(缓存还是数据库)。相反它会直接从缓存中读数据,该场景下是缓存去决定从哪查询数据。当我们比较两者的时候这是一个优势因为它会让程序代码变得更简洁。

Write-Through
Write-Through下所有的写操作都经过缓存,每次我们向缓存中写数据的时候,缓存会把数据持久化到对应的数据库中去,且这两个操作都在一个事务中完成。因此,只有两次都写成功了才是最终写成功了。这的确带来了一些写延迟但是它保证了数据一致性。
同时,因为程序只和缓存交互,编码会变得更加简单和整洁,当你需要在多处复用相同逻辑的时候这点变的格外明显。

当使用Write-Through的时候一般都配合使用Read-Through。
Write-Through适用情况有:
- 需要频繁读取相同数据
- 不能忍受数据丢失(相对
Write-Behind而言)和数据不一致
Write-Through的潜在使用例子是银行系统。
Write-Behind
Write-Behind和Write-Through在“程序只和缓存交互且只能通过缓存写数据”这一点上很相似。不同点在于Write-Through会把数据立即写入数据库中,而Write-Behind会在一段时间之后(或是被其他方式触发)把数据一起写入数据库,这个异步写操作是Write-Behind的最大特点。
数据库写操作可以用不同的方式完成,其中一个方式就是收集所有的写操作并在某一时间点(比如数据库负载低的时候)批量写入。另一种方式就是合并几个写操作成为一个小批次操作,接着缓存收集写操作(比如5个)一起批量写入。
异步写操作极大的降低了请求延迟并减轻了数据库的负担。同时也放大了数据不一致的。比如有人此时直接从数据库中查询数据,但是更新的数据还未被写入数据库,此时查询到的数据就不是最新的数据。
总结
真实的系统中需求都不太一样,我们应该根据自己的需要来选择一个或组合几个模式来完成实现。
参考
- Cat In Code: Caching Strategies Overview
- Things You Should Know About Database Caching
- Microsoft docs: Cache-Aside pattern
- DZone: The Cache Aside Pattern
- 酷壳:缓存更新的套路
- Cache Consistency with Database
缓存模式(Cache Aside、Read Through、Write Through、Write Behind)的更多相关文章
- 微软BI 之SSIS 系列 - Lookup 组件的使用与它的几种缓存模式 - Full Cache, Partial Cache, NO Cache
开篇介绍 先简单的演示一下使用 Lookup 组件实现一个简单示例 - 从数据源表 A 中导出数据到目标数据表 B,如果 A 数据在 B 中不存在就插入新数据到B,如果存在就更新B 和 A 表数据保持 ...
- Cache-Aside Pattern(缓存模式)
Load data on demand into a cache from a data store. This pattern can improve performance and also he ...
- SQLite剖析之异步IO模式、共享缓存模式和解锁通知
1.异步I/O模式 通常,当SQLite写一个数据库文件时,会等待,直到写操作完成,然后控制返回到调用程序.相比于CPU操作,写文件系统是非常耗时的,这是一个性能瓶颈.异步I/O后端是SQLit ...
- Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式
android线程池的理解,晚上在家无事 预习了一下android异步加载的例子,也学习到了一个很重要的东东 那就是线程池+缓存 下面看他们的理解.[size=1.8em]Handler+Runna ...
- 【转】Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式
http://www.cnblogs.com/wanqieddy/archive/2013/09/06/3305482.html android线程池的理解,晚上在家无事 预习了一下android异步 ...
- KVM几种缓存模式
原文在这里: http://pic.dhe.ibm.com/infocenter/lnxinfo/v3r0m0/index.jsp?topic=%2Fliaat%2Fliaatbpkvmguestca ...
- 【转】[Android实例] Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式
android线程池的理解,晚上在家无事 预习了一下android异步加载的例子,也学习到了一个很重要的东东 那就是线程池+缓存 下面看他们的理解. [size=1.8em]Handler+Runn ...
- django 三种缓存模式的使用及注意点
Django 缓存模式的使用(主要针对RestFul设计模式的项目) 有三种模式: 全站使用缓存模式(整个项目每个接口都会使用缓存,缺点:所以接口都无法实时性获取数据) 单独视图缓存模式(单个接口使用 ...
- [Android实例] Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式
android线程池的理解,晚上在家无事 预习了一下android异步加载的例子,也学习到了一个很重要的东东 那就是线程池+缓存 下面看他们的理解. [size=1.8em]Handler+Runn ...
随机推荐
- 【bug录】安装项目编译环境bug录
安装mySQL是遇到一些问题: 刚开始按照教程配置int文件,看着图标没有显示正确,把隐藏文件夹后缀名去掉, mysql由两种版本,zip和msi格式,我用的是zip格式,mysql后进行解压,记住解 ...
- Spark执行失败时的一个错误分析
错误分析 堆栈信息中有一个错误信息:Job aborted due to stage failure: Task 1 in stage 2.0 failed 4 times, most recent ...
- Gym102012A Rikka with Minimum Spanning Trees
题意 \(T\) 组数据,每组数据给定一个 \(n\) 个点,\(m\) 条边,可能含有重边自环的图,求出最小生成树的个数与边权和的乘积,对 \(10^9+7\) 取模. \(\texttt{Data ...
- PLC模拟量采集模块分辨率是什么意思?
14位分辨率的模块(mo kuai)和16位分辨率的模块有什么不同的地方? 14位的模块最高位是符号位,我们用S表示符号位,那么这个模块的数值范围(fàn wéi)就是S111 1111 1111 1 ...
- MYSQL中inner join、left join 和 right join的区别
首先join连接是用来进行多表关联查询的,join连接方式有三种连接方式:inner join.left join 和 right join 1.inner join 可以简写成join,叫内连接,查 ...
- 水题挑战4: luogu P1280 尼克的任务
题目描述 尼克每天上班之前都连接上英特网,接收他的上司发来的邮件,这些邮件包含了尼克主管的部门当天要完成的全部任务,每个任务由一个开始时刻与一个持续时间构成. 尼克的一个工作日为 \(n\) 分钟,从 ...
- 一篇搞懂Java的基本数据类型
byte 基本类型:byte 包装类:java.lang.Byte 大小:8bit 默认值:0 取值范围:-128~127 Byte.MIN_VALUE Byte.MAX_VALUE 二进制补码表示 ...
- WSL Ubuntu 18.04 LTS + VS Code
WSL Ubuntu 18.04 LTS + VS Code WSL(Windows Subsystem for Linux)使得Windows用户能够在Windows系统上使用原生的Linux环 ...
- C#两行代码实现三维地球
一. 为什么要用三维地球? 三维地球是地理信息技术的一个重要发展方向,相比较二维地图技术,三维地球最大的特点是更直观更形象地表达地理信息和空间上的方位.我们可以在三维气象模拟. ...
- tp3.2 前端截取字符串
在Common目录中建立 function.php <?php function subtext($text, $length) { if(mb_strlen($text, 'utf8') &g ...