Spring Data Redis + RabbitMQ - 基于 string 实现缓存、计数功能(同步数据)
目录
一、Spring Data Redis
1.1、缓存功能
1.1.1、分析
1.1.2、案例实现
1.1.3、效果演示
1.2、计数功能(Redis + RabbitMQ)
1.2.1、分析
1.2.2、案例实现
一、Spring Data Redis
1.1、缓存功能
1.1.1、分析
使用 redis 作为缓存, MySQL 作为数据库组成的架构

整体思路:
应用服务器访问数据的时候,先查询 Redis,如果 Redis 上存在该数据,就从 Redis 中取数据直接交给应用服务器,不用继续访问数据库了;如果 Redis 上不存在该数据,就会去 MySQL 中把读到的结构返回给应用服务器,同时,把这个数据也写入到 Redis 中.
由于 Redis 这样的缓存经常用来存储 “热点数据”,也就是高频使用的数据,那什么样的数据算高频呢?这里暗含了一层假设,某个数据一旦被用到了,那么可能在最近这段时间就可能被反复用到.
随着时间推移,越来越多的 key 在 redis 上访问不到,那 redis 的数据不是越来越多么?
- 把数据写给 redis 的同时,会给这个 key 设置一个过期时间.
- Redis 也有内存不足的时候,因此提供了 淘汰策略(之前的文章展开讲过).
1.1.2、案例实现
例如论坛网站,有些帖子的访问评论很高,就需要设置成热点文章,缓存起来(比起去 MySQL 数据库中查询文章要快的多).
实现思路:
根据上面理论,暗含假设当前使用的文章就是热点文章,也就是说,如果在缓存中有该文章,就直接返回,如果没有,就去数据库中查,然后再缓存起来,同时设置 30min(不同场景合理分配) 的过期时间.
帖子实体类.
@Data
public class Article {private String title;private String content;}
文章 mapper.
@Mapper
public interface ArticleMapper {/*** 根据 id 查询文章* @param id* @return*/Article selectArticleById(@Param("id") Integer id);}
<select id="selectArticleById" resultType="com.example.cyk.cache.Article">select * from article where id = #{id};</select>
帖子 controller
@RestController
@RequestMapping("/article")
public class ArticleController {@Autowiredprivate IArticleService articleService;@GetMapping("/get")public HashMap<String, Object> get(@NonNull Integer id) {//1.获取文章服务Article article = articleService.getArticleInfo(id);//2.返回响应return HandlerResponse(1000, "操作成功", article);}/*** 处理返回格式* @param code* @param msg* @param data* @return*/private HashMap<String, Object> HandlerResponse(Integer code, String msg, Object data) {HashMap<String, Object> result = new HashMap<>();result.put("code", code);result.put("msg", msg);result.put("data", data);return result;}}
帖子 service .
@Slf4j
@Service
public class ArticleService implements IArticleService {@Autowiredprivate ArticleMapper articleMapper;@Autowiredprivate StringRedisTemplate redisTemplate;@Overridepublic Article getArticleInfo(Integer id) {//1.非空校验if(id == null) {log.warn("文章 id 为空");throw new RuntimeException("文章 id 为空");}//2.先去 redis 上看有没有文章对应的这个id//我这里约定 redis 上存储格式://key: art:id//value: $title$content ($ 是分隔符)//例如 key: art:1 value: $决定$今天要好好学习String articleInfo = redisTemplate.opsForValue().get("art:" + id);if(articleInfo != null) {//存在直接返回log.info("从 redis 中获取到文章数据");//1) 解析格式Article article = analysisArticle(articleInfo);//2) 返回数据return article;}//3.redis 上没有数据,因此需要从 mysql 中取Article article = articleMapper.selectArticleById(id);if(article == null) {log.warn("文章不存在");throw new RuntimeException("文章不存在!");}//4.将文章存到 redis 中//1) 合成 redis 所需格式的文章articleInfo = synthesisArticle(article);//2) 设置 5 分钟过期时间(为了演示效果)redisTemplate.opsForValue().set("art:" + id, articleInfo, 5, TimeUnit.SECONDS);log.info("从 mysql 中获取到文章数据");return article;}/*** 合成 redis 需要的格式(提前约定好的)* @param article* @return*/private String synthesisArticle(Article article) {StringBuilder stringBuilder = new StringBuilder();stringBuilder.append("$");stringBuilder.append(article.getTitle());stringBuilder.append("$");stringBuilder.append(article.getContent());return stringBuilder.toString();}/*** 解析文章格式* @param articleInfo* @return*/private Article analysisArticle(String articleInfo) {Article article = new Article();String title = articleInfo.split("\\$")[1];String content = articleInfo.split("\\$")[2];article.setTitle(title);article.setContent(content);return article;}}
1.1.3、效果演示

1.2、计数功能(Redis + RabbitMQ)
1.2.1、分析
许多都会使应用用 Redis 作为计数的基础⼯具,它可以实现快速计数、查询缓存的功能,例如网站视频的播放量,点赞数量......
Ps:这些都是相比较 MySQL 数据库而言的,Redis 可以通过简单的键值对操作完成计数任务并且实在内存中完成的,而 MySQL 就需要先查询数据库,然后 +1,然后再存入数据库,是在需要进行硬盘存储的

1.2.2、案例实现
实现思路:
假设,用户点击某个帖子,此时需要进行访问量 + 1 的操作,这时候应用服务器就会直接去操作 Redis ,执行 incr 命令,然后将返回的数据反馈给用户,最后 Redis 会以异步的方式(RabbitMQ 实现异步)将播放量同步到 MySQL 数据库中(异步就表示:这里并不是每一个播放请求,都需要立即写入数据~ 至于什么时候写入,需要根据实际的业务需求场景而定),将数据持久化.
Ps:实际中要开发⼀个成熟、稳定的真实计数系统,要⾯临的挑战远不⽌如此简单:防作弊、按 照不同维度计数、避免单点问题、数据持久化到底层数据源等。
文章实体类
@Data
public class Article implements Serializable {private Integer id;private String title;private String content;private Long visits; //访问量}
rabbit 交换机、队列、绑定配置.
public class MqFinal {//处理文章的直接交换机public static final String UPDATE_DIRECT = "article.update.direct";//用于修改文章数据的队列public static final String UPDATE_QUEUE = "article.update.queue";//bindingKeypublic static final String UPDATE_KEY = "article.update.key";}
@Configuration
public class MqConfig {/*** 消息转化器* @return*/@Beanpublic MessageConverter jsonMessageConverter() {return new Jackson2JsonMessageConverter();}@Beanpublic DirectExchange ArticleDirectExchange() {return new DirectExchange(MqFinal.UPDATE_DIRECT, true, false);}@Beanpublic Queue ArticleUpdateQueue() {return new Queue(MqFinal.UPDATE_QUEUE, true);}@Beanpublic Binding ArticleUpdateBinding() {return BindingBuilder.bind(ArticleUpdateQueue()).to(ArticleDirectExchange()).with(MqFinal.UPDATE_KEY);}}
mq 监听配置
@Slf4j
@Component
public class MqListenerArticle {@Autowiredprivate ArticleMapper articleMapper;/*** 同步数据库*/@RabbitListener(queues = MqFinal.UPDATE_QUEUE)public void syncVisits(HashMap<String, Object> data) {Integer id = (Integer) data.get("id");// Rabbitmq 这里有一个问题,Map<String, Object> 中 Object 传入为 Long 类型,需要用 Integer 来接受,否则报错// 因此发送消息之前,体现将 Long 类型转化为 String,接收到消息之后只需要将 String 转化为 Long 即可String visits = (String) data.get("visits");articleMapper.updateArticleVisits(id, Long.valueOf(visits));log.info("访问量数据同步完成!");}}
访问量增加服务(这里为了可读性,只展示了本业务的核心逻辑)
@Overridepublic Article getArticleInfo(Integer id) {//1.非空校验if(id == null) {log.warn("文章 id 为空");throw new RuntimeException("文章 id 为空");}//2.访问量 +1//注意:incr 这个命令执行时,即使 key 不存在,也会自动生成 key,然后自增Long visits = redisTemplate.opsForValue().increment("v_art:" + id);//3.rabbitmq 实现异步数据同步(发送一个消息即可)HashMap<String, Object> visitsInfo = new HashMap<>();visitsInfo.put("id", id);visitsInfo.put("visits", visits.toString()); //转化原因前面解释过了rabbitTemplate.convertAndSend(MqFinal.UPDATE_DIRECT, MqFinal.UPDATE_KEY, visitsInfo);//4.获取文章数据//业务逻辑(这里为了可读性,就先不展示这里了)......//5.放入文章Article article = new Article();article.setVisits(visits);article.setId(id);return article;}

相关文章:
Spring Data Redis + RabbitMQ - 基于 string 实现缓存、计数功能(同步数据)
目录 一、Spring Data Redis 1.1、缓存功能 1.1.1、分析 1.1.2、案例实现 1.1.3、效果演示 1.2、计数功能(Redis RabbitMQ) 1.2.1、分析 1.2.2、案例实现 一、Spring Data Redis 1.1、缓存功能 1.1.1、分析 使用 redis 作为缓存, M…...
Facebook Developer 的 HashCode
在 Android 中,您可以使用 Facebook SDK 提供的工具来生成您的应用程序的哈希码(hash code),以便在 Facebook 开发者帐户中配置您的应用程序。 要生成哈希码,您可以使用以下步骤: 打开终端或命令提示符&am…...
下载使用 ant design Pro 中遇到的一些问题
文章目录 npm 版本问题在idea终端输入命令报错:error:0308010C:digital envelope routines::unsupported npm 版本问题 npm v9.6.3 is known not to run on Node.js v19.9.0. This version of npm supports the following node versions: ^14.17.0 || ^16.13.0 || …...
「Java开发指南」如何用MyEclipse搭建Spring MVC应用程序?(一)
本教程将指导开发者如何生成一个可运行的Spring MVC客户应用程序,该应用程序实现域模型的CRUD应用程序模式。在本教程中,您将学习如何: 从数据库表的Scaffold到现有项目部署搭建的应用程序 使用Spring MVC搭建需要MyEclipse Spring或Bling授…...
[动态规划] (七) 路径问题:LCR 166.剑指offer 47. 珠宝的最高价值
[动态规划] (七) 路径问题:LCR 166./剑指offer 47. 珠宝的最高价值 文章目录 [动态规划] (七) 路径问题:LCR 166./剑指offer 47. 珠宝的最高价值题目解析解题思路状态表示状态转移方程初始化和填表顺序 返回值代码实现总结 LCR 166. 珠宝的最高价值 题目…...
Mysql进阶-SQL优化篇
插入数据 insert 我们需要一次性往数据库表中插入多条记录,可以从以下三个方面进行优化。 批量插入数据 一条insert语句插入多个数据,但要注意,每个insert语句最好插入500-1000行数据,就得重新写另一条insert语句 Insert into…...
VueI18n中英文切换 vue2.0
1: npm install --save vue-i18n8.0.0 (版本不要高了,不然报错) 2:创建相关文件 3:main.js文件配置 //i18n插件 import VueI18n from vue-i18n // element-ui多语言文件 import locale from element-ui/lib/locale;…...
VUE组件间通信的七种方式
目录 1、 props / $emit (1)父组件向子组件传值(props的用法) (2)子组件向父组件传递数据($emit的用法) 2、ref / $refs 用法: 3、eventBus事件总线($e…...
问chatgpt最近生活的困难
你知道吗,因为我做的所有的事情没有任何目的性,所以曾经过的很好,这种很好是一种逃避式的好,怎么说呢?遇到困难了,那就不做了,换下一个项目。比如打游戏,如果我这局玩王者荣耀&#…...
Flink源码解析八之任务调度和负载均衡
源码概览 jobmanager scheduler:这部分与 Flink 的任务调度有关。 CoLocationConstraint:这是一个约束类,用于确保某些算子的不同子任务在同一个 TaskManager 上运行。这通常用于状态共享或算子链的情况。CoLocationGroup & CoLocationGroupImpl:这些与 CoLocationCon…...
4.3 传送门
算法设计与分析 4.3 传送门 题目描述 现在有 n 个传送门,你处在第一个传送门的位置,第 i 个传送门可以将你传送到第 i-a[i] 到第 ia[i] 范围内的任意一个传送门,请问你最少需要几次操作,使得你可以传送到最后一个传送门的位置。 …...
NLP之Bert介绍和简单示例
文章目录 1. Bert 介绍2. 代码示例2.1 代码流程 1. Bert 介绍 2. 代码示例 from transformers import AutoTokenizertokenizer AutoTokenizer.from_pretrained("bert-base-chinese") input_ids tokenizer.encode(欢迎来到Bert世界, return_tensorstf) print(input…...
【Windows】Google和火狐浏览器禁用更新的操作方式
想必很多网民常用的浏览器是Edge,Google,火狐这三种,但是浏览器都有后台自动更新,更新提示会一直显示,要用户去点击才关掉,有点强迫症的用户就会想要把它一直关掉,可每次打开都关不掉࿰…...
关于编程不得不说的事
这些年,互联网爆炸式的发展,促生了无数程序员,也促生了大量 IT培训机构。短短数年间,科班出生的程序员和培训机构出生的程序员呈指数增长。程序员的职业也不再是金饭碗。写了这么多代码,有些感触,所以写下来…...
2.4G合封芯片 XL2422,集成M0核MCU,高性能 低功耗
XL2422芯片是一款高性能低功耗的SOC集成无线收发芯片,集成M0核MCU,工作在2.400~2.483GHz世界通用ISM频段。该芯片集成了射频接收器、射频发射器、频率综合器、GFSK调制器、GFSK解调器等功能模块,并且支持一对多线网和带ACK的通信模式。发射输…...
【QT基础入门 控件篇】QLineEdit 基础、高级和样式表使用详解
一、QLineEdit简介 QLineEdit是一个单行文本编辑器,它可以让用户输入和编辑纯文本,也可以设置一些有用的编辑功能,如撤销和重做、剪切和粘贴、拖放等。QLineEdit: 可以根据不同的回显模式(echoMode)来显示不同的输入内…...
网络安全(网络安全)小白自学
想自学网络安全(黑客技术)首先你得了解什么是网络安全!什么是黑客! 网络安全可以基于攻击和防御视角来分类,我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术,而“蓝队”、“安全运营”、“安全…...
dupeGuru 清理微信重复文件
本文摘录于:https://www.bilibili.com/video/BV13p4y1G75Y/?spm_id_from333.337.search-card.all.click&vd_source483e5c52353ea59d1a5eadac7737591a只是做学习备份之用,绝无抄袭之意,有疑惑请联系本人! 微信用了七八年,文件…...
华为RS设备状态及接口配置命令
1、查看硬件信息 ①查看序列号 查看整机序列号 display esn display sn ②、查看功率 电源功率 display power 查看光模块功率 display transceiver interface gigabitethernet 1/0/0 verbose ③、查看风扇 display fan ④、查看温度 display temperature all ⑤、查看硬…...
单链表的应用(2)
环形链表的约瑟夫问题 编号为 1 到 n 的 n 个人围成一圈。从编号为 1 的人开始报数,报到 m 的人离开。 下一个人继续从 1 开始报数。 n-1 轮结束以后,只剩下一个人,问最后留下的这个人编号是多少? 利用链表实现 思路࿱…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...
深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...
3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...
ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...
【HTTP三个基础问题】
面试官您好!HTTP是超文本传输协议,是互联网上客户端和服务器之间传输超文本数据(比如文字、图片、音频、视频等)的核心协议,当前互联网应用最广泛的版本是HTTP1.1,它基于经典的C/S模型,也就是客…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...
AGain DB和倍数增益的关系
我在设置一款索尼CMOS芯片时,Again增益0db变化为6DB,画面的变化只有2倍DN的增益,比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析: 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...
