7、定时进行数据批处理任务

一、StopWatch时间控制类:

StopWatch 是spring工具包org.springframework.util下的一个工具类,主要用于计算同步 单线程执行时间。

1、StopWatch优缺点:

优点:

(1)、spring自带工具类,可直接使用;

(2)、代码实现简单,使用更简单;

(3)、统一归纳,展示每项任务耗时与占用总时间的百分比,展示结果直观;

(4)、性能消耗相对较小,并且最大程度的保证了start与stop之间的时间记录的准确性;

(5)、可在start时直接指定任务名字,从而更加直观的显示记录结果;

缺点:

(1)、一个StopWatch实例一次只能开启一个task,不能同时start多个task,并且在该task未stop之前不能start一个新的task,必须在该task stop之后才能开启新的task,若要一次开启多个,需要new不同的StopWatch实例;

(2)、代码侵入式使用,需要改动多处代码;

2、StopWatch模板:

7、定时进行数据批处理任务
public Object stopWatchDemo(){
        StopWatch stopWatch = new StopWatch("StopWatch测量业务逻辑的运行时间");
        stopWatch.start();
        log.info("====================StopWatch计时任务开始========================");

        //业务逻辑1
        //业务逻辑2
        //......

        stopWatch.stop();
        log.info("====================StopWatch计时任务结束========================");
        log.info(stopWatch.prettyPrint());  //打印执行时间
        return null;
    }

二、数据的批处理:

Java中的批处理用于执行一组插入操作或批处理,因为一次又一次地执行单个插入会浪费时间并降低性能。因此,使用批处理可以一次执行多个插入操作,这样可以提高程序的性能。

1、批处理方式一:

    @Autowired
    private SqlSessionFactory sqlSessionFactory;

    /**
     * 批处理插入
     */
    private void upSave1(List<BatchSaveVO> itemList) {
        SqlSession session = null;
        try {
            // 新获取一个模式为BATCH,自动提交为false的session
            // 如果自动提交设置为true,将无法控制提交的条数,改为最后统一提交,可能导致内存溢出
            session = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
            BatchSaveMapper mapper = session.getMapper(BatchSaveMapper.class);
            int batchCount = 1000;// 每批commit的个数
            int batchLastIndex = batchCount;// 每批最后一个的下标
            for (int index = 0; index < itemList.size(); ) {
                if (batchLastIndex >= itemList.size()) {
                    batchLastIndex = itemList.size();
                    log.info("index:{}", batchLastIndex);
                    mapper.batchInsert(itemList.subList(index, batchLastIndex));
                    session.commit();
                    log.info("index:" + index + " batchLastIndex:" + batchLastIndex);
                    log.info("数据插入完毕");
                    break;// 数据插入完毕,退出循环
                } else {
                    mapper.batchInsert(itemList.subList(index, batchLastIndex));
                    session.commit();
                    log.info("index:" + index + " batchLastIndex:" + batchLastIndex);
                    index = batchLastIndex;// 设置下一批下标
                    batchLastIndex = index + (batchCount - 1);
                }
            }
            session.commit();
        } finally {
            session.close();
        }
    }

2、批处理方式二:

    @Autowired
    private SqlSessionTemplate sqlSessionTemplate;

    private void upSave2(List<BatchSaveVO> newReports) {
        SqlSessionFactory sqlSessionFactory = sqlSessionTemplate.getSqlSessionFactory();
        // 如果自动提交设置为true,将无法控制提交的条数,改为最后统一提交,可能导致内存溢出
        SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, true);
        try {
            Optional.ofNullable(newReports).ifPresent(reports -> {
                BatchSaveMapper batchMapper = sqlSession.getMapper(BatchSaveMapper.class);
                batchMapper.batchInsert(newReports);
                sqlSession.commit();
            });
        }catch (Exception e){
            sqlSession.rollback();
            log.error("================保存失败================:{}",e.getMessage());
        }finally {
            sqlSession.close();
        }
    }

7、定时进行数据批处理任务

3、SqlSessionFactory详解:

(1)、介绍:

SqlSessionFactory是MyBatis的关键字,它是单个数据库映射关系经过编译后的内存镜像,SqlSessionFactory对象的实例可以通过SqlSessionFactoryBuilder对象来获得,而SqlSessionFactoryBuildr则可以从XML配置文件或一个预先定制的Configuration的实例构建出SqlSessionFactory的实例每一个MyBatis的应用程序都以一个SqlSessionFactory对象的实例为核心。SqlSessionFactory是线程安全的,他一旦被创建,应该在应用执行期间都存在,在应用运行期间不要重复创建多次,建议使用单例模式,SqlSessionFactory是创建SqlSession的工厂。

(2)、SqlSessionFactory.openSession()方法:

//新获取一个模式为BATCH,自动提交为false的session
// 如果自动提交设置为true,将无法控制提交的条数,改为最后统一提交,可能导致内存溢出
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);

7、定时进行数据批处理任务

(3)、ExecutorType三种SQL执行器模式:

1)、ExecutorType.SIMPLE:相当于JDBC的stmt.execute(sql) 执行完毕即关闭即stmt.close()

2)、ExecutorType.REUSE:相当于JDBC的stmt.execute(sql) 执行完不关闭,而是将stmt存入Map

3)、ExecutorType.BATCH:相当于JDBC语句的stmt.addBatch(sql),即仅将执行SQL加入到批量计划但是不真正执行,所以此时不会执行返回受影响的行数,而只有执行stmt.execteBatch()后才会真正执行sql。

方式

优势

劣势

ExecutorType.SIMPLE

默认执行器, 节约服务器资源

每次都要开关Statement

ExecutorType.REUSE

提升后端接口处理效率

每次一个新sql都缓存,增大JVM内存压力

ExecutorType.BATCH

专门用于更新插入操作,效率最快

对select 不适用,另外特殊要求,比如限制一次execteBatch的数量时需要写过滤器定制

三、定时任务:

1、@Schedule定时任务方式:

在日常开发中比较简单的实现方式就是使用Spring的@Scheduled()注解。但是在修改服务器时间时会导致定时任务不执行情况的发生,解决的办法是当修改服务器时间后,将服务进行重启就可以避免此现象的发生。

(1)、@Schedule详解:

@Schedule详解参考

(2)、@Schedule的使用:

要使用@Scheduled注解,首先需要在相关类上添加@EnableScheduling,启用Spring的计划任务执行功能,这样可以在容器中的任何Spring管理的bean上检测@Scheduled注解,执行计划任务。

@Scheduled(cron = "0 0 1 * * ?")
    public Object batchSaveWay() {
        StopWatch stopWatch = new StopWatch("@Scheduled定时任务");
        stopWatch.start();
        log.info("====================@Scheduled定时任务开始========================");

        //业务逻辑

        stopWatch.stop();
        log.info("====================@Scheduled定时任务结束========================");
        log.info(stopWatch.prettyPrint());
        return null;
    }

2、SchedulingConfigurer接口线程定时任务方式:

@Schedule 注解有一个缺点,其定时的时间不能动态的改变,而基于SchedulingConfigurer 接口的方式可以做到(例如将cron表达式存入数据库,通过修改数据中的cron表达式,动态改变定时任务的执行周期,而不需要重启项目。)

(1)、操作流程:

1)、声明ThreadPoolTaskScheduler配置类:

@Configuration
public class TaskPoolConfig {
    /**
     * ThreadPoolTaskExecutor是spring core包中的,而ThreadPoolExecutor是JDK中的JUC。
     * ThreadPoolTaskExecutor是对ThreadPoolExecutor进行了封装处理。
     *
     * 拒绝策略:
     * (1)、CallerRunsPolicy: 当触发拒绝策略,只要线程池没有关闭的话,则使用调用 线程直接运行任务。一般并发比较小,性能要求不高,不允许失败。
     *     但是,由于调用者自己运行任务,如果任务提交速度过快,可能导致程序阻塞,性能效率上必然的损失较大
     * (2)、AbortPolicy: 丢弃任务,并抛出拒绝执行
     * (3)、RejectedExecutionException 异常信息。线程池默认的拒绝策略。必须处理好抛出的异常,否则会打断当前的执行流程,影响后续的任务执行。
     * (4)、DiscardPolicy: 直接丢弃,其他啥都没有
     * (5)、DiscardOldestPolicy: 当触发拒绝策略,只要线程池没有关闭的话,丢弃阻塞队列 workQueue 中最老的一个任务,并将新任务加入
     */

    @Bean
    public ThreadPoolTaskScheduler taskSchedulerExecutor() {
        // 创建一个线程池对象
        final ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        // 定义一个线程池大小
        scheduler.setPoolSize(100);
        // 线程池名的前缀
        scheduler.setThreadNamePrefix("taskExecutor-");
        // 设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean
        scheduler.setWaitForTasksToCompleteOnShutdown(true);
        // 设置线程池中任务的等待时间,如果超过这个时候还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住
        scheduler.setAwaitTerminationSeconds(60);
        // 线程池对拒绝任务的处理策略,当线程池没有处理能力的时候,该策略会直接在 execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务
        scheduler.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return scheduler;
    }
}

2)、实现SchedulingConfigurer接口:

@EnableScheduling
@Component
@Configuration
public abstract class ThreadSchedulingConfigurerWay implements SchedulingConfigurer {
    @Autowired
    private TaskPoolConfig taskPoolConfig;

    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        scheduledTaskRegistrar.setScheduler(taskPoolConfig.taskSchedulerExecutor());
        scheduledTaskRegistrar.addTriggerTask(new Runnable() {
            @Override
            public void run() {
                // 定时任务要执行的内容
                executionWay();
            }
        }, new Trigger() {
            @Override
            public Date nextExecutionTime(TriggerContext triggerContext) {
                // 定时任务触发,可修改定时任务的执行周期
                String cron = getCron(); //可以将表达式配置在数据库中,动态改变定时的时间
                CronTrigger trigger = new CronTrigger(cron);
                Date nextExecDate = trigger.nextExecutionTime(triggerContext);
                return nextExecDate;
            }
        });
    }

    public abstract String getCron();
    public abstract void executionWay();

}

3)、实现定时线程任务:

任务一:

//定时线程任务一
@Configuration
public class ThreadSchedulingConfigurerWayOneImpl extends ThreadSchedulingConfigurerWay {
    @Override
    public String getCron() {
        String cron = "0 0/1 * * * ?";
        return cron;
    }

    @Override
    public void executionWay() {
        System.out.println("定时线程任务demo1:"
                + LocalDateTime.now()+",线程名称:"+Thread.currentThread().getName()
                + " 线程id:"+Thread.currentThread().getId());
    }
}

任务二:

//定时线程任务二
@Configuration
public class ThreadSchedulingConfigurerWayTwoImpl extends ThreadSchedulingConfigurerWay {
    @Override
    public String getCron() {
        String cron = "0 0/1 * * * ?";
        return cron;
    }

    @Override
    public void executionWay() {
        System.out.println("定时线程任务demo2:"
                + LocalDateTime.now()+",线程名称:"+Thread.currentThread().getName()
                + " 线程id:"+Thread.currentThread().getId());
    }
}

(2)、ThreadPoolTaskExecutor和ThreadPoolTaskScheduler的区别:

相关参考

3、Quartz框架定时任务方式:

Quartz框架定时任务默认都是并发执行的,不会等待上一次任务执行完毕,只要间隔时间到就会执行,如果定时任执行太长,会长时间占用资源,导致其它任务堵塞。

(1)、添加Quartz依赖:

org.springframework.boot
    spring-boot-starter-quartz

(2)、声明Job任务(继承QuartzJobBean抽象类):

@Slf4j
public class QuartzBatchSaveJob extends QuartzJobBean {
    @Autowired
    @Qualifier("QuartzWay")
    private BatchSaveService quartzWay;

    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        //定时任务的业务逻辑
        quartzWay.batchSaveWay();
    }
}

(3)、编写业务类:

public Object batchSaveWay() {
        StopWatch stopWatch = new StopWatch("Quartz定时框架启动任务");
        stopWatch.start();
        log.info("====================Quartz定时框架启动任务开始========================");

        //业务逻辑

        stopWatch.stop();
        log.info("====================Quartz定时框架启动任务结束========================");
        log.info(stopWatch.prettyPrint());
        return null;
    }

(4)、将Job任务注册到Spring容器中:

@Configuration
public class QuartzConfig {
    //Quartz定时框架启动任务
    @Bean
    public JobDetail businessJobDetail(){
        return JobBuilder.newJob(QuartzBatchSaveJob.class)
                .withDescription("Quartz定时框架启动任务")
                .withIdentity(QuartzBatchSaveJob.class.getSimpleName())
                .storeDurably().build();
    }
    @Bean
    public Trigger businessJobTrigger() {
        String cron = "0 0 1 * * ?";
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
        return TriggerBuilder.newTrigger().forJob(businessJobDetail())
                .withIdentity("QuartzBatchSaveJobTrigger")
                .withSchedule(cronScheduleBuilder).build();
    }
}

搜索

复制

Original: https://www.cnblogs.com/Iven-L/p/16587692.html
Author: 爱文(Iven)
Title: 7、定时进行数据批处理任务

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/575695/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Java基础四—泛型、注解、异常、反射

    泛型 泛型的本质是为了参数化类型( 在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类…

    数据库 2023年6月6日
    082
  • 23种设计模式之备忘录模式

    文章目录 概述 备忘录模式的优缺点 备忘录模式的结构和实现 * 模式结构 模式实现 总结 概述 备忘录模式(Memento Pattern) 保存一个对象的某个状态,以便在适当的时…

    数据库 2023年6月6日
    080
  • 设计模式之(7)——装饰设计模式

    定义:装饰设计模式允许向一个现有的对象添加功能,而不改变其结构(这就很符合程序设计的” 开闭原则“),重点突出类功能的增强,属于结构型创建模式,这种模式创建…

    数据库 2023年6月14日
    061
  • 工具 | 常用 MySQL 内核 Debug 技巧

    作者:柯煜昌 顾问软件工程师目前从事 RadonDB MySQL 容器化研发,华中科技大学研究生毕业,有多年的数据库内核开发经验。 掌握 MySQL 内核源码的阅读和调试能力,不仅…

    数据库 2023年5月24日
    0105
  • FTP文件上传

    一、配置FTP文件服务器 以Ubuntu为例 FTP两种模式简介 PORT(主动模式)第一步FTP客户端首先随机选择一个大于1024的端口p1,并通过此端口发送请求连接到FTP服务…

    数据库 2023年6月6日
    084
  • IntelliJ IDEA 断开svn连接

    1 设置菜单 2 进入pluglns 菜单,点击 browse repositonries….. 3 搜索 svn disconnect,然后安装插件 4 安装插件后,…

    数据库 2023年6月6日
    0149
  • Spring(三)-AOP

    1、名词理解 切面(Aspect): 含有前置通知,后置通知,返回通知,异常抛出通知,环绕通知等方法的 类; 通知(Advice): 对原方法进行添加处理(如日志等)的 方法; 切…

    数据库 2023年6月16日
    070
  • Python第二十天 shutil 模块 zipfile tarfile 模块

    Python第二十天 shutil 模块 zipfile tarfile 模块 注意:压缩打包/解压解包目录和文件使用tarfile模块而不要使用shutil 模块!!! os文件…

    数据库 2023年6月9日
    058
  • 2022-8-16 mysql 第二天 约束

    重点,DQL是我们每天都要接触编写最多也是最难的SQL,该语言用来查询记录,不会修改数据库和表结构。 构建数据库 创建一张student表: DROP TABLE IF EXIST…

    数据库 2023年6月14日
    095
  • 集合自序整理集

    集合和数组一样都是对多个数据进行存储操作的容器 * – 集合长度可变,数组长度固定 – 集合可以存储不同数据类型元素,数组只能存储单一数据类型元素 &#82…

    数据库 2023年6月9日
    0107
  • 我竟然才知道slf4j里还有个MDC

    大家好久不见,我是walking。今天给大家带来一个日志方面的知识——MDC,不知道大家认识不,反正我是最近刚知道的😂 初见MDC 前两天看项目中的代码,无意中看到一个自定义的线程…

    数据库 2023年6月11日
    0141
  • Java 线程常用操作

    继Java线程生命周期继续学习Java线程其他常用操作 线程的常用操作 设置线程名字:setName() 获取线程名称:getName() 线程唯一Id:getId() // 自定…

    数据库 2023年6月6日
    074
  • Vue el-date-picker 组件时间格式化方式

    官网地址:https://element.eleme.cn/#/zh-CN/component/date-picker value-format="yyyy-MM-dd&…

    数据库 2023年6月16日
    078
  • MySQL实战45讲 6,7,8

    06 | 全局锁和表锁 :给表加个字段怎么有这么多阻碍? Connection连接与Session会话 通俗来讲,会话(Session)是通信双⽅从开始通信到通信结束期间的⼀个上下…

    数据库 2023年6月16日
    077
  • 猿创征文|小而巧的API文档生成工具之smart-doc

    文章目录 smart-doc介绍 smart-doc特性 smart-doc的最佳搭档 谁在使用smart-doc smart-doc的优缺点 smart-doc和swagger区…

    数据库 2023年6月6日
    0260
  • MySQL存储过程和函数

    存储过程与函数 类似与Java的方法和C语言的函数 存储过程概述 含义 一组经过 预先编译的SQL语句的封装 执行过程:存储过程预先存储在MySQL服务器上,客户端发出命令后,服务…

    数据库 2023年5月24日
    074
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球