Mybatis与JDBC批量插入MySQL数据库性能测试
Author:boonya
Date:2017-04-13
系统中需要批量生成单据数据到数据库表,所以采用批量插入数据库的方式。由于系统中ORM操作集成使用的是Mybatis来完成的。
在Mybatis中操作一般使用批量插入的方式如下:
<insert id="insertBatch" parameterType="java.util.List" >
insert into userinfo (uid, uname, uphone, uaddress)
values
<foreach collection="list" item="item"index="index" separator=",">
(#{item.uid,jdbcType=INTEGER},
#{item.uname,jdbcType=VARCHAR},
#{item.uphone,jdbcType=VARCHAR},
#{item.uaddress,jdbcType=VARCHAR}
)
</foreach>
</insert>
在实际生产中发现,这样调用的效率并不高,于是我们迫切地需要寻找一种处理批量插入性能较高的方式——回归原生数据库JDBC操作。我们要分析Mybatis和JDBC的插入性能,来决策适合我们生产系统的批量插入方式。
参考资料中有一篇文章是对Spring Mybatis和Spring JDBC插入效率的测试,由于测试的数据量不多不予置评。
测试10组数据:
时间(ms) |
923 |
412 |
426 |
408 |
405 |
353 |
365 |
344 |
316 |
493 |
测试10组数据:
时间(ms) |
11031 |
3340 |
3571 |
2327 |
7273 |
1353 |
2676 |
1249 |
1245 |
1155 |
测试10组数据:
时间(ms) |
6070 |
5565 |
5731 |
5400 |
5830 |
5543 |
5469 |
5697 |
5528 |
5399 |
测试10组数据:
时间(ms) |
13383 |
12672 |
13030 |
13484 |
13841 |
12952 |
13331 |
13275 |
13000 |
13236 |
测试10组数据:
时间(ms) |
25312 |
24702 |
27065 |
25921 |
25156 |
24686 |
25314 |
33947 |
25304 |
25853 |
测试10组数据:
时间(ms) |
42148 |
39209 |
38548 |
40109 |
37820 |
37728 |
38178 |
38481 |
38157 |
39032 |
测试10组数据:
时间(ms) |
98250 |
88585 |
87438 |
89547 |
88427 |
89522 |
83261 |
80842 |
87163 |
84804 |
测试10组数据:
时间(ms) |
145481 |
146618 |
147098 |
145578 |
144947 |
145614 |
142014 |
142315 |
141984 |
143625 |
-- DELETE FROMprocess_spend_time_result WHEREpmethod=‘mybatis‘;
SELECTAVG(p.ptime) FROMprocess_spend_time_result p WHERE p.plimit=1000 AND p.pmethod=‘mybatis‘;
SELECTAVG(p.ptime) FROMprocess_spend_time_result p WHERE p.plimit=2000 AND p.pmethod=‘mybatis‘;
SELECTAVG(p.ptime) FROMprocess_spend_time_result p WHERE p.plimit=4000 AND p.pmethod=‘mybatis‘;
SELECTAVG(p.ptime) FROMprocess_spend_time_result p WHERE p.plimit=6000 AND p.pmethod=‘mybatis‘;
SELECTAVG(p.ptime) FROMprocess_spend_time_result p WHERE p.plimit=8000 AND p.pmethod=‘mybatis‘;
SELECTAVG(p.ptime) FROMprocess_spend_time_result p WHERE p.plimit=10000 AND p.pmethod=‘mybatis‘;
SELECTAVG(p.ptime) FROMprocess_spend_time_result p WHERE p.plimit=15000 AND p.pmethod=‘mybatis‘;
SELECTAVG(p.ptime) FROMprocess_spend_time_result p WHERE p.plimit=20000 AND p.pmethod=‘mybatis‘;
SELECTAVG(p.ptime)/1000 FROMprocess_spend_time_result p WHERE p.plimit=1000 AND p.pmethod=‘mybatis‘;
SELECTAVG(p.ptime)/1000 FROMprocess_spend_time_result p WHERE p.plimit=2000 AND p.pmethod=‘mybatis‘;
SELECTAVG(p.ptime)/1000 FROMprocess_spend_time_result p WHERE p.plimit=4000 AND p.pmethod=‘mybatis‘;
SELECTAVG(p.ptime)/1000 FROMprocess_spend_time_result p WHERE p.plimit=6000 AND p.pmethod=‘mybatis‘;
SELECTAVG(p.ptime)/1000 FROMprocess_spend_time_result p WHERE p.plimit=8000 AND p.pmethod=‘mybatis‘;
SELECTAVG(p.ptime)/1000 FROMprocess_spend_time_result p WHERE p.plimit=10000 AND p.pmethod=‘mybatis‘;
SELECTAVG(p.ptime)/1000 FROMprocess_spend_time_result p WHERE p.plimit=15000 AND p.pmethod=‘mybatis‘;
SELECTAVG(p.ptime)/1000 FROMprocess_spend_time_result p WHERE p.plimit=20000 AND p.pmethod=‘mybatis‘;
数据4舍5入保留3位小数
测试样例 | AVG(s) |
1K | 0.445 |
2K | 3.522 |
4K | 5.623 |
6K | 13.221 |
8K | 26.326 |
10K | 38.941 |
15K | 87.784 |
20K | 144.527 |
处理10000条耗时:34292ms
Windows下需要修改MySQL的my.ini文件加入如下配置内容:
max_allowed_packet=500M
Linux下是在/my.cnf修改添加如上内容。
也可以直接这样设置:
SET GLOBAL max_allowed_packet=1073741824;
但MySQL重启后就不起作用了。
在通过多线程处理的时,很容易导致数据库表锁表,使得后续的操作无法进行。
对象超出GC对象回收阀值,导致程序中断。
测试10组数据:
普通插入(ms) | 普通+事务(ms) | 普通批量(ms) | 批量+事务(MS) |
28489 | 801 | 31287 | 3494 |
30536 | 3042 | 35547 | 1899 |
25571 | 2041 | 31022 | 3501 |
27954 | 2733 | 28927 | 2547 |
29620 | 1261 | 34408 | 1449 |
27125 | 819 | 29318 | 923 |
28993 | 1079 | 31099 | 939 |
27594 | 2547 | 33504 | 3410 |
27967 | 781 | 31646 | 3587 |
33145 | 1293 | 37030 | 1912 |
测试10组数据:
普通插入(ms) | 普通+事务(ms) | 普通批量(ms) | 批量+事务(MS) |
131427 | 11568 | 168623 | 6926 |
132271 | 19313 | 231526 | 9915 |
192176 | 5238 | 227724 | 10978 |
185640 | 18955 | 227497 | 41959 |
211777 | 11238 | 184970 | 9461 |
208446 | 5019 | 263636 | 23394 |
253351 | 14265 | 227391 | 24870 |
225268 | 17009 | 229871 | 5583 |
163739 | 9719 | 230719 | 16657 |
215033 | 15802 | 238018 | 5330 |
测试10组数据:
普通插入(ms) | 普通+事务(ms) | 普通批量(ms) | 批量+事务(MS) |
308773 | 21389 | 360510 | 16432 |
352773 | 23487 | 372343 | 25545 |
378805 | 24034 | 368416 | 12507 |
384189 | 30119 | 392974 | 23742 |
369975 | 30651 | 378634 | 26180 |
368659 | 11902 | 416932 | 21321 |
388453 | 12644 | 411571 | 18138 |
391155 | 11287 | 396363 | 11678 |
368055 | 30987 | 399078 | 12212 |
363375 | 22576 | 361478 | 18544 |
测试10组数据:
普通插入(ms) | 普通+事务(ms) | 普通批量(ms) | 批量+事务(MS) |
942067 | 51343 | 990800 | 70103 |
1070688 | 28737 | 1051132 | 35536 |
1002076 | 38065 | 1222409 | 89644 |
1073114 | 57050 | 1312620 | 82354 |
960697 | 51733 | 1338932 | 33428 |
1025890 | 37666 | 1273338 | 76934 |
1017361 | 50916 | 1115627 | 92790 |
1077821 | 78650 | 1175512 | 52427 |
1038000 | 23290 | 1247797 | 91801 |
1200532 | 75494 | 1262051 | 72087 |
测试10组数据:
普通插入(ms) | 普通+事务(ms) | 普通批量(ms) | 批量+事务(MS) |
1914920 | 166575 | 2059826 | 146472 |
2111596 | 62807 | 1897888 | 125075 |
2174029 | 147265 | 1891542 | 166921 |
1948838 | 61284 | 2129791 | 93167 |
1909861 | 167575 | 1856811 | 56286 |
1990816 | 141381 | 1980060 | 148012 |
1896793 | 48087 | 2065937 | 56832 |
2130856 | 174388 | 2019914 | 113289 |
2073636 | 117462 | 2045715 | 102792 |
1966828 | 141319 | 1857867 | 116854 |
SELECTAVG(p.ptime) FROMprocess_spend_time_result p WHERE p.plimit=10000 and p.pmethod=‘batchInsert‘;
SELECTAVG(p.ptime) FROMprocess_spend_time_result p WHERE p.plimit=10000 and p.pmethod=‘batchInsert2‘;
SELECTAVG(p.ptime) FROMprocess_spend_time_result p WHERE p.plimit=10000 and p.pmethod=‘batchInsertWithTransaction‘;
SELECTAVG(p.ptime) FROMprocess_spend_time_result p WHERE p.plimit=10000 and p.pmethod=‘batchInsertWithTransaction2‘;
SELECTAVG(p.ptime) FROMprocess_spend_time_result p WHERE p.plimit=50000 and p.pmethod=‘batchInsert‘;
SELECTAVG(p.ptime) FROMprocess_spend_time_result p WHERE p.plimit=50000 and p.pmethod=‘batchInsert2‘;
SELECTAVG(p.ptime) FROMprocess_spend_time_result p WHERE p.plimit=50000 and p.pmethod=‘batchInsertWithTransaction‘;
SELECTAVG(p.ptime) FROMprocess_spend_time_result p WHERE p.plimit=50000 and p.pmethod=‘batchInsertWithTransaction2‘;
SELECTAVG(p.ptime) FROMprocess_spend_time_result p WHERE p.plimit=100000 and p.pmethod=‘batchInsert‘;
SELECTAVG(p.ptime) FROMprocess_spend_time_result p WHERE p.plimit=100000 and p.pmethod=‘batchInsert2‘;
SELECTAVG(p.ptime) FROMprocess_spend_time_result p WHERE p.plimit=100000 and p.pmethod=‘batchInsertWithTransaction‘;
SELECTAVG(p.ptime) FROMprocess_spend_time_result p WHERE p.plimit=100000 and p.pmethod=‘batchInsertWithTransaction2‘;
SELECTAVG(p.ptime) FROMprocess_spend_time_result p WHERE p.plimit=250000 and p.pmethod=‘batchInsert‘;
SELECTAVG(p.ptime) FROMprocess_spend_time_result p WHERE p.plimit=250000 and p.pmethod=‘batchInsert2‘;
SELECTAVG(p.ptime) FROMprocess_spend_time_result p WHERE p.plimit=250000 and p.pmethod=‘batchInsertWithTransaction‘;
SELECTAVG(p.ptime) FROMprocess_spend_time_result p WHERE p.plimit=250000 and p.pmethod=‘batchInsertWithTransaction2‘;
SELECTAVG(p.ptime) FROM process_spend_time_resultp WHEREp.plimit=500000 andp.pmethod=‘batchInsert‘;
SELECTAVG(p.ptime) FROMprocess_spend_time_result p WHERE p.plimit=500000 and p.pmethod=‘batchInsert2‘;
SELECTAVG(p.ptime) FROMprocess_spend_time_result p WHERE p.plimit=500000 and p.pmethod=‘batchInsertWithTransaction‘;
SELECTAVG(p.ptime) FROMprocess_spend_time_result p WHERE p.plimit=500000 and p.pmethod=‘batchInsertWithTransaction2‘;
SELECTAVG(p.ptime)/1000 FROMprocess_spend_time_result p WHERE p.plimit=10000 and p.pmethod=‘batchInsert‘;
SELECTAVG(p.ptime)/1000 FROMprocess_spend_time_result p WHERE p.plimit=10000 and p.pmethod=‘batchInsert2‘;
SELECTAVG(p.ptime)/1000 FROMprocess_spend_time_result p WHERE p.plimit=10000 and p.pmethod=‘batchInsertWithTransaction‘;
SELECTAVG(p.ptime)/1000 FROMprocess_spend_time_result p WHERE p.plimit=10000 and p.pmethod=‘batchInsertWithTransaction2‘;
SELECTAVG(p.ptime)/1000 FROMprocess_spend_time_result p WHERE p.plimit=50000 and p.pmethod=‘batchInsert‘;
SELECTAVG(p.ptime)/1000 FROMprocess_spend_time_result p WHERE p.plimit=50000 and p.pmethod=‘batchInsert2‘;
SELECTAVG(p.ptime)/1000 FROMprocess_spend_time_result p WHERE p.plimit=50000 and p.pmethod=‘batchInsertWithTransaction‘;
SELECTAVG(p.ptime)/1000 FROM process_spend_time_resultp WHEREp.plimit=50000 andp.pmethod=‘batchInsertWithTransaction2‘;
SELECTAVG(p.ptime)/1000 FROMprocess_spend_time_result p WHERE p.plimit=100000 and p.pmethod=‘batchInsert‘;
SELECTAVG(p.ptime)/1000 FROMprocess_spend_time_result p WHERE p.plimit=100000 and p.pmethod=‘batchInsert2‘;
SELECTAVG(p.ptime)/1000 FROMprocess_spend_time_result p WHERE p.plimit=100000 and p.pmethod=‘batchInsertWithTransaction‘;
SELECTAVG(p.ptime)/1000 FROMprocess_spend_time_result p WHERE p.plimit=100000 and p.pmethod=‘batchInsertWithTransaction2‘;
SELECTAVG(p.ptime)/1000 FROMprocess_spend_time_result p WHERE p.plimit=250000 and p.pmethod=‘batchInsert‘;
SELECTAVG(p.ptime)/1000 FROMprocess_spend_time_result p WHERE p.plimit=250000 and p.pmethod=‘batchInsert2‘;
SELECTAVG(p.ptime)/1000 FROMprocess_spend_time_result p WHERE p.plimit=250000 and p.pmethod=‘batchInsertWithTransaction‘;
SELECTAVG(p.ptime)/1000 FROMprocess_spend_time_result p WHERE p.plimit=250000 and p.pmethod=‘batchInsertWithTransaction2‘;
SELECTAVG(p.ptime)/1000 FROMprocess_spend_time_result p WHERE p.plimit=500000 and p.pmethod=‘batchInsert‘;
SELECTAVG(p.ptime)/1000 FROMprocess_spend_time_result p WHERE p.plimit=500000 and p.pmethod=‘batchInsert2‘;
SELECTAVG(p.ptime)/1000 FROMprocess_spend_time_result p WHERE p.plimit=500000 and p.pmethod=‘batchInsertWithTransaction‘;
SELECTAVG(p.ptime)/1000 FROMprocess_spend_time_result p WHERE p.plimit=500000 and p.pmethod=‘batchInsertWithTransaction2‘;
数据4舍5入保留2位小数
测试样例 | 1W(s) | 5W(s) | 10W(s) | 25W(s) | 50W(s) |
普通插入 | 28.70 | 191.91 | 367.42 | 1040.82 | 2011.82 |
普通+事务 | 1.64 | 12.81 | 21.91 | 49.29 | 122.81 |
批量插入 | 32.38 | 223.00 | 385.83 | 1199.02 | 1980.54 |
批量+事务 | 2.37 | 15.51 | 18.63 | 69.71 | 112.57 |
数据4舍5入保留2位小数
测试样例 | 1W(s) | 5W(s) | 10W(s) | 25W(s) | 50W(s) |
普通插入 | 28.70 | 191.91 | 367.42 | 1040.82 | 2011.82 |
普通+事务 | 1.64 | 12.81 | 21.91 | 49.29 | 122.81 |
批量插入 | 32.38 | 223.00 | 385.83 | 1199.02 | 1980.54 |
批量+事务 | 2.37 | 15.51 | 18.63 | 69.71 | 112.57 |
数据4舍5入保留2位小数
测试样例 | 1W(s) | 5W(s) | 10W(s) | 25W(s) | 50W(s) |
普通插入 | 28.70 | 191.91 | 367.42 | 1040.82 | 2011.82 |
普通+事务 | 1.64 | 12.81 | 21.91 | 49.29 | 122.81 |
批量插入 | 32.38 | 223.00 | 385.83 | 1199.02 | 1980.54 |
批量+事务 | 2.37 | 15.51 | 18.63 | 69.71 | 112.57 |
数据4舍5入保留2位小数
测试样例 | 1W(s) | 5W(s) | 10W(s) | 25W(s) | 50W(s) |
普通插入 | 28.70 | 191.91 | 367.42 | 1040.82 | 2011.82 |
普通+事务 | 1.64 | 12.81 | 21.91 | 49.29 | 122.81 |
批量插入 | 32.38 | 223.00 | 385.83 | 1199.02 | 1980.54 |
批量+事务 | 2.37 | 15.51 | 18.63 | 69.71 | 112.57 |
数据4舍5入保留2位小数
测试样例 | 1W(s) | 5W(s) | 10W(s) | 25W(s) | 50W(s) |
普通插入 | 28.70 | 191.91 | 367.42 | 1040.82 | 2011.82 |
普通+事务 | 1.64 | 12.81 | 21.91 | 49.29 | 122.81 |
批量插入 | 32.38 | 223.00 | 385.83 | 1199.02 | 1980.54 |
批量+事务 | 2.37 | 15.51 | 18.63 | 69.71 | 112.57 |
数据4舍5入保留2位小数
测试样例 | 1W(s) | 5W(s) | 10W(s) | 25W(s) | 50W(s) |
普通插入 | 28.70 | 191.91 | 367.42 | 1040.82 | 2011.82 |
普通+事务 | 1.64 | 12.81 | 21.91 | 49.29 | 122.81 |
批量插入 | 32.38 | 223.00 | 385.83 | 1199.02 | 1980.54 |
批量+事务 | 2.37 | 15.51 | 18.63 | 69.71 | 112.57 |
经过以上测试得出结论:Mybatis的批量适合处理少了数据的批量处理,而JDBC适合大数据量的批量处理。据此,采用JDBC批量+事务处理大数据量的表插入操作是最合适的。
因为要考虑JVM的GC所以数据应该限制一下,但鉴于Mybatis大数据量的批量插入效率不高,所以根据数据大小分段治理。
对JVM进行调优,但主要的性能瓶颈在批量插入操作。鉴于mybatis在项目开发方面的优势,数据量很小的情况下还是建议使用Mybatis。
对JVM进行调优(设置Stack和GC等)。一般操作30秒以内是可以容忍的性能耗时。
对JVM进行调优(设置Stack和GC等),通过数据分批处理。对于分批处理需要借鉴前面的测试数据来定义分批量的大小,主要是对操作时间调优。
如果是100W、1000W级别的数据量,分批处理可以很大程度地提升插入效率,具体的分批需要通过实践去分配,数据量太大这里就不做实验了。
JDBC实现往MySQL数据库插入百万数据:http://www.cnblogs.com/fnz0/p/5713102.html
MySQL Max_allowed_packet: http://stackoverflow.com/questions/8062496/how-to-change-max-allowed-packet-size
Spring Mybatis和Spring JDBC的插入效率比较:http://toplchx.iteye.com/blog/1988254
注:另外一种比较高效的导入方式是生成一个文本文件使用MySQL的JDBC LOAD DATA LOCAL INFILE;参考示例:
MySQL使用JDBC LOAD DATA LOCAL INFILE导入注意事项
MySQL使用LOAD DATA LOCAL INFILE数据3-5秒导入40W数据
本文测试文档代码和数据库均已上传:http://download.csdn.net/detail/boonya/9812860
Mybatis与JDBC批量插入MySQL数据库性能测试及解决方案
原文:http://blog.csdn.net/boonya/article/details/70157820