概览

缓存是一个有着更快的查询速度的存储技术,这里的更快是指比起从初始的数据源查询(比如数据库,以下都称作数据库)而言。我们经常会把频繁请求的或是耗时计算的数据缓存起来,在程序收到请求这些数据的时候可以直接从缓存中查询数据返回给客户端来提高系统的吞吐量,现在我们来看看有哪些缓存模式可以考虑。

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-ThroughCache-Aside很相似,不同点在于程序不需要再去管理从哪去读数据(缓存还是数据库)。相反它会直接从缓存中读数据,该场景下是缓存去决定从哪查询数据。当我们比较两者的时候这是一个优势因为它会让程序代码变得更简洁。

Write-Through

Write-Through下所有的写操作都经过缓存,每次我们向缓存中写数据的时候,缓存会把数据持久化到对应的数据库中去,且这两个操作都在一个事务中完成。因此,只有两次都写成功了才是最终写成功了。这的确带来了一些写延迟但是它保证了数据一致性。

同时,因为程序只和缓存交互,编码会变得更加简单和整洁,当你需要在多处复用相同逻辑的时候这点变的格外明显。

当使用Write-Through的时候一般都配合使用Read-Through

Write-Through适用情况有:

  • 需要频繁读取相同数据
  • 不能忍受数据丢失(相对Write-Behind而言)和数据不一致

Write-Through的潜在使用例子是银行系统。

Write-Behind

Write-BehindWrite-Through在“程序只和缓存交互且只能通过缓存写数据”这一点上很相似。不同点在于Write-Through会把数据立即写入数据库中,而Write-Behind会在一段时间之后(或是被其他方式触发)把数据一起写入数据库,这个异步写操作是Write-Behind的最大特点。

数据库写操作可以用不同的方式完成,其中一个方式就是收集所有的写操作并在某一时间点(比如数据库负载低的时候)批量写入。另一种方式就是合并几个写操作成为一个小批次操作,接着缓存收集写操作(比如5个)一起批量写入。

异步写操作极大的降低了请求延迟并减轻了数据库的负担。同时也放大了数据不一致的。比如有人此时直接从数据库中查询数据,但是更新的数据还未被写入数据库,此时查询到的数据就不是最新的数据。

总结

真实的系统中需求都不太一样,我们应该根据自己的需要来选择一个或组合几个模式来完成实现。

参考

缓存模式(Cache Aside、Read Through、Write Through、Write Behind)的更多相关文章

  1. 微软BI 之SSIS 系列 - Lookup 组件的使用与它的几种缓存模式 - Full Cache, Partial Cache, NO Cache

    开篇介绍 先简单的演示一下使用 Lookup 组件实现一个简单示例 - 从数据源表 A 中导出数据到目标数据表 B,如果 A 数据在 B 中不存在就插入新数据到B,如果存在就更新B 和 A 表数据保持 ...

  2. Cache-Aside Pattern(缓存模式)

    Load data on demand into a cache from a data store. This pattern can improve performance and also he ...

  3. SQLite剖析之异步IO模式、共享缓存模式和解锁通知

    1.异步I/O模式    通常,当SQLite写一个数据库文件时,会等待,直到写操作完成,然后控制返回到调用程序.相比于CPU操作,写文件系统是非常耗时的,这是一个性能瓶颈.异步I/O后端是SQLit ...

  4. Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式

    android线程池的理解,晚上在家无事 预习了一下android异步加载的例子,也学习到了一个很重要的东东 那就是线程池+缓存  下面看他们的理解.[size=1.8em]Handler+Runna ...

  5. 【转】Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式

    http://www.cnblogs.com/wanqieddy/archive/2013/09/06/3305482.html android线程池的理解,晚上在家无事 预习了一下android异步 ...

  6. KVM几种缓存模式

    原文在这里: http://pic.dhe.ibm.com/infocenter/lnxinfo/v3r0m0/index.jsp?topic=%2Fliaat%2Fliaatbpkvmguestca ...

  7. 【转】[Android实例] Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式

    android线程池的理解,晚上在家无事 预习了一下android异步加载的例子,也学习到了一个很重要的东东 那就是线程池+缓存  下面看他们的理解. [size=1.8em]Handler+Runn ...

  8. django 三种缓存模式的使用及注意点

    Django 缓存模式的使用(主要针对RestFul设计模式的项目) 有三种模式: 全站使用缓存模式(整个项目每个接口都会使用缓存,缺点:所以接口都无法实时性获取数据) 单独视图缓存模式(单个接口使用 ...

  9. [Android实例] Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式

    android线程池的理解,晚上在家无事 预习了一下android异步加载的例子,也学习到了一个很重要的东东 那就是线程池+缓存  下面看他们的理解. [size=1.8em]Handler+Runn ...

随机推荐

  1. mysql 改变表结构 alter

    总结:alter添加栏位时,只需记住添加新栏位为第一列,用first;添加其他用,after 前一个栏位字段,如下例 1.需求:将新的栏位添加为第二列 添加前: 添加后: 参考:http://www. ...

  2. Stream(三)

    public class Test08 { /* * 二.中间的加工操作 * (1)filter(Predicate p):过滤 * (2)distinct():去重 * (3)limit(long  ...

  3. 1.1:JAVA基础

    JAVA基础面试部分(多线程.算法.网络编程提出去了,详细分类见<面经>) 一.Java底层基础题 JDK和JRE区别? 1.JDK是整个JAVA的核心,包括了Java运行环境JRE,一堆 ...

  4. Luogu P4172 [WC2006]水管局长

    题意 给定一个 \(n\) 个点 \(m\) 条边的图和 \(q\) 次操作,每次操作分为以下两种: 1 u v:查询 \(u\) 到 \(v\) 的一条路径使得边权最大的边的权值最小. 2 u v: ...

  5. 优雅手撕bind函数(面试官常问)

    优雅手撕bind函数 前言: 为什么面试官总爱让实现一个bind函数? 他想从bind中知道些什么? 一个小小的bind里面内有玄机? 今天来刨析一下实现一个bind要懂多少相关知识点,也方便我们将零 ...

  6. MIT黑科技:通过手机记录的咳嗽数据检测是否感染新冠病毒

    这次的新冠状病毒虽然没有2002年的SARS破坏力那么强悍,但其可怕之处是长时间的无症状潜伏,使得被感染者在不知情的情况下,将病毒散播出去.如果没有强有力的防疫手段,病毒的传播几乎难以控制.而防止病毒 ...

  7. Centos中部署NetCore项目(一)

    前言 本文是基于centos8,dotnetcore3.1. (为了服务器安全使用,程序部署最好不要直接root账号进行操作.) 如果使用sudo命令时候,提示用户不在sudoers文件中的解决方法. ...

  8. C++实现RTMP协议发送H.264编码及AAC编码的直播软件开发音视频

    RTMP(Real Time Messaging Protocol)是专门用来传输音视频数据的流媒体协议,最初由Macromedia 公司创建,后来归Adobe公司所有,是一种私有协议,主要用来联系F ...

  9. 深入探索Spring Data JPA, 从Repository 到 Specifications 和 Querydsl

    数据访问层,所谓的CRUD是后端程序员的必修课程,Spring Data JPA 可以让我们来简化CRUD过程,本文由简入深,从JPA的基本用法,到各种高级用法. Repository Spring ...

  10. leetcode117:search-rotated-sorted-array

    题目描述 给出一个转动过的有序数组,你事先不知道该数组转动了多少 (例如,0 1 2 4 5 6 7可能变为4 5 6 7 0 1 2). 在数组中搜索给出的目标值,如果能在数组中找到,返回它的索引, ...