分布式多级缓存SDK设计的思考
分布式多级缓存SDK设计的思考
- 背景
- 整体架构
- 多层级组装
- 回调埋点
- 分区处理
- 一致性问题
- 缓存与数据库之间的一致性问题
- 不同层级缓存之间的一致性问题
- 不同微服务实例上,非共享缓存之间的一致性问题
- 小结
之前实习期间编写过一个简单的多级缓存SDK,后面了解到一些其他的开源产品,如J2Cache,京东的JdHotKey,有赞的多级缓存SDK实现,所以本文想来总结一下我对多级缓存SDK设计的考量和开发心得。
参考的相关开源实现链接:
- 有赞透明多级缓存解决方案(TMC)
- J2Cache
- hotkey
背景
编写这个SDK起因于部门各个服务缓存使用上的不统一,有些没有使用缓存,有些单独使用了本地缓存或者Redis集中式缓存,还有些使用了阿里的Tair缓存,因此为了结束缓存使用混乱的局面,就有了这个多级缓存SDK Demo 。
我们期望这个多级缓存SDK能够满足以下目标:
- 支持自定义缓存层级和缓存层级之间的顺序,例如: 可以是Caffeine+Redis的组合,也可以是Caffeine + Tair + Redis的组合
- 需要与链路追踪工具Cat结合,定时上报缓存工作状态,如: 全局缓存命中率,各级缓存命中率等
- 需要支持灰度与开关机制,灰度用于控制走缓存比例,开关用于上下线该SDK,或者单独上下线某一级缓存
- 需要处理好缓存穿透,缓存击穿,缓存与数据库一致性,多级缓存间的一致性,以及分布式环境下,各个实例上非共享的L1级缓存的一致性问题。
以上四点是我目前所能想到的内容,也是我所开发的SDK支持的功能,如果大家有补充欢迎在评论区留言。
下面我将从整体架构讲起,一直聊到以上所说的细节实现。
整体架构
多级缓存SDK整体架构如何所示:
- CacheFacade 作为缓存门面对象,向用户屏蔽了内部多个模块协同工作的复杂性,同时负责编排多级缓存 get 和 set 的模版流程,并在相关位置进行回调埋点,方便后续扩展。
- CacheRepository 作为多级缓存实现,采用装饰器模式层级嵌套关系,缓存的 get 流程是先走低层级缓存,再走高层级缓存;set 和 del 流程是先走高层级缓存,再走低层级缓存。
- CacheConfig 作为配置模块,收拢了整个缓存SDK所有的配置项,同时采用SPI机制可以实现配置中心的动态切换,默认只提供了ApolloConfigProvider,用于支持Apollo作为配置中心。
- CachePostProcessor 顶层提供了相关默认接口实现,如果我们希望能够在缓存执行的某个流程处进行监听,可以重写相关接口实现,添加对应的拦截逻辑,然后将自身交于缓存后置处理模块管理即可。
- CacheCluster 负责实现多个实例之间的非共享L1级缓存的一致性,当有请求试图在某个实例上执行set或者del操作时,都需要广播告知其他实例,用于清除自身的L1级缓存。
整个缓存SDK的架构还是非常简单的,下面我将针对各处细节进行说明。
多层级组装
多级缓存SDK默认情况下会提供Caffeine+Redis的两级缓存,但是如果业务有特殊需求,不满足于此,我们也可以自定义缓存层级 :
为了支持自定义缓存层级,这里采用装饰器模式的层层装饰来实现多级缓存的效果,伪代码如下图所示:
public abstract class AbstractCacheRepositoryWrapper implements CacheRepository {private final CacheRepository wrappedCacheWrapper;public AbstractCacheRepositoryWrapper(CacheRepository wrappedCacheWrapper) {this.wrappedCacheWrapper = wrappedCacheWrapper;}...
}
接入方只需要为接入的缓存提供一个CacheRepository实现,并且自行完成装饰层级的嵌套组装,最后将组装得到的对象实例交由CacheFacade管理即可;如果项目使用到了Spring ,这里可以将对象实例注入容器,CacheFacade 由容器中取得即可。
回调埋点
CacheFacade 作为缓存门面对象,向用户屏蔽了内部多个模块协同工作的复杂性,同时负责编排多级缓存 get 和 set 的模版流程,并在相关位置进行回调埋点,方便后续扩展。
因为 CacheFacade 拿到的其实是已经组装完毕的多级缓存对象,如下图所示:
所以这里 get 和 set 请求要分为两段来看,一段是存在于缓存门面对象中设定好的模版流程,另一段是存在于AbstractCacheRepositoryWrapper中设定好的多级缓存间的get,set,del 流程。
我们先来看看缓存门面对象中设置设定好的模版流程和相关回调埋点的工作时机:
- get 流程
-
list 流程
-
set 流程
- del 流程
上面可以理解为全局缓存的执行流程,下面我们来看看存在于AbstractCacheRepositoryWrapper中设定好的多级缓存间的get,set,del 流程:
- get 流程
- list 流程
- set 流程
- del 流程
上述流程图中各处埋点均以绿色标出,每当执行到埋点处时,都会去调用后置处理器链,后置处理器又分为全局后置处理器和局部后置处理器,前者工作在缓存门面设定的全局流程中,后者工作在多级缓存的局部流程中。
如果后续有扩展需求,只需要自定义一个后置处理器,加入后置处理器链中即可。
分区处理
我是从J2Cache中了解到的分区Region的思想,也在随后添加到了我自己开发的多级缓存SDK中,这里简单介绍一下为什么我们需要分区:
- 在实际的缓存场景中,不同的数据会有不同的 TTL 策略,例如有些缓存数据可以永不失效,而有些缓存我们希望是 30 分钟的有效期,有些是 60 分钟等不同的失效时间策略。在 Redis 我们可以针对不同的 key 设置不同的 TTL 时间。但是一般的 Java 内存缓存框架(如 Ehcache、Caffeine、Guava Cache 等),它没法为每一个 key 设置不同 TTL,因为这样管理起来会非常复杂,而且会检查缓存数据是否失效时性能极差。所以一般内存缓存框架会把一组相同 TTL 策略的缓存数据放在一起进行管理。
- 通过分区可以将属于不同业务的缓存隔离开来,防止相互污染,比如我们使用LRU缓存,所有业务共用一个LRU缓存,如果业务A总是大批次查询,那么可能会将其他业务热点key给淘汰出去,造成污染问题。
采用分区之后,CacheFacade 门面对象内部也就不是简单持有一个多级缓存实例对象了,而是持有一个多级缓存实例映射集合,如下图所示:
此时,我们的 get 和 set 等方法也都需要改造,在方法参数处添加一个 region,指明要操作哪个 region。
一致性问题
一致性问题主要考虑三点:
- 缓存与数据库之间的一致性问题
- 不同层级缓存之间的一致性问题
- 不同微服务实例上,非共享缓存之间的一致性问题
缓存与数据库之间的一致性问题
关于缓存与数据库之间的一致性问题,这里我简单介绍其中一种方案:
之前写过一篇文章讲述缓存与数据库一致性问题,这里就直接把图贴过来了
- 旁路缓存模式: 先更新数据库,再删除缓存
可能存在的问题是: 两个并发线程,一个读,一个写,读线程发现缓存失效,去数据库查询数据,查询完后更新redis,但是更新redis前,写线程率先完成了写入操作,导致读线程最终放入redis的还是旧数据
不过,实际上出现的概率可能非常低,因为这个条件需要发生在读缓存时缓存失效,而且并发着有一个写操作。而实际上数据库的写操作会比读操作慢得多,而且还要锁表,而读操作必需在写操作前进入数据库操作,而又要晚于写操作更新缓存,所有的这些条件都具备的概率基本并不大。
为了避免旁路缓存出现这个问题,我们可以采用缓存双删策略:
- 先删除缓存
- 再更新数据库
- 休眠一会(比如1秒),再次删除缓存。
这个休眠一会,一般多久呢?都是1秒?
- 这个休眠时间 = 读业务逻辑数据的耗时 + 几百毫秒。 为了确保读请求结束,写请求可以删除读请求可能带来的缓存脏数据。
不管是延时双删还是Cache-Aside的先操作数据库再删除缓存,如果第二步的删除缓存失败呢,删除失败会导致脏数据产生,因此为了保险起见,我们需要增加删除失败的重试逻辑:
- 写请求更新数据库
- 缓存因为某些原因,删除失败
- 把删除失败的key放到消息队列
- 消费消息队列的消息,获取要删除的key
- 重试删除缓存操作
上述逻辑可能会造成业务代码入侵,我们可以考虑使用canal监听binlog的修改变更,将所有修改消息发送到MQ,然后通过ACK机制确认处理这条更新消息,删除缓存,保证数据缓存一致性。
不同层级缓存之间的一致性问题
假设此时我的多级缓存层级是两层: Caffeine+Redis ,那么如何确保这两者之间的数据一致性呢 ?
首先,我们要明白一点:
- 离服务越近的缓存源,其存储容量越小,速度越快,过期时间越短
- 离服务越远的缓存源,其存储容量越大,速度越慢,过期时间越长
这里其实很像CPU多级缓存体系,为了保证多级缓存之间的数据一致性,需要分以下几个方面讨论:
- 查询先从L1级缓存查起,如果L1没有,再查询L2,如果L2也没有,那么查询DB;返回阶段,会依次把上一级查询得到的结果回填到本级缓存,最终返回结果给到调用方。
- set 操作是先设置L2级缓存,再设置L1级缓存,因为L2级缓存是共享的,设置完L2后,确保立刻对其他所有实例可见
- del 操作是先删除L2级缓存,再删除L2级缓存,也是因为L2级缓存是共享的,删除完L2后,确保立刻对其他所有实例可见
这里是否还需要考虑其他的点,欢迎各位在评论区留言。
不同微服务实例上,非共享缓存之间的一致性问题
这里也是参考的L2Cache的思路,当我们对某个实例的非共享缓存层级执行修改或者删除操作的时候,我们需要借助消息广播,告知其他所有实例删除自己本地对应的缓存,以此确保多个实例之间的非共享缓存的一致性。
假设此时我们的多级缓存层级为: Caffeine+Redis , 当我们对实例1的本地缓存进行修改或者删除操作时,我们需要将操作涉及到的keys广播给其他所有实例;对应的实例接收到广播消息后,需要删除本地缓存中对应的keys,确保一致性。
Redis是集中式缓存,所以无需担心一致性问题。
这里其实和CPU多级缓存的一致性问题解决思路类似,因为CPU多级缓存中通常L1和L2级缓存都是单个核私有的,L3是共享的,所以同样存在如何实现一致性的问题。
这里消息广播可以借助于消息队列,或者Redis的pub/sub,或者在SDK中引入netty进行通信。
小结
这里有一点没提到,就是关于京东的JdHotKey和有赞的TMC,他们的缓存SDK设计思路更多是为了解决热点key探测与即时缓存到LocalCache,因此他们整体的架构设计就和文本不太一样了,简单来说如下图所示:
他们只使用到了集中式缓存Redis,只使用本地缓存进行热点key的缓存,而非全量缓存;同时为了确保强一致性,会监听redis过期key事件,当发生key过期事件时,会广播给所有实例,删除所有实例热点缓存中对应的key,确保强一致性。
本文仅为笔者个人拙见,如有理解错误,欢迎各位大佬在评论区留言指出。
相关文章:
分布式多级缓存SDK设计的思考
分布式多级缓存SDK设计的思考 背景整体架构多层级组装回调埋点分区处理一致性问题缓存与数据库之间的一致性问题不同层级缓存之间的一致性问题不同微服务实例上,非共享缓存之间的一致性问题 小结 之前实习期间编写过一个简单的多级缓存SDK,后面了解到一些…...
设计模式:适配器模式(C++实现)
适配器模式(Adapter Pattern)是一种结构设计模式,它允许将一个类的接口转换成客户端所期望的另一个接口。适配器模式通常用于连接两个不兼容的接口或类,使它们能够一起工作。 以下是一个简单的C适配器模式的示例: #in…...
【深度学习实验】前馈神经网络(二):使用PyTorch实现不同激活函数(logistic、tanh、relu、leaky_relu)
目录 一、实验介绍 二、实验环境 1. 配置虚拟环境 2. 库版本介绍 三、实验内容 0. 导入必要的工具包 1. 定义激活函数 logistic(z) tanh(z) relu(z) leaky_relu(z, gamma0.1) 2. 定义输入、权重、偏置 3. 计算净活性值 4. 绘制激活函数的图像 5. 应用激活函数并…...
容器技术所涉及Linux内核关键技术
一、容器技术前世今生 1.1 1979年 — chroot 容器技术的概念可以追溯到1979年的UNIX chroot。 它是一套“UNIX操作系统”系统,旨在将其root目录及其它子目录变更至文件系统内的新位置,且只接受特定进程的访问。 这项功能的设计目的在于为每个进程提供…...
IPV4和IPV6,公网IP和私有IP有什么区别?
文章目录 1、什么是IP地址?1.1、背景1.2、交换机1.3、局域网1.4、广域网1.5、ISP 互联网服务提供商 2、IPV42.1、什么是IPV4?2.2、IPV4的组成2.3、NAT 网络地址转换2.4、端口映射 3、公网IP和私有IP4、IPV6 1、什么是IP地址? 1.1、背景 一台…...
高云FPGA系列教程(7):ARM GPIO外部中断
文章目录 [toc]GPIO中断简介FPGA配置常用函数MCU程序设计工程下载 本文是高云FPGA系列教程的第7篇文章。 本篇文章介绍片上ARM Cortex-M3硬核处理器GPIO外部的使用,演示按键中断方式来控制LED亮灭,基于TangNano 4K开发板。 参考文档:Gowin_E…...
Python爬虫:动态获取页面
动态网站根据用户的某些操作产生一些结果。例如,当网页仅在向下滚动或将鼠标移动到屏幕上时才完全加载时,这背后一定有一些动态编程。当您将鼠标指针悬停在某些文本上时,它会为您提供一些选项,它还包含一些动态.这是是一篇关于动态…...
大数据平台迁移后yarn连接zookeeper 异常分析
大数据平台迁移后yarn连接zookeeper 异常分析 XX保险HDP大数据平台机房迁移异常分析。 异常现象: 机房迁移后大部分组件都能正常启动Yarn 启动后8088 8042等端口无法访问Hive spark 作业提交到yarn会出现卡死。 【备注】虽然迁移,但IP不变。 1. Yarn连…...
Ubuntu Nginx 配置 SSL 证书
首先需要在 Ubuntu 中安装 Nginx 服务, 打开终端执行以下命令: $ sudo apt update $ sudo apt install nginx -y然后启动 Nginx 服务并设置为开机时自动启动, 执行以下命令: $ sudo systemctl start nginx $ sudo systemctl enable nginx最后再验证一下 Nginx 服务的当前状态…...
将本地前端工程中的npm依赖上传到Nexus
【问题背景】 用Nexus搭建了内网的依赖仓库,需要将前端工程中node_modules中的依赖上传到Nexus上,但是node_modules中的依赖已经是解压后的状态,如果直接机械地将其简单地打包上传到Nexus,那么无法通过npm install下载使用。故有…...
软考高级架构师下篇-16通信系统架构设计理论与实践
目录 1. 引言2. 通信系统网络架构3. 网络构建关键技术4. 网络构建5. 前文回顾1. 引言 此章节主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本节知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中…...
国庆中秋特辑(二)浪漫祝福方式 使用生成对抗网络(GAN)生成具有节日氛围的画作
要用人工智能技术来庆祝国庆中秋,我们可以使用生成对抗网络(GAN)生成具有节日氛围的画作。这里将使用深度学习框架 TensorFlow 和 Keras 来实现。 一、生成对抗网络(GAN) 生成对抗网络(GANs,…...
stm32 串口发送和接收
串口发送 #include "stm32f10x.h" // Device header #include <stdio.h> #include <stdarg.h>//初始化串口 void Serial_Init() {//开启时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Pe…...
Vite + Vue3 实现前端项目工程化
通过官方脚手架初始化项目 第一种方式,这是使用vite命令创建,这种方式除了可以创建vue项目,还可以创建其他类型的项目,比如react项目 npm init vitelatest 第二种方式,这种方式是vite专门为vue做的配置,…...
Java动态代理Aop的好处
1. 预备知识-动态代理 1.1 什么是动态代理 动态代理利用Java的反射技术(Java Reflection)生成字节码,在运行时创建一个实现某些给定接口的新类(也称"动态代理类")及其实例。 1.2 动态代理的优势 动态代理的优势是实现无侵入式的代…...
各种存储性能瓶颈如何分析与优化?
【摘要】本文结合实践剖析存储系统的架构及运行原理,深入分析各种存储性能瓶颈场景,并提出相应的性能优化手段,希望对同行有一定的借鉴和参考价值。 【作者】陈萍春,现就职于保险行业,拥有多年的系统、存储以及数据备…...
Android StateFlow初探
Android StateFlow初探 前言: 最近在学习StateFlow,感觉很好用,也很神奇,于是记录了一下. 1.简介: StateFlow 是一个状态容器式可观察数据流,可以向其收集器发出当前状态更新和新状态更新。还可通过其 …...
Docker Compose初使用
简介 Docker-Compose项目是Docker官方的开源项目,负责实现对Docker容器集群的快速编排。 Docker-Compose将所管理的容器分为三层,分别是 工程(project),服务(service)以及容器(cont…...
测试与FastAPI应用数据之间的差异
【squids.cn】 全网zui低价RDS,免费的迁移工具DBMotion、数据库备份工具DBTwin、SQL开发工具等 当使用两个不同的异步会话来测试FastAPI应用程序与数据库的连接时,可能会出现以下错误: 在测试中,在数据库中创建了一个对象&#x…...
WebStorm 2023年下载、安装教程、亲测有效
文章目录 简介安装步骤常用快捷键 简介 WebStorm 是JetBrains公司旗下一款JavaScript 开发工具。已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的…...
k8s储存卷
卷的类型 In-Tree存储卷插件 ◼ 临时存储卷 ◆emptyDir ◼ 节点本地存储卷 ◆hostPath, local ◼ 网络存储卷 ◆文件系统:NFS、GlusterFS、CephFS和Cinder ◆块设备:iSCSI、FC、RBD和vSphereVolume ◆存储平台:Quobyte、PortworxVolume、Sto…...
【解决Win】“ 无法打开某exe提示无法成功完成操作,因为文件包含病毒或潜在的垃圾软件“
在下载某个应用程序,打开时出现了“无法成功完成操作因为文件包含病毒或潜在垃圾”的提示,遇到这个情况怎么解决? 下面为大家分享故障原因及具体的处理方法。 故障原因 是由于杀毒 防护等原因引起的。 解决方案 打开Windows 安全中心 选择…...
SpringBoot调用ChatGPT-API实现智能对话
目录 一、说明 二、代码 2.1、对话测试 2.2、单次对话 2.3、连续对话 2.4、AI绘画 一、说明 我们在登录chatgpt官网进行对话是不收费的,但需要魔法。在调用官网的API时,在代码层面上使用,通过API KEY进行对话是收费的,不过刚…...
element-table出现错位解决方法
先看示例图,这个在开发中还是很常遇到的,在table切换不同数据时或者切换页面时,容易出现: 解决方法很简单,官方有提供方法: 我们可以在重新渲染数据后: this.$nextTick(() > {this.$refs.…...
DC电源模块具有不同的安装方式和安全规范
BOSHIDA DC电源模块具有不同的安装方式和安全规范 DC电源模块是将低压直流电转换为需要的输出电压的装置。它们广泛应用于各种领域和行业,如通信、医疗、工业、家用电器等。安装DC电源模块应严格按照相关的安全规范进行,以确保其正常运行和安全使用。 D…...
zabbix自定义监控、钉钉、邮箱报警
目录 一、实验准备 二、安装 三、添加监控对象 四、添加自定义监控项 五、监控mariadb 1、添加模版查看要求 2、安装mariadb、创建用户 3、创建用户文件 4、修改监控模版 5、在上述文件中配置路径 6、重启zabbix-agent验证 六、监控NGINX 1、安装NGINX,…...
短信、邮箱验证码本地可以,部署到服务器接口却不能使用
应对公司双验证要求,对本系统做邮箱、短信验证码登录,本地开发正常发送,到服务器上部署却使用失败,已全部解决,记录坑。 一、nginx拦截 先打开你的服务器 nginx.conf 看看有没有做接口拦截。(本地可能做Sp…...
Java web基础知识
Servlet Servlet是sun公司开发的动态web技术 sun在API中提供了一个接口叫做 Servlet ,一个简单的Servlet 程序只需要完成两个步骤 编写一个实现了Servlet接口的类 把这个Java部署到web服务器中 一般来说把实现了Servlet接口的java程序叫做,Servlet 初步…...
【Linux学习】01Linux初识与安装
Linux(B站黑马)学习笔记 01Linux初识与安装 文章目录 Linux(B站黑马)学习笔记前言01Linux初识与安装操作系统简述Linux初识虚拟机介绍安装VMware Workstation虚拟化软件VMware中安装CentOS7 Linux操作系统下载CentOS操作系统VMwa…...
android 将数据库中的 BLOB 对象动态加载为 XML,并设置到 Android Activity 的内容视图上
以下是一个示例代码,演示如何将数据库中的 BLOB 对象动态加载为 XML,并设置到 Android Activity 的内容视图上: ```java import android.app.Activity; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import and…...
网站免费正能量软件直播/随州今日头条新闻
把手机中的号码复制到SIM卡的操作方法如下:1,首先把手机SIM卡插好,打开手机主界面,在主界面中找了“联系人”或是“通讯录”这个图标,点击进入。2,进入“联系人”或“通讯录”,会发现你的号码都…...
百姓网站制作/百度推广价格表
6安装可选部件介绍R390X G2有哪些可选部件,以及如何进行部件安装的详细操作步骤。R390X G2可选部件如下:介绍如何安装硬盘。 请确保同一RAID的所有硬盘类型相同,否则会因硬盘性能不同而造成RAID性能下降或者无法创建RAID。即满足如下两点…...
网站设计公司网站设计/建站公司网站源码
https://jingyan.baidu.com/article/0aa22375e2089588cd0d6410.html 如果界面里没有图标,要在对话框属性里将Minimize Box改为true即可...
深圳网站建设定制开发 .超凡科技/深圳知名网络优化公司
两只青蛙相爱了,结婚后生了一个癞蛤嫫, 公青蛙见状大怒说:贱人,怎么回事? 母青蛙哭着说:他爹,认识你之前我整过容。 (爱情需要信任) 小驴问老驴:为啥咱们天天吃干…...
b2c网上商城/关键词排名优化易下拉排名
来源:华为云原文地址:https://www.toutiao.com/a6793181470287462916/2020年2月13日,华为云安全团队监测到应用广泛的Apache Dubbo出现一个较为严重的漏洞:反序列化漏洞(漏洞编号:CVE-2019-17564࿰…...
天津网站建设设计/seo线下培训课程
对于每一个刚接触编程语言的人来说安装环境是一件很让人难受的事。需要找许多网站或许才能找到适合自己的问题。那么这里就提供了极其简单的安装python库和MinGW的解决方案。(据说我还没有找到和我的解决方案一样的)——————废话还是要有一点的———…...