1. 从一次慢到怀疑人生的批量插入说起
前几天,我团队里一个刚工作不久的小伙伴跑来找我,一脸愁容。他说自己负责的一个数据同步功能,每次要往数据库里写一万条记录,跑一次要将近十秒钟,用户那边已经投诉好几次了。他信誓旦旦地说:“哥,我用了MyBatis-Plus的 saveBatch 方法,不是说这是批量插入吗?怎么还这么慢?” 我让他把代码和配置发我看看,果然,问题就出在这个“想当然”的批量操作上。这其实是一个很多使用MyBatis-Plus的开发者都会踩的坑,包括一些有几年经验的朋友也可能中招。你以为调用了 saveBatch,数据就真的“批量”飞进数据库了?真相可能让你大跌眼镜。
简单来说,MyBatis-Plus 的 saveBatch 方法,在默认情况下,并没有实现我们想象中的那种“批量SQL执行”。它底层走的还是JDBC的 PreparedStatement 的 addBatch 和 executeBatch 这套流程。但是,关键来了!MySQL的JDBC驱动(就是那个连接Java和MySQL的桥梁)在默认行为下,会“好心办坏事”。它表面上接收了你批量执行的指令,背地里却把这一批SQL语句拆成一条一条,再逐条发给MySQL服务器执行。你可以想象一下,本来一辆大卡车一次能运1000箱货(批量),现在非要拆成1000辆小摩托,一辆一辆地运(单条),这效率能不低吗?这也就是为什么你感觉用了批量方法,速度却跟用for循环一条条insert没太大区别的原因。
那么,真正的“救世主”是谁呢?就是今天我们要深入剖析的 rewriteBatchedStatements 这个JDBC连接参数。把它设置为 true,就像是给JDBC驱动下了一道“军令”:“别拆了,给我原样打包,整批发送!” 驱动就会把多条INSERT语句重写成一个多值列表的INSERT语句,或者通过其他优化手段,实现真正的批量提交,性能提升往往是数倍甚至数十倍。接下来,我们就一层层剥开它的神秘面纱。
2. rewriteBatchedStatements:它到底是什么,又如何工作?
光知道这个参数能提速还不够,我们得明白它的工作原理,这样以后遇到类似问题才能举一反三。rewriteBatchedStatements 是MySQL Connector/J(也就是MySQL的官方JDBC驱动)提供的一个非常重要的连接属性。
它的核心作用,是让JDBC驱动重写(Rewrite)批量(Batched)的语句(Statements)。当这个参数设置为 true 时,驱动会尝试对通过 addBatch() 方法添加的SQL语句进行优化重写。
具体是怎么重写的呢?主要有两种方式,我们以最常用的INSERT为例:
第一种,也是最经典的方式:合并多条INSERT语句。 假设我们通过 saveBatch 要插入3条记录,默认情况下,JDBC驱动会生成3条独立的SQL发给MySQL:
INSERT INTO user (name, age) VALUES ('张三', 20);
INSERT INTO user (name, age) VALUES ('李四', 25);
INSERT INTO user (name, age) VALUES ('王五', 30);
当开启 rewriteBatchedStatements=true 后,JDBC驱动会在客户端(也就是你的Java应用这一侧)将这些语句重写为一条:
INSERT INTO user (name, age) VALUES ('张三', 20), ('李四', 25), ('王五', 30);
这一下子,网络通信的次数从3次减少到了1次,MySQL服务器解析和执行SQL的开销也大大降低。对于大批量数据,这种合并带来的性能收益是指数级增长的。
第二种方式:基于预编译语句的优化。 对于使用 PreparedStat



被折叠的 条评论
为什么被折叠?



