Redis打包事务,分批提交

一、需求背景

      接手一个老项目,在项目启动的时候,需要将xxx省整个省的所有区域数据数据、以及系统字典配置逐条保存在Redis缓存里面,这样查询的时候会更快;
      区域数据+字典数据一共大概20000多条,,前同事直接使用 list.forEach()逐条写入Redis,如下:

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
/*** @author xxx* @version 1.0* @date 2022/7/21 15:29* @Description: 项目启动成功后初始化区域数据到redis*/
@Component
@Slf4j
public class AreasInitialComponent implements ApplicationRunner {@AutowiredprivateAreaMapper areaMapper;private static boolean isStart = false;/*** 项目启动后,初始化字典到缓存*/@Overridepublic void run(ApplicationArguments args) throws Exception {if (isStart) {return;}try {log.info("Start*******************项目启动后,初始化字典到缓存*******************");QueryWrapper<Area> wrapper = new QueryWrapper<>();wrapper.eq("del", "0");List<Area> areas = areaMapper.selectList(wrapper);if (!CollectionUtils.isEmpty(areas )) {RedisCache redisCache = SpringUtils.getBean(RedisCache.class);//先将区域集合整体做个缓存log.info("*******************先将区域集合整体做个缓存*******************");AreaUtil.setAreaListCache(redisCache, areas);//再将每一条区域进行缓存areas.stream().forEach(a -> {AreaUtil.setAreaCache(redisCache, a.getId(), a);});}isStart = true;log.info("End*******************项目启动后,初始化字典到缓存*******************");}catch (Exception e) {e.printStackTrace();}}
}

image.png

导致项目启动速度巨慢,再加上需要使用代理软件才能连接公司的数据库,每次启动项目都需要10几分钟,当真是苦不堪言;由于受不了这样的启动速度,因此决定自己动手优化。

二、解决思路

      联想到MySQL的事务打包方式,于是自己动手尝试通过Redis打包事务+分批提交的方式来提高启动速度,具体实现如下:

三、实现方法

  1. 实现方法
   @Autowiredpublic RedisTemplate redisTemplate;  /*** 逐条设置区域缓存** @param areas* @throws InterruptedException*/public void setAreaCacheItemByItem(List<Area> areas) throws InterruptedException {MoreThreadCallBack<Area> callBack = new MoreThreadCallBack<>();callBack.setThreadCount(10);callBack.setLimitCount(50);callBack.setTitle("设置区域缓存批量任务");callBack.setAllList(areas);callBack.call((list, threadNum) -> {//使用自定义线程回调工具分摊任务redisTemplate.execute(new SessionCallback<Object>() {@Overridepublic Object execute(RedisOperations operations) throws DataAccessException {//开启redis事务operations.multi();list.forEach(item -> {operations.opsForValue().set(item.getId(), item);});// 提交事务operations.exec();return null;}});});}
  1. 线程回调工具MoreThreadCallBack()
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;@Data
@Slf4j
public class MoreThreadCallBack<P> {public int limitCount = 1000;private int threadCount = 10;private List<P> allList;private AtomicInteger errorCheck;private String title;public interface CallBack<P> {void call(List<P> list, Integer threadNum);}public boolean call(CallBack<P> callBack) throws InterruptedException, RuntimeException {if (allList.isEmpty()) {return false;}// 线程池ExecutorService exec = Executors.newCachedThreadPool();// 根据大小判断线程数量if (allList.size() <= limitCount) {threadCount = 1;}// 等待结果类final CountDownLatch countDownLatch = new CountDownLatch(threadCount);// 分摊多份List<List<P>> llist = Lists.newArrayList();for (int i = 0; i < threadCount; i++) {llist.add(Lists.newArrayList());}int index = 0;for (P p : allList) {llist.get(index).add(p);index = index == (threadCount - 1) ? 0 : index + 1;}// 异常记录errorCheck = new AtomicInteger(0);// 执行for (int i = 0; i < llist.size(); i++) {List<P> list = llist.get(i);final Integer threadNum = i;exec.execute(() -> {long startTime = System.currentTimeMillis();//抛出异常 自身不处理log.info("标题:{}-{}号线程开始回调执行 数量:{}", this.getTitle(), threadNum, list.size());callBack.call(list, threadNum);long endTime = System.currentTimeMillis();log.info("标题:{}-{}号线程回调执行完毕 耗时:{}", this.getTitle(), threadNum, +(endTime - startTime));countDownLatch.countDown();});}// 等待处理完毕countDownLatch.await();// 关闭线程池exec.shutdown();return errorCheck.get() <= 0;}public boolean next() {// 检测是否有线程提前结束if (errorCheck.get() > 0) {return false;}return true;}public void error() {errorCheck.incrementAndGet();}public String getTitle() {return title == null ? "" : title;}
}
  1. 经过如上处理以后,项目启动速度大大提升,由原本的10几分钟缩短至1分钟左右,成果如下:
    image.png

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

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

相关文章

Windows安装MongoDB

1、下载MongoDB的zip&#xff0c;解压 2、创建目录 mkdir D:\JavaSoftware\Database\MongoDB\mongodb-win32-x86_64-windows-5.0.8\data\db mkdir D:\JavaSoftware\Database\MongoDB\mongodb-win32-x86_64-windows-5.0.8\data\log 3、创建一个配置文件mongod.cfg&#xff0c…

使用一个接口的结果作为第二个接口的参数并将两者的数据放置成下拉框的格式

背景 我使用下拉框实现选择id 但是只有两个接口 一个是所有的id 另一个是id对应的具体信息 我想把id传入另一个接口并且获取其name然后写成类似这样的数组 [ { value: 1, label: ‘名称1’ }&#xff0c; { value: 2, label: ‘名称2’ } { value: 3, label: ‘名称3’ } ] 然…

【PPspliT】ppt转pdf-保留过渡动画

网址 http://www.maxonthenet.altervista.org/ppsplit.php 下载安装 使用 再次打开ppt&#xff0c;就能在上方的选项栏里头看到了&#xff1a;

GEE生物量碳储量——利用红和近红外波段和OTSU大津法提取纯净森林面积

简介: 如何利用红和近红外波段和OTSU大津法提取纯净森林面积?本文的主要逻辑是利用特定时期的遥感影像的波段,提取指定范围的内的DN值,然后分别统计发生阈值变化的峰值区域,从而作为筛选森林的临界点,如果研究区较大的话则需要先进行影像分割,分割成为相同大小的区域,…

前端开发Java学习

注释&#xff1a; 单行注释 // 多行注释 /* */ 文件注释 /** */ 1 关键字 &#xff08;关键字一定是小写&#xff09; 2 常量 字符串常量"HelloWord","你好世界"整数常量12&#xff0c;-33小数常量3.14&#xff0c;22.1字符常量A&#xff0c;a &…

不是默认进入Linux|总是自动进入windows系统

问题描述 不是默认进入Linux系统无法主动出现boot引导自动进入windows系统 尝试无效 修复引导无效重装Grub无效重装系统无效 环境 Ubuntu 22.04 LST微星主板 解决方案 修改引导顺序&#xff1a; 开机狂按Del键&#xff0c;进入BIOS系统&#xff0c;左侧Settings 设置&…

src和href的区别

前言 持续学习总结输出中&#xff0c;src和href都是HTML中特定元素的属性&#xff0c;都可以用来引入外部的资源。两者区别如下&#xff1a; 1、作用 href 用于在当前文档和引用资源之间确立联系。 src 用于替换当前内容。 2、范围用途 src&#xff08;source&#xff09…

RabbitMQ基础教程

1.什么是消息队列 消息队列&#xff08;Message Queue&#xff09;&#xff0c;我们一般简称为MQ。消息队列中间件是分布式系统中重要的组件&#xff0c;具有异步性、松耦合、分布式、可靠性等特点。用于实现高性能、高可用、可伸缩和最终一致性架构。是大型分布式系统不可缺少…

Angular11 MSAL B2C登录实例 (一)

前言 因为项目需求&#xff0c;需要把Angular 11项目中登录方式改成B2C登录&#xff0c;所以在参考了一系列文档后&#xff0c;成功通过MSAL将项目的登录方式改成B2C登录。下面介绍了详细步骤及一些注意事项。 步骤&#xff1a; 1. 安装MSAL 在项目中安装msal npm i azure/…

BUUCTF [WUSTCTF2020]find_me 1

BUUCTF:https://buuoj.cn/challenges 题目描述&#xff1a; 得到的 flag 请包上 flag{} 提交。 感谢 Iven Huang 师傅供题。 比赛平台&#xff1a;https://ctfgame.w-ais.cn/ 密文&#xff1a; 下载附件&#xff0c;得到一个.jpg图片。 解题思路&#xff1a; 1、得到一张图…

vue中页面(路由)跳转及传值的几种方式 router-link + query + params

vue中页面(路由)跳转及传值的几种方式 知道query 和 params 是什么 参考文案:https://www.php.cn/js-tutorial-382859.html 跳转的几种方式与传值 1、router-link 1.1 根据路由路径(无参数与有参数) <router-link to = "/page">跳转到page页面</…

Java WebSocket 客户端接收大量数据

介绍 WebSocket 是一种基于 TCP 协议的全双工通信协议&#xff0c;它能够在客户端和服务器之间建立一个持久连接&#xff0c;实现实时的双向数据传输。在实际应用中&#xff0c;有时候我们需要处理大量的数据&#xff0c;例如实时监控系统或者实时股票行情等。本文将介绍如何使…

Matlab三角剖分插值问题分析

目录 前言 一、问题引入 二、一个例子 1.生成散点图 2.对数据进行剖分 3.点法式分析 三、最后结果 前言 上一篇文章感觉对三角剖分问题没有说清楚&#xff0c;这次专门对三角剖分问题再仔细说说。 一、问题引入 实际上这个问题是用来解决二维曲面插值问题的。 二维插值问题&…

外部中断为什么会误触发?

今天在写外部中断的程序的时候&#xff0c;发现中断特别容易受到干扰&#xff0c;我把手放在对应的中断引脚上&#xff0c;中断就一直触发&#xff0c;没有停过。经过一天的学习&#xff0c;找到了几个解决方法&#xff0c;所以写了这篇笔记。如果你的中断也时不时会误触发&…

数 组

数组格式 普通数组下标是数字 关联数组下标是字符串 例子&#xff1a; a&#xff08;10 20 30 40 50&#xff09; a[0] 10 数组的分类(普通数组和关联数组) 普通数组 可以不需要手动声明&#xff0c;直接使用 declare -a 数组名 关联数组 一定要手动声明 adeclare …

通过Spring整合MyBatis实现持久层操作

文章目录 为什么要整合Spring和MyBatis&#xff1f;步骤一&#xff1a;添加依赖步骤二&#xff1a;配置数据源步骤三&#xff1a;配置MyBatis步骤四&#xff1a;创建Mapper接口和XML文件步骤五&#xff1a;使用Mapper接口拓展&#xff1a;事务管理 &#x1f389;通过Spring整合…

Leetcode173. 二叉搜索树迭代器

Every day a Leetcode 题目来源&#xff1a;173. 二叉搜索树迭代器 解法1&#xff1a;中序遍历 我们可以直接对二叉搜索树做一次完全的递归遍历&#xff0c;获取中序遍历的全部结果并保存在数组中。随后&#xff0c;我们利用得到的数组本身来实现迭代器。 代码&#xff1a…

竞赛 : 题目:基于深度学习的水果识别 设计 开题 技术

1 前言 Hi&#xff0c;大家好&#xff0c;这里是丹成学长&#xff0c;今天做一个 基于深度学习的水果识别demo 这是一个较为新颖的竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f9ff; 更多资料, 项目分享&#xff1a; https://gitee.com/dancheng-senior/pos…

Spark-06:共享变量

目录 1.广播变量&#xff08;broadcast variables&#xff09; 2.累加器&#xff08;accumulators&#xff09; 在分布式计算中&#xff0c;当在集群的多个节点上并行运行函数时&#xff0c;默认情况下&#xff0c;每个任务都会获得函数中使用到的变量的一个副本。如果变量很…