多值insert需控制单条语句在1–2mb(约1000–2000行),避免触发max_allowed_packet错误;应按字节数估算长度,使用list拼接而非字符串累加,并配合事务分批提交(每批1000–5000行)以平衡性能与稳定性。

多值 INSERT 语句怎么写才不触发 max_allowed_packet?
单条 INSERT INTO t(a,b) VALUES (...),(...),... 超长会直接报错 Packet too large,不是性能差,是根本执行不了。关键不是“尽量多塞”,而是“稳住长度”。
- MySQL 默认
max_allowed_packet=64MB,但实际建议单条语句控制在 1–2MB 内(约 1000–2000 行),避免解析延迟和内存抖动 - 每行数据长度差异大时(比如含 TEXT 字段),按字节数估算更准:用
LENGTH(CONCAT(...))模拟预估,别只数行数 - 拼接时别用字符串累加(如 Python 的
+=),改用list.append()+','.join(),避免中间对象反复拷贝 - JDBC 场景下,即使用了
addBatch(),若驱动没开rewriteBatchedStatements=true,仍会退化成 N 条单语句发送
事务提交批次设多少才不锁表又不崩 undo log?
设 1000 行一批不是玄学,而是平衡日志刷盘频率与事务持有时间的结果。太小(如 100 行)→ commit 太勤,WAL 写放大;太大(如 10 万行)→ undo 表空间撑爆、主键冲突回滚代价高、其他会话长时间等锁。
- MySQL InnoDB 下,推荐每批 1000–5000 行,具体看单行平均大小:若平均 2KB,5000 行 ≈ 10MB,仍在安全阈值内
- 高并发写入场景(如订单入库),把批次降到 200–500 行,减少 gap lock 范围,避免
SELECT ... FOR UPDATE或唯一索引冲突卡死 - PostgreSQL 注意
work_mem设置,大批量INSERT ... ON CONFLICT可能触发 hash join 内存溢出,需同步调大 - SQL Server 建议加
TABLOCK提示,让批量插入走最小日志模式,否则默认 full logging 吞吐直降 3–5 倍
禁用索引后为什么导入完要重建而不是 ENABLE KEYS?
ALTER TABLE t DISABLE KEYS 只对 MyISAM 有效,InnoDB 下无效——这是最容易踩的坑。InnoDB 没有 “KEYS” 概念,禁用的是唯一约束检查,不是索引本身。
- MyISAM:可用
DISABLE KEYS+ENABLE KEYS,后者会一次性 B-tree 排序建索引,比逐行插入快 5–10 倍 - InnoDB:必须
DROP INDEX手动删二级索引,导入后再CREATE INDEX;主键聚簇索引无法删除,只能接受逐行维护 - PostgreSQL:临时
DROP INDEX更稳妥,SET CONSTRAINTS ALL DEFERRED只推迟检查,不跳过索引更新 - 所有数据库禁用约束后,务必做最终校验:
SELECT COUNT(*) FROM t+SELECT COUNT(DISTINCT key_col) FROM t,防止脏数据漏检
COPY / LOAD DATA 比 INSERT 快在哪?
快在绕过了 SQL 解析器和查询优化器,数据直接进存储引擎缓冲区。不是“语法糖”,是协议层切换。
- PostgreSQL
COPY t FROM STDIN:客户端把二进制/文本流直推 wal_writer,跳过 parser → planner → executor 全链路,实测快 8–15 倍 - MySQL
LOAD DATA INFILE:服务端读文件走 bulk insert path,不走 SQL 接口,但要求文件在数据库服务器本地,且受secure_file_priv限制 - JDBC 场景下,
copyManager.copyIn()(pgjdbc)或LoadDataStatement(mysql-connector-java)才能真正启用底层协议,普通executeBatch()还是走 SQL - 注意字符集转换:
COPY默认按数据库编码读,CSV 中含 emoji 或 GBK 数据时,必须显式指定ENCODING 'UTF8',否则乱码且不报错
DISABLE KEYS 对 InnoDB 无效,或者误以为 executeBatch() 自动启用 rewriteBatchedStatements。这些细节不验证,调再多次参数也没用。











