5_2-点赞功能-结合(多线程)or(多线程兼异步)定时持久化到数据库-应用redis的scan方法

0、前提引入:

视频讲解:

5.2-点赞功能-结合多线程定时持久化到数据库-应用redis的scan方法_哔哩哔哩_bilibili

项目前身:

5-点赞功能-定时持久化到数据库-pipeline+lua-优化ByScan_哔哩哔哩_bilibili

https://www.bilibili.com/video/BV1Gs4y1676q

blogLike_schedule/like05 · xin麒/XinQiUtilsOrDemo - 码云 - 开源中国 (gitee.com)

本项目地址:

本项目like05_02是like05的优化:

blogLike_schedule/like05_02 · xin麒/XinQiUtilsOrDemo - 码云 - 开源中国 (gitee.com)

https://gitee.com/flowers-bloom-is-the-sea/XinQiUtilsOrDemo/tree/master/blogLike_schedule/like05_02

数据表库等注意的点:

https://gitee.com/flowers-bloom-is-the-sea/XinQiUtilsOrDemo/tree/master/blogLike_schedule

其他需要注意的点:

测试时一定要将executeInternal里面的内容注释掉,主要是防止定时任务的干扰:

    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {// 要执行的内容就写在这个函数中
//        blogService.updateAllLikeListToDatabase();
//        blogService.updateAllLikeListToDatabaseByThreads();}

1、先造一些假user数据吧:

现在是20230715-9:50

@Slf4j
@SpringBootTest
public class DataConstructTest {@Resourceprivate BlogServiceImpl blogService;@Autowiredprivate UserService userService;@Test//获取Redis里的数据,得到的是Map<String, String>public void addSomeUser(){for (int i = 0; i < 10000; i++) {User user = new User();user.setName("xinqi" + i);Long phone = 10000000000L + i;user.setPhone(phone.toString());userService.save(user);}}
}

1.2先造假的博客数据:

@Test//这里不知道为什么不行
public void addSomeBlogByBatch() {long userId = 0;int addCount = 10000;int preCount = 100;int connection = addCount / preCount;for (int i = 0; i < connection; i++) {ArrayList<Blog> blogs = new ArrayList<>(preCount);for (int j = 0; j < preCount; j++) {Blog blog = new Blog();blog.setTitle("title" + userId);blog.setUserId(userId++);}System.out.println("now userId is" + userId);blogService.saveBatch(blogs);}
}

不知道为什么不行博客假数据的添加01.

思考:应该是mybatisPlus没有配置saveBatch相关的。

将就用:

@Test
public void addSomeBlog() {long userId = 0;for (int i = 0; i < 10000; i++) {Blog blog = new Blog();blog.setTitle("title" + userId);blog.setUserId(userId++);System.out.println("now userId is" + userId);blogService.save(blog);}
}

1.3win10查看、关闭和开启mysql服务:

看这里吧:https://blog.csdn.net/ws_please/article/details/131736814

2、先查看下redis和java服务器最大承载的内存数据量大概是多少?

这个到底要不要关心,这里还是要去关系的,具体可以看gitee项目里的readme文档。

2.2往redis里装载数据

    @Testpublic void likeBlog() {long userId = 0;for (int i = 4; i < 10000; i++) {blogService.likeBlog(userId,userId++);}}

补充单线程的操作:

@Override
public Result updateAllLikeListToDatabaseByNoThreads() {int totalCount = 0;String prefix = "BLOG_LIKED_KEY";Jedis jedis = null;ScanParams scanParams = new ScanParams();try {jedis = jedisPool.getResource();String cursor = "0";do {// 扫描并获取一部分keyScanResult<String> result = jedis.scan(cursor, scanParams.match(prefix.concat("*")));//https://www.cnblogs.com/xubiao/p/8489522.html// 记录cursorcursor = result.getCursor();List<String> list = result.getResult();totalCount += list.size();if (list == null || list.isEmpty()) {break;}// 遍历for (String key : list) {log.debug("key is {}", key);//这里可以看到有哪些keyMap<String, String> map = queryAllBlogLikesUserIdWithScoresByRedisKey(key);Long blogId = Long.parseLong(key.substring(prefix.length(), key.length()));durableToDatabaseByBlogIdAndMap(map, blogId);}} while (!cursor.equals("0"));} catch (Exception e) {e.printStackTrace();} finally {if (jedis != null) jedis.close();}log.debug("totalCount is {}", totalCount);return Result.ok("some like_lists had bean durable in database from cache");}

接下来要来实现多线程的优化了,有2种优化方式,都可以,各有各自的应用场景。

为什么可以用多线程来优化?redis服务器是有一个容器可以装载命令的,发多个命令过去可以减少网络通讯的时间,MySQL也一样。

3.1优化1-多线程-等待方式

循环{通过scan扫描一部分key(如果扫描的游标归0,说明已经扫描结束)开启线程来执行:{通过这部分key在redis找到博客点赞信息再存储到数据库里面。}等待这些线程都执行完毕后再进入下一次循环。
}

1、多线程-等待-的代码:

@GetMapping("/updateAllLikeListToDatabaseByThreads")
@ApiOperation("测试多线程持久化接口")
public Result updateAllLikeListToDatabaseByThreads(){return blogService.updateAllLikeListToDatabaseByThreads();
}
    @Overridepublic Result updateAllLikeListToDatabaseByThreads() {int totalCount = 0;String prefix = "BLOG_LIKED_KEY";Jedis jedis = null;ScanParams scanParams = new ScanParams();try {jedis = jedisPool.getResource();String cursor = "0";do {// 扫描并获取一部分keyScanResult<String> result = jedis.scan(cursor, scanParams.match(prefix.concat("*")));//https://www.cnblogs.com/xubiao/p/8489522.html// 记录cursorcursor = result.getCursor();List<String> list = result.getResult();totalCount += list.size();if (list == null || list.isEmpty()) {break;}//                List<Thread> threads = new Vector<>(list.size());//对于线程的添加,这里不需要用Vector,因为临界区仅仅包括线程里面的内容List<Thread> threads = new ArrayList<>(list.size());//用ArrayList足以// 遍历for (String key : list) {log.debug("key is {}", key);//这里可以看到有哪些keyThread t = new Thread(() -> {Map<String, String> map = queryAllBlogLikesUserIdWithScoresByRedisKey(key);Long blogId = Long.parseLong(key.substring(prefix.length(), key.length()));durableToDatabaseByBlogIdAndMap(map, blogId);});//这里是和like05不一样的地方 TODO postman已经测试通过了,还没有对jemeter进行测试// 判断key的类型t.start();threads.add(t);}threads.forEach((t) -> {try {t.join();} catch (InterruptedException e) {e.printStackTrace();}});threads = null;} while (!cursor.equals("0"));} catch (Exception e) {e.printStackTrace();} finally {if (jedis != null) jedis.close();}log.debug("totalCount is {}", totalCount);return Result.ok("some like_lists had bean durable in database from cache");}

也就是用了join来等待一下,好处是如果风险某些线程没办法执行完毕,那么可以通过threads这个ArrayList集合来拿到抛出异常对象做一些异常处理已来做一些兜底或者防护或者是补救的措施,当然应该也可以执行将执行失败的线程再次通过其他方法让他重新执行直到执行成功(本项目目前就没写这些内容了,可以自己优化)。

3.2多线程-异步

这里是因为线程创建一般默认是非守护线程,因此不用join时,主线程执行完毕,哪怕守护线程没执行完毕,后续也会继续执行下去。

//异步
@Override
public Result updateAllLikeListToDatabaseByThreadsWithNoJoin() {int totalCount = 0;String prefix = "BLOG_LIKED_KEY";Jedis jedis = null;ScanParams scanParams = new ScanParams();try {jedis = jedisPool.getResource();String cursor = "0";do {// 扫描并获取一部分keyScanResult<String> result = jedis.scan(cursor, scanParams.match(prefix.concat("*")));//https://www.cnblogs.com/xubiao/p/8489522.html// 记录cursorcursor = result.getCursor();List<String> list = result.getResult();totalCount += list.size();if (list == null || list.isEmpty()) {break;}// 遍历for (String key : list) {log.debug("key is {}", key);//这里可以看到有哪些keynew Thread(() -> {Map<String, String> map = queryAllBlogLikesUserIdWithScoresByRedisKey(key);Long blogId = Long.parseLong(key.substring(prefix.length(), key.length()));durableToDatabaseByBlogIdAndMap(map, blogId);}).start();//这里是和like05不一样的地方 TODO postman已经测试通过了,还没有对jemeter进行测试// 判断key的类型}} while (!cursor.equals("0"));} catch (Exception e) {e.printStackTrace();} finally {if (jedis != null) jedis.close();}log.debug("totalCount is {}", totalCount);return Result.ok("some like_lists had bean durable in database from cache");}

就这样了,其他的测试过程使用到了postman和jmeter这些工具,具体可以看项目的README文档。

4、其他

肯定是还有其他优化空间的,当然也不一定这个项目的代码没有瑕疵,如果有问题可以在gitee或B站评论区提问。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/3519.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CASAIM与大疆达成全自动化测量技术合作,CASAIM IS全自动化蓝光测量仪实现无人机叶片全尺寸检测及质量控制

近期&#xff0c;CASAIM与大疆达成全自动化测量技术合作&#xff0c;CASAIM IS全自动化蓝光测量仪实现无人机叶片全尺寸检测及质量控制。 无人机行业在过去几年里取得了迅猛发展&#xff0c;大疆是全球领先的无人飞行器控制系统及无人机解决方案的研发商和生产商&#xff0c;客…

Spring-AOP(面向切面)

Spring-AOP(面向切面) 场景模拟(计算器) 功能接口 public interface Calculator {int add(int i, int j);int minus(int i, int j);int multiply(int i, int j);int div(int i, int j); }实现类 public class CalculateLogImpl implements Calculator {Overridepublic int …

PALO ALTO NETWORKS 的新一代防火墙如何保护企业安全

轻松采用创新技术、阻止网络攻击得逞并专注更重要的工作 IT 的快速发展已改变网络边界的面貌。数据无处不在&#xff0c;用户可随时随地从各类设备访问这些数据。同时&#xff0c;IT 团队正在采用云、分析和自动化来加速新应用的交付以及推动业务发展。这些根本性的转变带来了…

Go 框架推荐

基础库 castgoframe Kafka kafka-gosaramagoka Redis go-redis 本地缓存 freecachebigcache Nacos & Viper nacos-sdk-govipernacos-viper Golang Web gingo-zeroREST client Go Grpc go-groc Plugin ** go-plugin HBase go-hbase Postgresql pgx-ormpg…

【Linux】- 任务调度和定时任务

任务调度和定时任务 1 crond 任务调度2 at 定时任务 1 crond 任务调度 crontab 进行 定时任务的设置 任务调度&#xff1a;是指系统在某个时间执行的特定的命令或程序。 任务调度分类&#xff1a;1.系统工作&#xff1a;有些重要的工作必须周而复始地执行。如病毒扫描等 个别…

ChatGPT 最佳实践指南之:系统地测试变化

Test changes systematically 系统地测试变化 Improving performance is easier if you can measure it. In some cases a modification to a prompt will achieve better performance on a few isolated examples but lead to worse overall performance on a more representa…

【Docker】Docker基本概念

Docker基本概念 1.Docker概述1.1 Docker是什么&#xff1f;1.2 Docker的宗旨1.3 容器的优点1.4 Docker与虚拟机的区别1.5 容器在内核中支持的两种技术1.6 namespace的六大类型 2.Docker核心概念2.1 镜像2.2 容器2.3 仓库 3. 知识点总结3.1 Docker是什么&#xff1f;3.2 容器和虚…

智能分析网关V2有抓拍告警但无法推送到EasyCVR,是什么原因?

我们在此前的文章中也介绍了关于智能分析网关V2接入EasyCVR平台的操作步骤&#xff0c;感兴趣的用户可以查看这篇文章&#xff1a;在EasyCVR新版本v3.3中&#xff0c;如何正确接入智能分析网关V2&#xff1f; 智能分析网关V2是基于边缘AI计算技术&#xff0c;可对前端摄像头采…

常见Redis使用问题

一 lettuce使用问题 1 问题描述 Redis Cluster集群&#xff0c;当master宕机&#xff0c;主从切换&#xff0c;客户端报错 timed out 2 原因 SpringBoot2.X版本开始Redis默认的连接池都是采用的Lettuce。当节点发生改变后&#xff0c;Letture默认是不会刷新节点拓扑的。 3…

每日一题2023.7.19|ACM模式

文章目录 C的输入方式介绍cin>>cin.get(字符变量名)cin.get(数组名,接收字符数目)cin.get()cin.getline() getline()gets()getchar() AB问题|AB问题||AB问题|||ABⅣAB问题ⅤAB问题Ⅵ C的输入方式介绍 参考博客 cin>> 最基本&#xff0c;最常用的字符或者数字的输…

el-tree 树全部展开或收起

绑定属性expanded&#xff0c;树自带方法this.$refs.tree.store.root.expanded&#xff0c;在mounted方法中给树方法赋值expandAll false&#xff0c;具体代码实现详情如下&#xff1a; html代码&#xff1a; <template><el-treeref"tree":data"sho…

java建造者模式

在Java中实现建造者模式&#xff0c;可以通过创建一个建造者类&#xff08;Builder&#xff09;和一个产品类&#xff08;Product&#xff09;来完成。下面是一个简单的示例&#xff1a; 首先&#xff0c;我们创建一个产品类&#xff08;Product&#xff09;&#xff0c;其中包…

rabbitmq部署(docker方式)

前言&#xff1a;rabbitmq一旦有漏洞&#xff0c;版本升级麻烦&#xff0c;于是改为docker部署 环境&#xff1a;centos7 #停掉之前的服务 systemctl stop rabbitmq-server systemctl disable rabbitmq-server 查了官网&#xff0c;当前3.11.x 最高版本是3.11.19, 虽然3.12…

jupyter定制数学函数

from math import * #导入绘图模块 import numpy as np #导入数值计算模块 import matplotlib.pyplot as plt #导入绘图模块 plt.rcParams[font.sans-serif][SimHei] #绘图中文 plt.rcParams[axes.unicode_minus]False #绘图负号 import mpl_toolkits.axisartist as axisartist…

微信公众平台自定义菜单 /事件推送

用户点击自定义菜单后&#xff0c;微信会把点击事件推送给开发者&#xff0c;请注意&#xff0c;点击菜单弹出子菜单&#xff0c;不会产生上报。请注意&#xff0c;第3个到第8个的所有事件&#xff0c;仅支持微信iPhone5.4.1以上版本&#xff0c;和Android5.4以上版本的微信用户…

react中使用redux,但是通过useEffect监听不到redux中的数据变化?

在React中使用Redux并通过useEffect监听Redux中的数据变化时&#xff0c;需要使用react-redux库中的useSelector钩子来选择需要监听的Redux状态。useSelector函数允许您从Redux存储中选择和获取特定的状态。 以下是一种在React组件中使用Redux&#xff0c;并通过useEffect监听…

安卓通过adb pull和adb push 手机与电脑之间传输文件

1.可以参考这篇文章 https://www.cnblogs.com/hhddcpp/p/4247923.html2.根据上面的文章&#xff0c;我做了如下修改 //设置/system为可读写&#xff1a; adb remount //复制手机中的文件到电脑中。需要在电脑中新建一个文件夹&#xff0c;我新建的文件夹为ce文件夹 adb pull …

如何在无人机支持下完成自然灾害风险评估的原理和方法

对灾害的损失进行估算与测算是制定防灾、抗灾、救灾 及灾后重建方案的重要依据。 自然灾害评估按灾害客观地发展过程可分三种&#xff1a;一是灾前预评估&#xff0c;二是灾期跟踪或监测性评估&#xff0c;三是灾后实测评估。 灾前预评估要考虑三个因素&#xff0c;第一是未来…

基于ssm的社区生活超市的设计与实现

博主介绍&#xff1a;专注于Java技术领域和毕业项目实战。专注于计算机毕设开发、定制、文档编写指导等&#xff0c;对软件开发具有浓厚的兴趣&#xff0c;工作之余喜欢钻研技术&#xff0c;关注IT技术的发展趋势&#xff0c;感谢大家的关注与支持。 技术交流和部署相关看文章…

JVM系列(5)——类加载过程

一、类的生命周期 加载&#xff08;Loading&#xff09;、验证&#xff08;Verification&#xff09;、准备&#xff08;Preparation&#xff09;、解析&#xff08;Resolution&#xff09;、初始化&#xff08;Initialization&#xff09;、使用&#xff08;Using&#xff09…