文章目录
一、背景
二、代码实现:
三、分页查询下游批次处理场景
四、MyBatis的批量插入
1、活动表简单表结构:
2、业务层组装数据:
一、背景
数据量较多时,我们常常遇到需要分批处理的情况,比如上千上万数据需要需要操作数据库时(入库或者更新),我们想到分批处理,或者解析文件数据量较多,我们可能多线程分批处理,每个线程执行一定的批次数据等…
不管何种场景,当我们想到分批处理时,那如何分批,怎么计算批次数呢?
tip:此处以小数据量为例:假设待处理数据总条数75,每10条数据一处理
我常用的方法总结两种供参考:
方法一:循环遍历待处理的处理,当到达待处理的批次或批次倍数,或最后不足一次剩余的,满足条件即执行一次分批处理操作。eg:10,20,30…7
方法二:根据总数计算总批次数(75/10+1=8次),每次从待处理数据集中截取要处理的数据,最后一个批次特殊处理。eg:0-9,10-19…70-74
二、代码实现:
public static void main(String[] args) {
List sourList = new ArrayList();
//1、将list中放进去75个数据--模拟待处理数据集
for (int i = 0; i < 75; i++) {
sourList.add(i);
}
//假设每N个(10个)数据一处理(实际应用中可能每N个开一个线程,或者每N个调一次数据库操作)
int batchSize = 10;
//tempList存放批次数据
List<Object> tempList = new ArrayList<Object>();
//方法一:循环遍历待处理的处理(可能多线程,可能分批次入数据库,总之需要按每次处理的大小,计算需要处理的批次数)
for (int i = 0; i < sourList.size(); i++) {
tempList.add(sourList.get(i));
//判断批次,够N个数开始处理一波
if ((i + 1) % batchSize == 0 || (i + 1) == sourList.size()) {
log.info("处理数据集:{}", tempList.toString());
//todo 你的线程处理,或者数据库操作等
tempList.clear();
}
}
log.info("************* 方法一结束 *************");
//方法二:根据总数计算总批次数,每次从待处理数据集中截取要处理的数据
int count = sourList.size();
int totalPages = (count % batchSize > 0) ? (count / batchSize + 1) : (count / batchSize);
log.info("待处理数据总条数:{},总批次数:{}", count, totalPages);
int startIndex = 0;
int endIndex = 0;
if (totalPages > 0) {
//循环批次数,计算待处理数据下标
for (int pageNum = 1; pageNum <= totalPages; pageNum++) {
//每批次计算要处理的数据起始位置。终止位置
List subList = new ArrayList();
startIndex = ((pageNum - 1) * batchSize);
if (pageNum == totalPages) {
//考虑最后一个批次特殊处理
subList = sourList.subList(startIndex, count);
} else {
endIndex = startIndex + batchSize;
subList = sourList.subList(startIndex, endIndex);
}
//todo 你的线程处理,或者数据库操作等
log.info("处理第{}批次:{}", pageNum, subList.toString());
}
}
log.info("************* 方法二结束 *************");
}
========================打印结果:
17:42:03.357 处理数据集:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
17:42:03.368 处理数据集:[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
17:42:03.368 处理数据集:[20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
17:42:03.368 处理数据集:[30, 31, 32, 33, 34, 35, 36, 37, 38, 39]
17:42:03.368 处理数据集:[40, 41, 42, 43, 44, 45, 46, 47, 48, 49]
17:42:03.368 处理数据集:[50, 51, 52, 53, 54, 55, 56, 57, 58, 59]
17:42:03.368 处理数据集:[60, 61, 62, 63, 64, 65, 66, 67, 68, 69]
17:42:03.368 处理数据集:[70, 71, 72, 73, 74]
17:42:03.368 ************* 方法一结束 *************
17:42:03.368 待处理数据总条数:75,总批次数:8
17:42:03.369 处理第1批次:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
17:42:03.369 处理第2批次:[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
17:42:03.369 处理第3批次:[20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
17:42:03.369 处理第4批次:[30, 31, 32, 33, 34, 35, 36, 37, 38, 39]
17:42:03.369 处理第5批次:[40, 41, 42, 43, 44, 45, 46, 47, 48, 49]
17:42:03.369 处理第6批次:[50, 51, 52, 53, 54, 55, 56, 57, 58, 59]
17:42:03.369 处理第7批次:[60, 61, 62, 63, 64, 65, 66, 67, 68, 69]
17:42:03.369 处理第8批次:[70, 71, 72, 73, 74]
17:42:03.369 ************* 方法二结束 *************
这样每一波分批待处理的数据集合,以及位置你都能get到了,也就可以进行你的业务逻辑操作了,入库或者开线程了。
三、分页查询下游批次处理场景
如果是分页查询下游或者其他接口循环处理场景:
//每次导出或者查询下游接口等分页页大小(10页一查)
public final static int PERSIZE = 10;
//查询第一次数据(返回的分页信息page中包含总页数、总条数)
Page<OrderInfoDTO> firstBOList = orderRepository.queryPrescriptionByOrderId(param);
//处理第一页查询回来的数据
if (CollectionUtil.isNotEmpty(firstBOList)) {
log.info("处理数据{}条 ", firstBOList.size());
}
//页数大于1继续查询
int totalPages=firstBOList.getTotalPage();
if (totalPages > 1) {
for (int pageNum = 2; pageNum <= totalPages; pageNum++) {
query.setPageSize(PERSIZE);
query.setPageNo(pageNum);
Page<OrderInfoDTO> list = orderRepository.queryPrescriptionByOrderId(param);
//todo 处理结果集
log.info("处理第{}批次数据{}条 ", pageNum, list.size());
}
}
四、MyBatis的批量插入
当我们有很多数据需要通过数据库操作,例如很多数据的更新或者插入, 如果用for循环的方式,里面一条条数据去调数据库操作,我们知道那样开销是很大的,会频繁的创建链接,带来性能问题,为了减少连接,我们通常会批量操作。
批量插入代码样例,已mybatis的批量插入活动表为例:
1、活动表简单表结构:
CREATE TABLE `activity` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增id',
`activity_id` bigint(20) DEFAULT NULL COMMENT '活动id',
`user_id` bigint(20) DEFAULT NULL COMMENT '用户id',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT DEFAULT CHARSET=utf8 COMMENT='活动表';
2、业务层组装数据:
ActivityServiceImpl.java:
private void saveRecords() {
List<Activity> recordList = new ArrayList<>();
Activity activity = new Activity();
activity.setUserId(userId);
activity.setActivityId(activityId);
recordList.add(activity);
//此处根据业务需求具体组装待处理集合list
activityDao.insertBatchActivity(recordList);
}
activityDao.xml中写法:
activityDao.xml:
<!-- 批量插入活动表-->
<insert id="insertBatchActivity">
INSERT INTO activity
(
activity_id, user_id, create_time
)
VALUES
<foreach collection="list" item="item" separator=",">
(
#{item.activityId},
#{item.userId},
now()
)
</foreach>
</insert>
特别注意:mysql默认接受sql的大小是1048576(1M),即第三种方式若数据量超过1M会报如下异常:(可通过调整MySQL安装目录下的my.ini文件中[mysqld]段的"max_allowed_packet = 1M")
标签:待处理,int,分批,处理,线程,activity,MyBatis,数据,id From: https://www.cnblogs.com/codeLearn/p/17994880