今天我们会介绍Spring Batch批处理读写大型数据库的优化方案
背景介绍
现在我们有两个数据库,数据库A是新数据库,里面保存了公司所有员工的员工工号,数据库B是老数据库,里面保存了公司所有员工对应的详细信息,数据库B里面的数据会每天被定时任务更新。
我们希望用两个定时任务来维护我们的两个数据库,第一个定时任务会把数据库A中所有的员工工号发送给人力资源部门系统,再把从人力资源部门返回的信息写到公司的云盘中。
第二个定时任务会读取云盘中被写入的文件,解析并写出到数据库B中,从而达到每日更新的目的。
初步方案
以上就是我们的需求啦,现在程序员小明推出以下解决方案:
- 人力资源系统新开一个接口,接收一条存储了员工工号的http请求,并返回该员工的详细信息。
- 我们开一个新的定时任务,每天在固定时间会被执行,定时任务的Reader会将数据库内的一条条员工工号读取出来并传给Processor,这里我们设置的chunk size为100,在Processor中我们会把每一个员工工号发送给人力资源再接受返回数据,最后再按批次写出到云盘。
- 第二个定时任务开始工作,读取云盘文件并更新数据库B,本篇文章先略过这个部分。
结果使用该方案定时任务跑了整整24小时才跑完。。
问题分析:
- 现公司员工记录为26万,按照现在的方案我们一共需要发送并处理26万条http请求。
- 所有请求单线程序列化运行,不具备任何并发能力。
优化方案一.
优化People接口,并优化每一次Reader和Processor的读和处理过程。
这里Reader的每一次读取都需要对数据库进行分页,主要需要pageNum 和 pageSize这两个变量,变量offset就是pageNum 乘以 pageSize,每读一次pageNum加一。
我们可以将变量注入XML的MySQL语句中,通过MyBatis对数据库进行分页读,每次读100个数据,将读取到的数据打包成一个list一次性传递给Processor。Processor再将List传给人力资源系统,之后利用StringBuilder将返回的数据封装成一个String,传递给Writer直接写出。
这样一来http请求数量为之前的1/100,定时任务运行时间从24小时被缩短到了1小时。
这里主要为大家分享以下代码,自定义Reader类,Processor类和Writer类的实现:
public class BatchReader<T> implements ItemReader<List<T