前言:
此刻小伙伴们对“数据库定时任务”都比较注意,同学们都想要剖析一些“数据库定时任务”的相关知识。那么小编也在网摘上收集了一些关于“数据库定时任务””的相关知识,希望小伙伴们能喜欢,朋友们快快来了解一下吧!我们在实际开发中,多多少少都会用到定时任务来处理一些问题。
比如金融项目中的对账,每天定时对昨天的账务进行核对,每个月初对上个月的账务进行核对等。
还比如,我们需要处理一些老数据迁移,修复一些新项目和老项目数据不兼容的问题等等。
常规实现方案方案1:Timer
这个目前在项目中用得较少,直接贴demo代码。
具体的介绍可以查看api ,但是在某些框架中是有用到。
public class TestTimer { public static void main(String[] args) { TimerTask timerTask = new TimerTask() { @Override public void run() { System.out.println("task run:"+ new Date()); } }; Timer timer = new Timer(); //安排指定的任务在指定的时间开始进行重复的固定延迟执行。这里是每3秒执行一次 timer.schedule(timerTask,10,3000); }}
执行结果:
task run:Sun Dec 11 21:23:47 CST 2022task run:Sun Dec 11 21:23:50 CST 2022task run:Sun Dec 11 21:23:53 CST 2022
这么使用,阿里代码检查插件会提示:
从提示中可以看出,在多线程并行处理定时任务时,Timer运行多个TimerTask时,只要有其中之一没有捕获抛出的异常,其他任务会自动终止运行。
方案2:ScheduledExecutorService
和Timer类型,也就是阿里代码检查插件推荐的方案:
public class TestScheduledExecutorService { public static void main(String[] args) { ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor(); // 参数:1、任务体 2、首次执行的延时时间 // 3、任务执行间隔 4、间隔时间单位 service.scheduleAtFixedRate(()->System.out.println("task ScheduledExecutorService "+new Date()), 0, 3, TimeUnit.SECONDS); }}
运行结果:
task ScheduledExecutorService Sun Dec 11 21:30:06 CST 2022task ScheduledExecutorService Sun Dec 11 21:30:09 CST 2022task ScheduledExecutorService Sun Dec 11 21:30:12 CST 2022
阿里代码检查插件也会提示:
这里提示的是我们创建线程池的方式,建议我们使用手动创建线程池,不要使用Executors工厂类,因为手动创建更能有效规划资源的使用。
方案3:spring task
用起来也非常简单:
@Slf4j@Componentpublic class ScheduledService { @Scheduled(cron = "0/5 * * * * *") public void scheduled(){ log.info("=====>>>>>使用cron {}",System.currentTimeMillis()); } @Scheduled(fixedRate = 5000) public void scheduled1() { log.info("=====>>>>>使用fixedRate{}", System.currentTimeMillis()); } @Scheduled(fixedDelay = 5000) public void scheduled2() { log.info("=====>>>>>fixedDelay{}",System.currentTimeMillis()); }}
运行结果:
2022-12-11 21:36:25.001 INFO 10660 --- [ scheduling-1] com.tian.utils.ScheduledService : =====>>>>>使用cron 16707657850012022-12-11 21:36:28.212 INFO 10660 --- [ scheduling-1] com.tian.utils.ScheduledService : =====>>>>>使用fixedRate16707657882122022-12-11 21:36:28.212 INFO 10660 --- [ scheduling-1] com.tian.utils.ScheduledService : =====>>>>>fixedDelay16707657882122022-12-11 21:36:30.001 INFO 10660 --- [ scheduling-1] com.tian.utils.ScheduledService : =====>>>>>使用cron 16707657900012022-12-11 21:36:33.212 INFO 10660 --- [ scheduling-1] com.tian.utils.ScheduledService : =====>>>>>使用fixedRate16707657932122022-12-11 21:36:33.213 INFO 10660 --- [ scheduling-1] com.tian.utils.ScheduledService : =====>>>>>fixedDelay16707657932132022-12-11 21:36:35.001 INFO 10660 --- [ scheduling-1] com.tian.utils.ScheduledService : =====>>>>>使用cron 16707657950012022-12-11 21:36:38.214 INFO 10660 --- [ scheduling-1] com.tian.utils.ScheduledService : =====>>>>>使用fixedRate16707657982142022-12-11 21:36:38.214 INFO 10660 --- [ scheduling-1] com.tian.utils.ScheduledService : =====>>>>>fixedDelay16707657982142022-12-11 21:36:40.001 INFO 10660 --- [ scheduling-1] com.tian.utils.ScheduledService : =====>>>>>使用cron 16707658000012022-12-11 21:36:43.214 INFO 10660 --- [ scheduling-1] com.tian.utils.ScheduledService : =====>>>>>使用fixedRate16707658032142022-12-11 21:36:43.215 INFO 10660 --- [ scheduling-1] com.tian.utils.ScheduledService : =====>>>>>fixedDelay1670765803215方案4:多线程执行
基于注解设定多线程定时任务 :
@Component@EnableScheduling // 1.开启定时任务@EnableAsync // 2.开启多线程public class MultithreadScheduleTask { @Async @Scheduled(fixedDelay = 5000) //间隔5秒 public void first() throws InterruptedException { System.out.println("第一个定时任务开始 : " + LocalDateTime.now().toLocalTime() + "\r\n线程 : " + Thread.currentThread().getName()); System.out.println(); Thread.sleep(1000 * 10); } @Async @Scheduled(fixedDelay = 5000) public void second() { System.out.println("第二个定时任务开始 : " + LocalDateTime.now().toLocalTime() + "\r\n线程 : " + Thread.currentThread().getName()); System.out.println(); }}
运行结果:
第一个定时任务开始 : 21:44:02.800线程 : 入库操作日志记录表 线程1第二个定时任务开始 : 21:44:02.801线程 : 入库操作日志记录表 线程2第一个定时任务开始 : 21:44:07.801线程 : 入库操作日志记录表 线程3第二个定时任务开始 : 21:44:07.802线程 : 入库操作日志记录表 线程4第一个定时任务开始 : 21:44:12.807线程 : 入库操作日志记录表 线程5第二个定时任务开始 : 21:44:12.812线程 : 入库操作日志记录表 线程6......方案5:quartz
我们需要引入依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId></dependency>
实现类:
public class Myquartz extends QuartzJobBean { @Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { System.out.println("这是我的 quartz 定时任务"); }}
配置类:
/** * @author tianwc 面试专栏 * @version 1.0.0 * @date 2022年12月11日 21:48 */@Configurationpublic class QuartzConfig { @Bean public JobDetail teatQuartzDetail(){ return JobBuilder.newJob(MyQuartz.class).withIdentity("myQuartz").storeDurably().build(); } @Bean public Trigger testQuartzTrigger(){ SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(10) //设置时间周期单位秒 .repeatForever(); return TriggerBuilder.newTrigger().forJob(teatQuartzDetail()) .withIdentity("testQuartz") .withSchedule(scheduleBuilder) .build(); }}
只要启动Spring Boot项目,就会输出:
这是我的 quartz 定时任务这是我的 quartz 定时任务这是我的 quartz 定时任务其他方案
我们在项目,可能会涉及动态调整定时任务执行core表达式、动态关闭开启定时任务,我们可以使用SchedulingConfigurer来实现(使用数据库结合来搞):
比如:
@Configurationpublic class ScheduledConfig implements SchedulingConfigurer { @Autowired private ApplicationContext context; @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { for (SpringScheduledCron springScheduledCron : cronRepository.findAll()) { Class<?> clazz; Object task; try { clazz = Class.forName(springScheduledCron.getCronKey()); task = context.getBean(clazz); } catch (ClassNotFoundException e) { throw new IllegalArgumentException("spring_scheduled_cron表数据" + springScheduledCron.getCronKey() + "有误", e); } catch (BeansException e) { throw new IllegalArgumentException(springScheduledCron.getCronKey() + "未纳入到spring管理", e); } Assert.isAssignable(ScheduledOfTask.class, task.getClass(), "定时任务类必须实现ScheduledOfTask接口"); // 可以通过改变数据库数据进而实现动态改变执行周期 taskRegistrar.addTriggerTask(((Runnable) task), triggerContext -> { //这个可以使用持久层,比如Mybatis来实现,从数据库中获取 String cronExpression = "0/10 * * * * ? " return new CronTrigger(cronExpression).nextExecutionTime(triggerContext); } ); } } @Bean public Executor taskExecutor() { return Executors.newScheduledThreadPool(10); }}
如果项目中用得到类似的,可以网上搜搜SchedulingConfigurer便可实现。
进而再扩展,那就来到分布式任务调度了。
什么是分布式任务调度?
任务调度是指基于给定的时间点,给定的时间间隔或者给定执行次数自动得执行任务。任务调度是是操作系统的重要组成部分,而对于实时的操作系统,任务调度直接影响着操作系统的实时性能。任务调度涉及到多线程并发、运行时间规则定制及解析、线程池的维护等诸多方面的工作。
WEB服务器在接受请求时,会创建一个新的线程服务。但是资源有限,必须对资源进行控制,首先就是限制服务线程的最大数目,其次考虑以线程池共享服务的线程资源,降低频繁创建、销毁线程的消耗;然后任务调度信息的存储包括运行次数、调度规则以及运行数据等。一个合适的任务调度框架对于项目的整体性能来说显得尤为重要。
分布式任务调度框架有:cronsun、Elastic-job、saturn、lts、TBSchedule、xxl-job 等。
另外,就是cron表达式,推荐
可以根据自己业务情况来,手动选择,自动生成表达式。
好了,今天就分享这么多。
标签: #数据库定时任务