当装饰者模式遇上Read Through缓存,一场技术的浪漫邂逅
在《经验之谈:我为什么选择了这样一个激进的缓存大Key治理方案》一文中,我提到在系统中使用的缓存是旁路缓存模式,有读者朋友问,有没有用到过其他的缓存模式,本文将结合一个我曾经工作中的案例,使用装饰者模式实现Read Through缓存模式,助你轻松掌握设计模式和缓存。
一、缓存模式
不说废话,直入主题。缓存模式是指用于管理缓存数据的策略和方式。常见的有这几种:Cache Aside、Read Through、Write Through、Write Back等。

Cache Aside
在平时的开发工作中,旁路缓存是最常用的一种缓存模式,“旁路”二字意味着读取缓存、读取数据、更新缓存和操作都是在应用程序中完成的,缓存和数据库之间是没有连接的。

Read Through
在这种模式下,缓存与数据库是连接起来的。当缓存未命中的时候,缓存中数据库中加载数据,填充缓存并返回给应用程序。

虽然Read Through和Cache Aside非常相似,但至少有两个关键区别:
- 在Cache Aside中,应用程序负责从数据库获取数据并填充缓存。在Read Through中,此逻辑通常由库或独立缓存提供程序来实现。
- 与Cache Aside不同,Read Through缓存中的数据模型不能与数据库的数据模型不同。
Write Through
在穿透写入模式下,当应用程序需要更新数据时,它会先更新到缓存,同时更新到数据源,这两个操作在共一个事务中完成。因此只有2个都写成功了才会最终写成功,有助于保持缓存和数据源之间的数据一致性。

当应用程序想要写入数据或更新值时,会发生以下情况:
- 应用程序将数据直接写入缓存。
- 缓存更新主数据库中的数据。当写入完成后,缓存和数据库都具有相同的值,并且缓存始终保持一致。
Write Back
在这个模式下,当写入数据的时候,只是写到了缓存。当缓存过期的时候,才会被刷新到数据库。这个模式最大的一个问题就是如果缓存突然宕机,那么还没有刷新到数据库的数据就彻底丢失了。

说明
我认为严格来说,现在的缓存工作模式都归属于旁路缓存。如果在代码中的缓存层(类似于Mapper层)进行了封装的话;那么在业务代码中调用缓存层方法进行操作,这里屏蔽了缓存的实现细节,站在业务代码层面来看,可以暂且认为是实现了不同的缓存模式吧。
二、代理模式和装饰者模式
那如何优雅实现上述缓存层的封装,在开发中可以丝滑接入呢?接着看,这里需要使用一些设计模式。
这是代理模式、装饰者模式的代码结构示例:二者结构完全一致。
// 装饰者模式代码结构
public interface AInterface {
void run();
}
public class A implements AInterface {
@Override
public void run() {
// run
System.out.println("A run...");
}
}
public class ADercorator implements AInterface {
private AInterface a;
public ADercorator(AInterface a) {
this.a = a;
}
@Override
public void run() {
// doSomething
System.out.println("ADerocator do something...");
a.run();
// doSomething
}
}
当然代理模式和装饰者模式还是有区别的,主要是看应用的场景。
代理模式主要是为其他对象提供一种代理,以控制对这个对象的访问。
例如有一个案例这样的,一个RPC接口的提供方和调用方都是双机房部署的,原本的远程调用也是机房垂直调用(机房A只调用机房A,机房B只调用机房B)的;由于接口提供方接口性能原因,希望调用方改为跨机房调用(机房A同时调用机房A和机房B,机房B同时调用机房A和机房B)。我的实现方式是使用代理模式,在代码中新增一个接口调用的代理层,在代理层中注入机房A和机房B的RPC Consumer配置,根据时间戳的奇偶来判断调用哪个机房,从而实现跨机房调用。
而装饰者模式则指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式。在《Head First 设计模式》一书中,更是将装饰者模式称为“给爱用继承的人一个全新的设计眼界”。例如对于不同缓存模式的实现,可以使用装饰者模式。
三、装饰者模式实现Read Through缓存模式
这是我之前做过的一个RPC读接口性能优化的案例。系统架构比较简单,就是一个常规的Java Web应用,对外提供RPC服务,底层数据采用的是MySQL,缓存使用的是Redis。

该查询接口涉及数据库多张MySQL表和多个外部RPC接口的查询。
在接口性能优化过程中,
对于多张表查询,检查慢SQL并进行了索引优化;
对于多外部RPC接口调用改为并行调用;
后续又使用了Redis缓存进行缓存数据。
当时刚进入职场不久,作为一个职场新人,年轻气盛,还想着彰显一下个人的技术,所以我打算使用装饰者模式来实现Read Through缓存模式;并且实现了一个当时认为稍微“展示一点技术”的方案:当缓存未命中时,从数据库加载数据,返回业务层,将写入缓存改为异步写入。
大概代码如下,大家参考:
public interface CacheInterface {
String get(String key);
String set(String key, String value);
}
public class Cache implements CacheInterface {
private Jedis jedis;
@Override
public String get(String key) {
// run
System.out.println("Cache get...");
return jedis.get(key);
}
@Override
public String set(String key, String value) {
return jedis.set(key, value);
}
}
public class CacheDecorator implements CacheInterface {
private Repository myRepositoty = new MyRepository();
private CacheInterface cache;
public CacheDecorator(CacheInterface a) {
this.cache = cache;
}
@Override
public String get(String key) {
// doSomething
System.out.println("CacheDecorator do something...");
String cacheResult = cache.get(key);
if (!StringUtils.isEmpty(cacheResult)) {
return cacheResult;
}
String result = myRepositoty.get(key);
CompletableFuture.runAsync(() -> cache.set(key, result));
return result;
}
@Override
public String set(String key, String value) {
return cache.set(key, value);
}
}
public class Test {
public static void main(String[] args) {
Cache cache = new Cache();
CacheDecorator derocator = new CacheDecorator(cache);
derocator.get("a");
}
}
一起学习
欢迎各位在评论区或者私信我一起交流讨论,或者加我主页weixin,备注技术渠道(如博客园),进入技术交流群,我们一起讨论和交流,共同进步!
也欢迎大家关注我的博客园、公众号(码上暴富),点赞、留言、转发。你的支持,是我更文的最大动力!
参考资料
https://codeahoy.com/2017/08/11/caching-strategies-and-how-to-choose-the-right-one/
当装饰者模式遇上Read Through缓存,一场技术的浪漫邂逅的更多相关文章
- JavaScript设计模式-16.装饰者模式(上)
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- 设计模式(十):Decorator装饰者模式 -- 结构型模式
1. 概述 若你从事过面向对象开发,实现给一个类或对象增加行为,使用继承机制,这是所有面向对象语言的一个基本特性.如果已经存在的一个类缺少某些方法,或者须要给方法添加更多的功能(魅力),你也许会仅仅继 ...
- 设计模式(八)装饰器模式Decorator(结构型)
设计模式(八)装饰器模式Decorator(结构型) 1. 概述 若你从事过面向对象开发,实现给一个类或对象增加行为,使用继承机制,这是所有面向对象语言的一个基本特性.如果已经存在的一个类缺少某些方法 ...
- Java IO流以及装饰器模式在其上的运用
流概述 Java中,流是一种有序的字节序列,可以有任意的长度.从应用流向目的地称为输出流,从目的地流向应用称为输入流. Java的流族谱 Java的 java.io 包中囊括了整个流的家族,输出流和输 ...
- JAVA装饰者模式(从现实生活角度理解代码原理)
装饰者模式可以动态地给一个对象添加一些额外的职责.就增加功能来说,Decorator模式相比生成子类更为灵活. 该模式的适用环境为: (1)在不影响其他对象的情况下,以动态.透明的方式给单个对象添加职 ...
- 设计模式(三):“花瓶+鲜花”中的装饰者模式(Decorator Pattern)
在前两篇博客中详细的介绍了"策略模式"和“观察者模式”,今天我们就通过花瓶与鲜花的例子来类比一下“装饰模式”(Decorator Pattern).在“装饰模式”中很好的提现了开放 ...
- 设计模式(九)装饰者模式(Decorator Pattern)
一.引言 在软件开发中,我们经常想要对一类对象添加不同的功能,例如要给手机添加贴膜,手机挂件,手机外壳等,如果此时利用继承来实现的话,就需要定义无数的类,如StickerPhone(贴膜是手机类).A ...
- C#设计模式-装饰者模式
在软件开发中,我们经常想要对一类对象添加不同的功能,例如要给手机添加贴膜,手机挂件,手机外壳等,如果此时利用继承来实现的话,就需要定义无数的类,如StickerPhone(贴膜是手机类).Access ...
- Java 的设计模式之一装饰者模式
刚开始接触装饰者的设计模式,感觉挺难理解的,不够后来花了一个晚上的时间,终于有头绪了 装饰者设计模式:如果想对已经存在的对象进行装饰,那么就定义一个类,在类中对已经有的对象进行功能的增强或添加另外的行 ...
- 设计模式-装饰器模式(Decrator Model)
文 / vincentzh 原文连接:http://www.cnblogs.com/vincentzh/p/6057666.html 目录 1.概述 2.目的 3.结构组成 4.实现 5.总结 1.概 ...
随机推荐
- OpenHarmony技术日探讨教育发展,聚焦开源人才培养
4 月 25 日,OpenAtom OpenHarmony (以下简称"OpenHarmony")技术日在深圳成功举办.作为 OpenHarmony 开源项目的年度盛会,大会以&q ...
- Jchardet——支持检测并输出文件编码方式的组件
简介 Jchardet是OpenAtom OpenHarmony(以下简称"OpenHarmony")系统的一款检测文本编码的组件.当上传一个文件时,组件可以检测并输出该文件中 ...
- Qt调用摄像头二,Pro版
本示例,为纯Qt调用摄像头,功能会比版本一要多一点:打开摄像头,设置参数,完整拍照,框选拍照,切换分辨率,旋转,水平镜像,垂直镜像,放大,缩小 上一个版本,使用的显示窗口直接显示出摄像头画面,所以可操 ...
- sql 语句系列(字符串之裂开)[八百章之第十三章]
创建分割列表 一张表: 先查询出来的效果是这样的: mysql: select emp_copy.deptno,GROUP_CONCAT(emp_copy.emps SEPARATOR ',') fr ...
- 前端之多线程 ---webworker
一.啥是workerJavaScript为单线程,worker则为JavaScript创建多线程环境.使用场景如:计算文件hash,计算大于1G的文件hash过程是很慢的,但由于要将hash传给后端, ...
- 使用JSZip实现在浏览器中操作文件与文件夹
1. 引言 浏览器中如何创建文件夹.写入文件呢? 答曰:可以借助JSZip这个库来实现在浏览器内存中创建文件与文件夹,最后只需下载这个.zip文件,就是最终得结果 类似的使用场景如下: 在线下载很多图 ...
- 2024-04-21:用go语言,给一棵根为1的树,每次询问子树颜色种类数。 假设节点总数为n,颜色总数为m, 每个节点的颜色,依次给出,整棵树以1节点做头, 有k次查询,询问某个节点为头的子树,一共
2024-04-21:用go语言,给一棵根为1的树,每次询问子树颜色种类数. 假设节点总数为n,颜色总数为m, 每个节点的颜色,依次给出,整棵树以1节点做头, 有k次查询,询问某个节点为头的子树,一共 ...
- MyBatis源码之前言—JDBC编码存在的问题和Mybatis的介绍
MyBatis源码之前言-JDBC编码存在的问题和Mybatis的介绍 为了方便操作,我们在sjdwz_test数据库下建立一张表: CREATE TABLE `t_student` ( `id` b ...
- ModelScope初探:一行代码调用成熟AI模型。
简介: 如何用一行代码调用成熟AI模型?试试ModelScope,让AI开发者解放生产力! ModelScope是阿里推出的下一代开源的模型即服务共享平台,为泛AI开发者提供灵活.易用.低成本的一站式 ...
- 【全观测系列】Elasticsearch应用性能监控最佳实践
简介:本文介绍了应用性能监控的应用价值以及解决方案等. 1.什么是全观测? 要了解全观测,我们先看看传统运维存在哪些问题. 数据孤岛,分散在不同部门,分析排查故障困难: 多个厂商的多种工具,无法自动 ...