当前位置: 首页 > news >正文

AOP+Redisson 延时队列,实现缓存延时双删策略

一、缓存延时双删

关于缓存和数据库中的数据保持一致有很多种方案,但不管是单独在修改数据库之前,还是之后去删除缓存都会有一定的风险导致数据不一致。而延迟双删是一种相对简单并且收益比较高的实现最终一致性的方式,即在删除缓存之后,间隔一个短暂的时间后再删除缓存一次。这样可以避免并发更新时,假如缓存在第一次被删除后,被其他线程读到旧的数据更新到了缓存,第二次删除还可以补救,从而时间最终一致性。

实现延时双删的方案也有很多,有本地用 Thread.sleep(); 睡眠的方式做延时,也有借助第三方消息中间件做延时消息等等,本文基于 Redisson 中的延时队列进行实验。

Redisson 中提供了 RDelayedQueue 可以迅速实现延时消息,本文所使用的 Redisson 版本为 3.19.0

二、Redisson 实现延时消息

新建 SpringBoot 项目,在 pom 中加入下面依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency><dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.19.0</version>
</dependency>

yum 配置中,增加 redis 的信息:

spring:redis:timeout: 6000password:cluster:max-redirects:nodes:- 192.168.72.120:7001- 192.168.72.121:7001- 192.168.72.122:7001

声明 RedissonClient

@Configuration
public class RedissonConfig {@Beanpublic RedissonClient getRedisson(RedisProperties redisProperties) {Config config = new Config();String[] nodes = redisProperties.getCluster().getNodes().stream().filter(StringUtils::isNotBlank).map(node -> "redis://" + node).collect(Collectors.toList()).toArray(new String[]{});ClusterServersConfig clusterServersConfig = config.useClusterServers().addNodeAddress(nodes);if (StringUtils.isNotBlank(redisProperties.getPassword())) {clusterServersConfig.setPassword(redisProperties.getPassword());}clusterServersConfig.setConnectTimeout((int) (redisProperties.getTimeout().getSeconds() * 1000));clusterServersConfig.setScanInterval(2000);return Redisson.create(config);}
}

延时队列实现延时消息:

@Slf4j
@Component
public class MsgQueue {@ResourceRedissonClient redissonClient;public static final String QUEUE_KEY = "DELAY-QUEUE";// 发送消息public void send(String msg, Long time, TimeUnit unit) {// 获取队列RBlockingQueue<String> blockingQueue = redissonClient.getBlockingQueue(QUEUE_KEY);// 延时队列RDelayedQueue<String> delayedQueue = redissonClient.getDelayedQueue(blockingQueue);// 添加数据delayedQueue.offer(msg, time, unit);}// 消息监听@PostConstructpublic void listen() {CompletableFuture.runAsync(() -> {RBlockingQueue<String> blockingQueue = redissonClient.getBlockingQueue(MsgQueue.QUEUE_KEY);log.info("延时消息监听!");while (true) {try {consumer(blockingQueue.take());} catch (InterruptedException e) {throw new RuntimeException(e);}}});}// 消费消息public void consumer(String msg) {log.info("收到延时消息: {} , 当前时间: {} ", msg, LocalDateTime.now().toString());}}

测试延时消息:

@Slf4j
@RestController
@RequestMapping("/msg")
public class MsgController {@ResourceMsgQueue queue;@GetMapping("/test")public void test() {String msg = "你好";queue.send(msg, 5L, TimeUnit.SECONDS);}}

上面发送了延时5秒的消息,运行后可以看到日志:

在这里插入图片描述

三、AOP+延时队列,实现延时双删策略

缓存延时删除队列:

@Slf4j
@Component
public class CacheQueue {@ResourceRedissonClient redissonClient;public static final String QUEUE_KEY = "CACHE-DELAY-QUEUE";// 延时删除public void delayedDeletion(String key, Long time, TimeUnit unit) {RBlockingQueue<String> blockingQueue = redissonClient.getBlockingQueue(QUEUE_KEY);RDelayedQueue<String> delayedQueue = redissonClient.getDelayedQueue(blockingQueue);log.info("延时删除key: {} , 当前时间: {} ", key, LocalDateTime.now().toString());delayedQueue.offer(key, time, unit);}// 消息监听@PostConstructpublic void listen() {CompletableFuture.runAsync(() -> {RBlockingQueue<String> blockingQueue = redissonClient.getBlockingQueue(CacheQueue.QUEUE_KEY);while (true) {try {consumer(blockingQueue.take());} catch (InterruptedException e) {throw new RuntimeException(e);}}});}// 消费消息public void consumer(String key) {log.info("删除key: {} , 当前时间: {} ", key, LocalDateTime.now().toString());redissonClient.getBucket("key").delete();}
}

定义缓存和删除缓存注解:

@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.METHOD)
public @interface Cache {String name() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.METHOD)
public @interface DeleteCache {String name() default "";
}

缓存AOP逻辑:

@Aspect
@Component
public class CacheAspect {@ResourceRedissonClient redissonClient;private final Long validityTime = 2L;@Pointcut("@annotation(com.bxc.retrydemo.anno.Cache)")public void pointCut() {}@Around("pointCut()")public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {Cache ann = ((MethodSignature) pjp.getSignature()).getMethod().getDeclaredAnnotation(Cache.class);if (Objects.nonNull(ann) && StringUtils.isNotBlank(ann.name())) {Object proceed = redissonClient.getBucket(ann.name()).get();if (Objects.nonNull(proceed)){return proceed;}}Object proceed = pjp.proceed();if (Objects.nonNull(ann) && StringUtils.isNotBlank(ann.name())) {redissonClient.getBucket(ann.name()).set(proceed, validityTime, TimeUnit.HOURS);}return proceed;}
}

延时双删 AOP 逻辑:

@Aspect
@Component
public class DeleteCacheAspect {@ResourceRedissonClient redissonClient;@ResourceCacheQueue cacheQueue;private final Long delayedTime = 3L;@Pointcut("@annotation(com.bxc.retrydemo.anno.DeleteCache)")public void pointCut() {}@Around("pointCut()")public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {// 第一次删除缓存DeleteCache ann = ((MethodSignature) pjp.getSignature()).getMethod().getDeclaredAnnotation(DeleteCache.class);if (Objects.nonNull(ann) && StringUtils.isNotBlank(ann.name())) {redissonClient.getBucket(ann.name()).delete();}// 执行业务逻辑Object proceed = pjp.proceed();//延时删除if (Objects.nonNull(ann) && StringUtils.isNotBlank(ann.name())) {cacheQueue.delayedDeletion(ann.name(), delayedTime, TimeUnit.SECONDS);}return proceed;}
}

伪业务逻辑使用:

@Slf4j
@Service
public class MsgServiceImpl implements MsgService {@Cache(name = "you key")@Overridepublic String find() {// 数据库操作// ....// 数据库操作结束return "数据结果";}@DeleteCache(name = "you key")@Overridepublic void update() {// 数据库操作// ....// 数据库操作结束}
}

相关文章:

AOP+Redisson 延时队列,实现缓存延时双删策略

一、缓存延时双删 关于缓存和数据库中的数据保持一致有很多种方案&#xff0c;但不管是单独在修改数据库之前&#xff0c;还是之后去删除缓存都会有一定的风险导致数据不一致。而延迟双删是一种相对简单并且收益比较高的实现最终一致性的方式&#xff0c;即在删除缓存之后&…...

Hive中left join 中的where 和 on的区别

目录 一、知识点 二、测试验证 三、引申 一、知识点 left join中关于where和on条件的知识点&#xff1a; 多表left join 是会生成一张临时表。on后面&#xff1a; 一般是对left join 的右表进行条件过滤&#xff0c;会返回左表中的所有行&#xff0c;而右表中没有匹配上的数…...

LaTeX教程(001)-LaTeX文档结构(01)

LaTeX教程(001)- LaTeX \LaTeX LATE​X文档结构(01) 说在前面 这是我本人学习《The LaTeX Companion》第三版的笔记&#xff0c;但并不是翻译。 书籍的第一章对 LaTeX \LaTeX LATE​X及其历史进行了相当长的介绍&#xff0c;这是几乎每一本关于 LaTeX \LaTeX LATE​X的书都会…...

SV-7041T 多媒体教学广播IP网络有源音箱

SV-7041T是深圳锐科达电子有限公司的一款2.0声道壁挂式网络有源音箱&#xff0c;具有10/100M以太网接口&#xff0c;可将网络音源通过自带的功放和喇叭输出播放&#xff0c;可达到功率30W。同时它可以外接一个30W的无源副音箱&#xff0c;用在面积较大的场所。5寸进口全频低音喇…...

Linux文本三剑客awk经典案例

前言&#xff1a; AWK是一种专门用于文本处理的编程语言&#xff0c;它被广泛用于数据提取和报告生成&#xff0c;也是企业笔试面试常考的内容&#xff0c;以下34题是awk的用法案例&#xff0c;希望可以帮到你&#xff01; 1.查看TCP连接状态 [rootnode1 ~]# netstat -nat | a…...

如何使用 Mermaid、GitHub 和 VSCode 用代码创建关系图三

Mermaid 系列 如何使用 Mermaid、GitHub 和 VSCode 用代码创建关系图一如何使用 Mermaid、GitHub 和 VSCode 用代码创建关系图二 1.如何创建甘特图 Gantt 甘特图以条形图的形式用作可视化表示。它有效地展示了项目的时间表&#xff0c;揭示了各个项目组件完成所需的持续时间…...

考研经验总结——政治篇

文章目录 一、前言二、学习情况三、最后 一、前言 不要提前&#xff0c;不要提前&#xff0c;不要提前&#xff0c; 我曾在暑假的时候上了7天左右的政治课&#xff0c;讲真话是很有趣的&#xff0c;并且对于自身的世界观、人生观和价值观的改善也是相当不错的&#xff0c;把我…...

春招秋招,在线测评到底难不难?

现在很多企业在春招的时候&#xff0c;都会有一个在线测评的环节&#xff0c;目的当然就是希望更加了解清楚毕业生的综合能力以及其他方面的素质&#xff0c;好让HR可以根据岗位筛选出能力达标的人才。所以&#xff0c;现在不少即将面对春招的大学毕业生&#xff0c;比较关心的…...

数学建模比赛中,使用大语言模型如chatgpt、文心一言该如何写Prompt(提示)?

在大型语言模型中&#xff0c;"prompt"&#xff08;中文常译为“提示”或“引导”&#xff09;是指提供给模型的输入文本&#xff0c;用于指示或引导模型产生特定的输出。它的作用主要是告诉模型用户想要得到什么样的信息或完成什么样的任务。 例如&#xff0c;在使…...

tcpdump 抓包无法落盘

文章目录 问题背景解决办法 问题背景 在嵌入式设备中(Linux系统)&#xff0c;为了分析两个网络节点的通讯问题&#xff0c;往往需要用到tcpdump&#xff0c;抓一个.pcap的包在PC端进行分析。博主在实际操作中发现&#xff0c;抓包无法实时落盘。 解决办法 # 下面的命令是写在…...

【网站项目】066农家乐信息平台

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…...

idea/webstorm 创建Vue实例 Unresolved type Vue 处理方法

1.电脑本地安装node.js 官网下载 2. 其他: 未排除变量,前期试错(以下步骤配置了,但不确定对解决问题是否有帮助)...

C++ 11新特性之语法甜点2

概述 C 11中引入了许多简化编程工作的语法上的新特性&#xff0c;我们暂且美其名曰&#xff1a;“语法甜点”。书接上篇&#xff0c;我们继续介绍C 11中的这些“语法甜点”&#xff0c;也是第二篇关于“语法甜点”的文章。 语法甜点6&#xff1a;模板右边双括号 在C 03中&#…...

【芯片设计- RTL 数字逻辑设计入门 番外篇 8.1 -- memory repair 详细介绍】

文章目录 memory repair 详细介绍Memory Repair 方法Memory Repair 过程举例memory repair 详细介绍 SoC (System on Chip) 的 Memory Repair 是一种技术,用于检测和修复内存中的损坏单元。由于SoC内部集成了大量的逻辑和存储单元,包括RAM(随机访问存储器)、ROM(只读存储…...

2023强网杯复现

强网先锋 SpeedUp 要求2的27次方的阶乘的逐位之和 在A244060 - OEIS 然后我们将4495662081进行sha256加密 就得到了flag flag{bbdee5c548fddfc76617c562952a3a3b03d423985c095521a8661d248fad3797} MISC easyfuzz 通过尝试输入字符串判断该程序对输入字符的验证规则为9…...

IP代理协议有哪些?爬虫代理如何被合理使用?

随着互联网的普及和发展&#xff0c;IP代理作为一种网络代理方式&#xff0c;越来越受到人们的关注。IP代理协议是网络代理的一种规范&#xff0c;它规定了代理服务器与客户端之间进行通信的规则。了解IP代理协议对于使用代理的人来说非常重要&#xff0c;因为它可以帮助我们更…...

Vue学习笔记(二)快速入门

Vue学习笔记&#xff08;二&#xff09;快速入门 vue小试牛刀 hello-vue3.html <body><div id"app"><h1>{{msg}}</h1></div><script type"module">import {createApp} from https://unpkg.com/vue3/dist/vue.esm-b…...

在Vue中@click方法不起效

问题描述&#xff1a; 在跟项目的时候&#xff0c;我们可能会遇到我们click点击时&#xff0c;需要执行多个操作&#xff0c;如&#xff1a;调用方法&#xff0c;修改变量等。举个例子&#xff0c;像这样&#xff0c;我们在管理项目中想要编辑某一值&#xff0c;编辑好后&…...

服装行业ERP系统解决方案

我国的服装企业大多属于劳动密集型&#xff0c;主要有三种类型&#xff1a;自有品牌服装生产销售企业、接订单生产型企业及处于产业链下游的零售分销企业。在经营过程中&#xff0c;服装行业面临诸多挑战&#xff0c;如流行周期短、季节性强&#xff0c;市场变化快&#xff1b;…...

AI绘画探索人工智能的未来

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-8fL64RHWVzwpzR6m {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…...

模型参数、模型存储精度、参数与显存

模型参数量衡量单位 M&#xff1a;百万&#xff08;Million&#xff09; B&#xff1a;十亿&#xff08;Billion&#xff09; 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的&#xff0c;但是一个参数所表示多少字节不一定&#xff0c;需要看这个参数以什么…...

大数据零基础学习day1之环境准备和大数据初步理解

学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 &#xff08;1&#xff09;设置网关 打开VMware虚拟机&#xff0c;点击编辑…...

工程地质软件市场:发展现状、趋势与策略建议

一、引言 在工程建设领域&#xff0c;准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具&#xff0c;正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...

Python实现prophet 理论及参数优化

文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候&#xff0c;写过一篇简单实现&#xff0c;后期随着对该模型的深入研究&#xff0c;本次记录涉及到prophet 的公式以及参数调优&#xff0c;从公式可以更直观…...

[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制

在数字化浪潮席卷全球的今天&#xff0c;数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具&#xff0c;在大规模数据获取中发挥着关键作用。然而&#xff0c;传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时&#xff0c;常出现数据质…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题

分区配置 (ptab.json) img 属性介绍&#xff1a; img 属性指定分区存放的 image 名称&#xff0c;指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件&#xff0c;则以 proj_name:binary_name 格式指定文件名&#xff0c; proj_name 为工程 名&…...

mac 安装homebrew (nvm 及git)

mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用&#xff1a; 方法一&#xff1a;使用 Homebrew 安装 Git&#xff08;推荐&#xff09; 步骤如下&#xff1a;打开终端&#xff08;Terminal.app&#xff09; 1.安装 Homebrew…...

[ACTF2020 新生赛]Include 1(php://filter伪协议)

题目 做法 启动靶机&#xff0c;点进去 点进去 查看URL&#xff0c;有 ?fileflag.php说明存在文件包含&#xff0c;原理是php://filter 协议 当它与包含函数结合时&#xff0c;php://filter流会被当作php文件执行。 用php://filter加编码&#xff0c;能让PHP把文件内容…...

在 Spring Boot 项目里,MYSQL中json类型字段使用

前言&#xff1a; 因为程序特殊需求导致&#xff0c;需要mysql数据库存储json类型数据&#xff0c;因此记录一下使用流程 1.java实体中新增字段 private List<User> users 2.增加mybatis-plus注解 TableField(typeHandler FastjsonTypeHandler.class) private Lis…...