事务中批量insert易出错,因单条数据违反约束(如主键重复)会导致整批回滚;autocommit=1时更使begin...commit失效;on duplicate key update可容错更新,但需唯一约束;分批+异常捕获可精确定位失败行;max_allowed_packet超限会直接报错。

事务中批量INSERT为什么容易出错
直接用 INSERT INTO ... VALUES (...), (...), (...) 一次性插几百条,看似高效,但一旦中间某条数据违反约束(比如重复主键、外键不匹配、字段超长),整个事务会回滚——你得重试全部,而不是只跳过那条坏数据。更麻烦的是,MySQL默认的 autocommit=1 下,每条 INSERT 都是独立事务,根本没进你写的 BEGIN...COMMIT 里。
用 INSERT ... ON DUPLICATE KEY UPDATE 替代单纯批量插入
这是 MySQL 最实用的“带容错的批量写入”方案,前提是表有 PRIMARY KEY 或 UNIQUE 约束。它不会因冲突中断,而是自动转为更新操作:
INSERT INTO users (id, name, email) VALUES (1, 'Alice', 'a@x.com'), (2, 'Bob', 'b@x.com'), (3, 'Charlie', 'c@x.com') ON DUPLICATE KEY UPDATE name = VALUES(name), email = VALUES(email);
注意点:
-
VALUES(col)引用的是当前这一行 VALUES 子句中的值,不是整张表的值 - 只对触发唯一键冲突的行生效,其他行照常插入
- 不能用于无唯一约束的表;PostgreSQL 要用
ON CONFLICT DO UPDATE
分批提交 + 显式错误捕获(Python + pymysql 示例)
当业务逻辑不允许“冲突即更新”,而必须严格区分插入/跳过/报错时,靠 SQL 自身已不够,得在应用层控制:
关键不是“一次塞多少条”,而是“每批失败后能否定位到具体哪条”:
- 把 1000 条数据拆成每批 100 条,用
executemany()执行 - 捕获
pymysql.IntegrityError,再用cursor.rowcount和原始数据索引反推失败位置 - 避免在循环里逐条
execute()—— 网络往返和事务开销会拖慢 5 倍以上 - 记得设
connection.autocommit = False,否则BEGIN没意义
别忽略事务隔离级别对批量INSERT的影响
在 REPEATABLE READ(MySQL 默认)下,并发执行相同批量插入时,可能触发间隙锁(gap lock),导致死锁或阻塞。尤其当你用 SELECT ... FOR UPDATE 预检数据再插入时:
- 如果只是插入新记录,且主键/唯一键递增,
READ COMMITTED更轻量 - 批量插入前加
SELECT ... LOCK IN SHARE MODE是常见误用——它锁住范围,反而加剧争用 - 真正需要一致性校验的场景(如防重复下单),优先用唯一索引 +
ON DUPLICATE KEY,比手写锁逻辑更可靠
最易被忽略的一点:批量 INSERT 的性能瓶颈往往不在 SQL 本身,而在客户端内存堆积和网络包大小限制。MySQL 默认 max_allowed_packet=4MB,超长批量语句会直接被截断报错 Packets larger than max_allowed_packet are not allowed——调这个参数比优化 SQL 更快见效。











