insert慢主因是索引实时更新导致写放大,尤其innodb多二级索引时开销超70%;应删索引再重建、用临时表中转、批量插入、调优buffer_pool和log参数。

INSERT 很慢?先看是不是索引在拖后腿
大量 INSERT 时性能骤降,十有八九是索引在实时更新。每插一行,所有相关索引都要同步维护 B+ 树结构,写放大严重。尤其当表有 3 个以上二级索引、单次插入数万行时,索引维护开销可能占总耗时 70% 以上。
实操建议:
- 对目标表执行
ALTER TABLE table_name DISABLE KEYS(仅 MyISAM 有效);InnoDB 必须用更底层的方式——删索引再重建 - 如果是 InnoDB,直接
DROP INDEX idx_name ON table_name,等数据导入完再CREATE INDEX - 注意:主键和唯一约束不能禁用,
DISABLE KEYS对 InnoDB 完全无效,别白试 - 操作前确保无并发读写,否则会引发不一致或锁表
用临时表中转,绕过原表锁与触发器
直接向业务表大批量插入,容易撞上行锁、间隙锁,还可能被触发器、外键约束反复拉停。临时表本质是隔离层:先灌数据进去,再原子性地 INSERT INTO ... SELECT 搬到目标表,全程避开大部分运行时检查。
实操建议:
- 建临时表用
CREATE TEMPORARY TABLE temp_import LIKE original_table,保留结构但不带索引、约束、触发器 - 往
temp_import插入时加LOAD DATA INFILE或分批次INSERT ... VALUES (...), (...),每批 1000–5000 行较稳 - 最后用
INSERT INTO original_table SELECT * FROM temp_import搬迁;若需去重,加ON DUPLICATE KEY UPDATE或先TRUNCATE原表 - 临时表只在当前会话可见,断连自动销毁,不用手动清理
批量 INSERT 的写法差异直接影响吞吐
INSERT INTO t VALUES (1),(2),(3) 和循环执行三次 INSERT INTO t VALUES (1),性能差距可达 10 倍以上。前者减少网络往返、语句解析、权限校验次数,也降低事务日志刷盘频率。
实操建议:
- 单条
INSERT最多塞 1000 组值,再多可能触发 max_allowed_packet 或解析超时 - 避免在循环里拼接 SQL 字符串,用参数化批量接口(如 Python 的
executemany()、Java 的addBatch()) - 显式开启事务:
BEGIN; INSERT ...; INSERT ...; COMMIT;,否则每条INSERT都是独立事务,刷 redo log 开销极大 - MyISAM 表可加
INSERT DELAYED(已废弃),InnoDB 不支持,别用
innodb_buffer_pool_size 和 bulk_insert_buffer_size 关键参数
InnoDB 的写入性能不是光靠“关索引”就能拉起来的。缓冲池太小,每插一批就要刷脏页;批量插入缓冲区没开足,索引重建阶段反而更卡。
实操建议:
-
innodb_buffer_pool_size至少设为物理内存的 50%–75%,低于 2GB 时大批量插入必然频繁刷盘 -
innodb_log_file_size太小会导致 checkpoint 过于频繁,建议单个 log 文件 ≥ 1GB(配合innodb_log_files_in_group = 2) -
bulk_insert_buffer_size控制索引构建时的内存用量,默认 8MB,大批量建索引前可临时调高到 64–256MB - 改这些参数必须重启 MySQL,线上操作务必提前规划维护窗口
真正卡住的地方往往不在 SQL 写法,而在 buffer_pool 是否吃满、redo log 是否被迫频繁刷盘、或者你误以为 DISABLE KEYS 对 InnoDB 也管用。










