mysql存储过程不支持bulk insert,报错1064;高效方案是concat拼接多值insert(每批≤2000行)、显式事务控制、禁用unique_checks,或改用load data infile。

MySQL 存储过程里直接写 BULK INSERT 会报错 ERROR 1064,SQL Server 才支持;用游标逐行插入百万数据,等于主动触发锁争抢和日志爆炸,实际性能可能比裸循环还差 3–5 倍。真正能落地的方案只有两个:关事务 + 字符串拼接多值 INSERT,或改用 LOAD DATA INFILE(但需文件权限和路径可控)。
MySQL 存储过程里怎么拼接批量 INSERT
不能依赖 PREPARE/EXECUTE 在循环里反复解析 SQL——那反而更慢;也不能用游标 fetch 一行插一行。核心是用 CONCAT 动态拼出大批次 VALUES 字符串,每批控制在 2000 行左右。
- 显式
START TRANSACTION开启事务,结尾COMMIT,禁用自动提交 - 插入前执行
SET UNIQUE_CHECKS = 0,结束后再设回1 -
sql_part变量类型必须为TEXT或MEDIUMTEXT,否则拼接超长会被截断 - 每批拼完立即执行
EXECUTE stmt,别等全部拼完——max_allowed_packet很容易爆
为什么 LIMIT 分批 UPDATE/DELETE 不等于 INSERT 分批
UPDATE ... WHERE id > ? ORDER BY id LIMIT 1000 这类分批逻辑对修改有效,但不适用于 INSERT:INSERT 没有“上一批最后 id”可依赖。你无法靠主键递增来切分待插入的数据源,除非数据本身已按 id 排好序且来源稳定(比如从另一张表 SELECT ... ORDER BY id 出来)。
- 如果插入数据来自外部(如 CSV、API、消息队列),必须在应用层做分片,再传入存储过程
- 若强行在存储过程中模拟“分页插入”,得额外维护一个临时计数器或中间表,增加复杂度和锁风险
- 不要试图用
INSERT ... SELECT ... LIMIT来替代,MySQL 8.0 之前不支持该语法,且LIMIT在子查询中行为不可靠
MyBatis 或 ORM 场景下如何避免超时
不是数据库不行,而是 JDBC 层或框架配置没调对。常见超时根源在连接池和驱动层面,不是 SQL 写得不够“酷”。
- 检查
HikariCP的connection-timeout和max-lifetime,批量操作建议设为 30000ms 以上 - MyBatis 必须用
ExecutorType.BATCH创建SqlSession,否则<foreach></foreach>仍是逐条提交 - 单次
<foreach></foreach>插入不超过 500 条,否则生成的 SQL 超过 2MB,触发max_allowed_packet截断 - Spring 事务注解
@Transactional别套在整批插入方法上——它会让所有数据包进一个事务,应改用编程式事务控制每批边界
最易被忽略的一点:无论用哪种方式分批,都要确认目标表的索引状态。插入前若有唯一索引或全文索引,每批插入都会触发重建开销;临时禁用 UNIQUE_CHECKS 有用,但 FULLTEXT 索引无法跳过,只能提前删掉,插完再建。










