批量执行&rewriteBatchedStatements=true深入理解、深度分析。通过查询mysql日志深入理解

导读:本篇文章讲解 批量执行&rewriteBatchedStatements=true深入理解、深度分析。通过查询mysql日志深入理解,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

网上的案例就一篇文章,也不知道谁抄谁的
https://www.codeleading.com/article/6048363902/
https://blog.csdn.net/qq_38361800/article/details/102697824
https://www.jianshu.com/p/0f4b7bc4d22c
要不就是解释不清楚
https://www.cnblogs.com/ad-zhou/p/9092941.html
http://events.jianshu.io/p/1eeb517c6ce7

最后还是自己写吧,先看这篇文章
首先了解一下我们的每一个动作mysql的sql是如何执行的

这篇文章没有分析源码,因为源码的底层也是数据库的日志为最终结果
由于我是用生产数据来做的测试,为了保密,以下字段和数据均做处理,但结果保证真实

URL的参数
url: jdbc:mysql://127.0.0.1:3306/photo?rewriteBatchedStatements=true

批量插入分析

一条insert语句后面拼接的参数个数1000个效率为最佳。
比如一万条数据,如果用一个insert语句后面加一万个参数的速度没有10条insert每条1000个参数快
但是如果一条insert后面的参数低于1000,效率也会慢,因为要提交至少大于一次的事务
就好比抛物线,他有一个峰值,两遍都是降低

url不加rewriteBatchedStatements=true参数的分析如下

1 jdbc的,只分析prepareStatement的

executeBatch的案例

String sql = "insert into table (name, age, sex) values (?, ?, ?)"
Connection conn = commonJdbcServiceImpl.getConn();
PreparedStatement ps = conn.prepareStatement(sql);
for (int i = 0; i < 2005; i++) {
     ps.setString(1, "3121022636620626");
     ps.setString(2, "2021-01-30 00:00:00.0");
     ps.setString(3, "31a0007");
     ps.addBatch();
}
ps.executeBatch();

有些杠精会说我可以再values后面自己拼接多个参数。我只想说,有的预编译不用,那就真的太愚蠢了

idea没有打印日志

下面是mysql日志,没有手动提交事务,我的测试数据2005条

2022-01-19T03:17:17.765251Z	249020 Query	insert into table (name, age, sex) values ('小李','18','男')
2022-01-19T03:17:17.800911Z	249020 Query	insert into table (name, age, sex) values ('小李','18','男')
2022-01-19T03:17:17.837039Z	249020 Query	insert into table (name, age, sex) values ('小李','18','男')
......一共有2005insert
2 mybatis的foreach拼接sql语句,例如

list里面有2005条数数据

aaaMapper.saveBatch(list);

日志如下

insert into table (name, age, sex) values (?, ?, ?),(?, ?, ?),(?, ?, ?)......

一条insert2005(?, ?, ?),然后一堆参数去填充
<==    Updates: 2005

看mysql日志,就一条insert,后面2005个小括号,没有手动提交事务

2022-01-18T07:36:48.366482Z	248874 Query	select @@session.tx_read_only
2022-01-18T07:36:48.428956Z	248874 Query	insert into table (name, age, sex) values ('小李','18','男'),('小李','18','男'),('小李','18','男')...... 

2022-01-18T07:38:18.364000Z	248875 Quit	
3 mybatis-plus的saveBatch方法
aaaService.saveBatch(list);

先看mybatis-plus的日志

类似于下面这种

INSERT INTO table (name, age, sex) VALUES ( ?, ?, ?)
然后有1000行参数(==> Parameters: )去填充,也就是1000insert

看mysql的日志

2022-01-18T07:09:46.461726Z	248864 Query	SET autocommit=0
2022-01-18T07:09:46.960218Z	248864 Query	select @@session.tx_read_only
2022-01-18T07:09:46.960934Z	248864 Query	INSERT INTO table (name, age, sex) VALUES  ('小李','18','男')
2022-01-18T07:09:46.960934Z	248864 Query	INSERT INTO table (name, age, sex) VALUES  ('小李','18','男')
......
2022-01-18T07:09:47.921155Z	248864 Query	select @@session.tx_read_only
2022-01-18T07:09:47.922676Z	248864 Query	INSERT INTO table (name, age, sex) VALUES  ('小李','18','男')
2022-01-18T07:09:47.922676Z	248864 Query	INSERT INTO table (name, age, sex) VALUES  ('小李','18','男')
......

2022-01-18T07:09:47.923745Z	248864 Query	commit
2022-01-18T07:09:48.071948Z	248864 Query	SET autocommit=1

自动提交改为0
然后多行insert语句
最后提交事务

加上rewriteBatchedStatements=true后

1 jdbc的

代码和上面一样

mysql日志。会帮我们组装成一条insert,values 后面一共2005个小括号。不会分批,但也比多个insert强

2022-01-19T02:58:41.895906Z	249010 Query	select @@session.tx_read_only
2022-01-19T02:58:41.901423Z	249010 Query	insert into table (name, age, sex) values ('小李','18','男'),('小李','18','男'),('小李','18','男')...... 

2022-01-19T02:58:42.161162Z	249004 Quit	
2 mybatis的foreach拼接sql语句

mybatis的日志和上面的一样

mysql日志也和上面的一样

3 mybatis-plus的saveBatch方法

mybatis-plus的日志和上面的一样

再看mysql日志

2022-01-18T06:41:47.986185Z	248859 Query	SET autocommit=0
2022-01-18T06:41:59.640821Z	248859 Query	select @@session.tx_read_only
2022-01-18T06:41:59.975773Z	248859 Query	INSERT INTO table (name, age, sex) VALUES  ('小李','18','男'), ('小李','18','男').......

2022-01-18T06:42:12.055502Z	248859 Query	select @@session.tx_read_only
2022-01-18T06:42:12.323399Z	248859 Query	INSERT INTO table (name, age, sex) VALUES  ('小李','18','男'), ('小李','18','男').......

2022-01-18T06:42:13.231933Z	248859 Query	select @@session.tx_read_only
2022-01-18T06:42:13.234253Z	248859 Query	INSERT INTO table (name, age, sex) VALUES  ('小李','18','男'), ('小李','18','男').......

2022-01-18T06:42:13.236109Z	248859 Query	commit
2022-01-18T06:42:13.373313Z	248859 Query	SET autocommit=1

它是先将自动提交改为0
然后将1000条数据组成一批(一个insert语句)来执行
最后提交事务,autocommit改为1

批量更新、删除分析

jdbc的批量更新和删除其实也是executeBatch

foreach的话需要开启多查询(&allowMultiQueries=true),才能拼接多条update语句

mybats-plus里面的批量更新其实是一条一条来更新的

优化最好可以把数据控制再1000条提交一次事务

总结

批量插入,从事务方面总结

url的参数 jdbc foreach拼接 mybatis-plus
不加 一条insert一条参数 一条insert多条参数(建议1000条参数) 每条insert有1条参数,只提交一次事务
一条insert多条参数(建议1000条参数) 一条insert多条参数(建议1000条参数) 每条insert有1000条参数,只提交一次事务

效率总结
数据库和代码都是我本地,基础配置如下
处理器 i7-10510U CPU 1.80GHz
内存16.0 GB
机械硬盘HDD 转速5400转 数据传输速率600M/秒

表有30个字段,每次测试都会清空数据,一条insert按1000条参数来测试

数量/大小 jdbc不加参数 jdbc加 foreach加不加一样 mybatis-plus不加 mybatis-plus加
1万/3.53MB 420秒 2秒 4秒 5秒 5秒
10万/32.58MB 没测 17秒 30秒 35秒 20秒
100万/276.84MB 没测 266秒 455秒 330秒 180秒

实际写入速度请以自己电脑或服务器配置为准。我这个测试没有网络消耗,正常生产环境是有网络消耗的

一万以下
jdbc不加 < mybatis-plus不加 < mybatis-plus加 < foreach < jdbc加

一万到十万
jdbc不加 < mybatis-plus不加 < foreach < mybatis-plus加 < jdbc加

十万到-一百万
jdbc不加 < mybatis-plus不加 < jdbc加 < foreach < mybatis-plus加

mybatis-plus加参数比其他情况还是有所提升的

过了好几个月补的

为了贴近实际,测试了一个有网络消耗的

数据库在内网服务器
java代码再我本地,我连上WiFi来连接数据库

50个字段

数量/大小 foreach加不加一样 mybatis-plus不加 mybatis-plus加
1千/0.33MB 1秒 15秒 0.8秒
1万/3.53MB 4秒 150秒 5秒

mybatis-plus不加 比 foreach |mybatis-plus加 慢15到30倍

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/116383.html

(0)
Java光头强的头像Java光头强

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!