# 几十亿行要往 MySQL 里灌，InnoDB 哪些开关能临时关掉？

**URL:** <https://www.shiqidu.com/t/topic/1888>\
**Category:** 开发调优\
**Tags:** MySQL, 服务器, 数据库\
**Created:** [2026 年11 月 1 日 13:23 UTC](https://www.shiqidu.com/t/topic/1888 "2026-11-01T13:23:08Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

作者： ![shiqidu\_editor](https://www.shiqidu.com/user_avatar/www.shiqidu.com/shiqidu_editor/32/528_2.png) [@shiqidu\_editor](https://www.shiqidu.com/u/shiqidu_editor)\
发布日期： [2026 年11 月 1 日 13:23 UTC](https://www.shiqidu.com/t/topic/1888/1 "2026-11-01T13:23:08Z")

</div>

读一行，拼一条 INSERT，执行，再读下一行。这样的导入脚本十来分钟就能写完，拿几万行测试数据跑也挺顺。数据量换成几十亿行以后，时间多半耗在每条语句背后数据库要做的那些事上。MySQL 8.4 手册有一节专讲 InnoDB 的批量导入（[10.5.5 Bulk Data Loading for InnoDB Tables](https://dev.mysql.com/doc/refman/8.4/en/optimizing-innodb-bulk-data-loading.html)），下面按风险从小到大把里面的建议过一遍。

## 基本没副作用的几项

- 导入时关掉 autocommit，整批做完再 COMMIT。手册给的理由是 autocommit 模式下每插一行都要把日志刷一次盘。用 mysqldump 的 `--opt` 选项导出的文件，手册说不用再套一层事务也能较快导进 InnoDB 表。
- 用多行 INSERT，一条语句带多组 VALUES，减少客户端和服务端之间的通信开销。这条对任何表都适用，不限 InnoDB。
- 按主键顺序插入。InnoDB 表用的是聚簇索引，按 PRIMARY KEY 顺序写入相对快，表大到 buffer pool 装不下时这一点尤其要紧。
- 源数据本来就是文本文件的，用 LOAD DATA。[10.2.5.1 Optimizing INSERT Statements](https://dev.mysql.com/doc/refman/8.4/en/insert-optimization.html) 里说，这通常比用 INSERT 语句快 20 倍。

LOAD DATA 也有几处要留意，都写在 [LOAD DATA 语句页](https://dev.mysql.com/doc/refman/8.4/en/load-data.html)里：

- 不带 LOCAL 时文件放在服务器上，需要 FILE 权限，还受 `secure_file_priv` 限制。带 LOCAL 时文件在客户端，服务端和客户端两边都得允许，mysqld 关着 `local_infile` 就会报错。
- 没写 REPLACE 的话，LOCAL 的效果等同于 IGNORE：唯一键重复的行直接丢弃，数据解析错误降成警告，导入照常继续。手册的解释是服务端没法中途叫停文件传输。导完用 SHOW WARNINGS 看一眼比较放心。
- 用 LOCAL 时服务端会在临时文件目录里复制一份文件，那里空间不够，语句就会失败。
- LOAD DATA 对基于语句的复制不安全。`binlog_format=STATEMENT` 时每个副本都会生成一个临时数据文件，源端开了 binlog 加密，这个文件也照样不加密。

## 要自己担责任的开关

- 二级索引上有 UNIQUE 约束时，可以在导入会话里临时把 `unique_checks` 设成 0。大表能借 change buffer 批量写二级索引记录，省下不少磁盘 I/O。手册紧跟着的一句是：确保数据里没有重复键。检查关掉以后，重复键要靠导入前的数据清洗来挡。
- 有外键约束的表，导入期间把 `foreign_key_checks` 设成 0，同样能省磁盘 I/O。这一节没写违反外键的数据进去以后会怎样，导之前自己核一遍父表数据更稳妥。
- 批量导入页仍建议把 `innodb_autoinc_lock_mode` 从 1（consecutive）改成 2（interleaved）。8.4 的 [AUTO\_INCREMENT 处理页](https://dev.mysql.com/doc/refman/8.4/en/innodb-auto-increment-handling.html)却写明 2 已经是默认值，而且这个变量要在启动时配置。代价也写在那页：用基于语句的复制，或者靠回放 binlog 里的 SQL 做恢复时，模式 2 不安全。有并发插入时，批量插入分到的自增值可能有空洞。用基于语句复制的库，手册要求设成 0（traditional）或 1（consecutive），源库和副本保持同一个值。
- 有全文索引的表，建表时就定义 `FTS_DOC_ID` 列（`BIGINT UNSIGNED NOT NULL`），配一个叫 `FTS_DOC_ID_INDEX` 的唯一索引，先导数据，最后再建 FULLTEXT 索引。这一列每次 INSERT 或 UPDATE 都得单调递增。不预先建的话，InnoDB 会在 CREATE FULLTEXT INDEX 时把 `FTS_DOC_ID` 加成隐藏列，代价是重建整张表。

## redo log 那个开关

批量导入页的倒数第二条，是往新实例导数据时考虑用 `ALTER INSTANCE DISABLE INNODB REDO_LOG` 关掉 redo 日志。[Redo Log 一节](https://dev.mysql.com/doc/refman/8.4/en/innodb-redo-log.html)补充说，关掉以后省下的是 redo 写入和 doublewrite 缓冲。

两页用的是同一段警告：这个功能只用于往新的 MySQL 实例导数据，不要在生产系统上关 redo 日志。关闭期间可以正常关机重启，但服务意外停掉的话，可能丢数据、损坏实例。真碰上了，重启会被拒绝并报错，手册说这时必须初始化一个新实例，从头再导一遍。

启用和关闭 redo 日志需要 `INNODB_REDO_LOG_ENABLE` 权限，当前状态可以查 `Innodb_redo_log_enabled`。关闭期间不能做克隆，也不能开 redo log 归档。

手册最后一条建议是用 MySQL Shell：`util.importTable()` 是并行的表导入工具，面向大数据文件。`util.loadDump()` 加载 dump 时也能并行。

这些手段各能省多少时间，除了 LOAD DATA 那一句，手册都没给数字，想知道效果得拿自己的数据分别试。有人在新实例上关过 redo log 做初始化导入吗？开回来以后有没有遇到别的问题？
