单条循环插入是PHP批量写入性能黑洞,必须改用多值INSERT(如INSERT INTO t(a,b) VALUES (1,'x'),(2,'y'))、显式事务控制(SET autocommit=0+BEGIN/COMMIT分批)、预处理防注入,并在百万级场景优先用LOAD DATA INFILE。

单条循环插入在PHP里就是性能黑洞,不改必卡死——核心问题不在代码写法,而在事务、SQL结构和驱动层默认行为三重叠加。
多值INSERT语句必须拼成一行
MySQL原生支持 INSERT INTO t (a,b) VALUES (1,'x'),(2,'y'),(3,'z'),这是最基础也最有效的提速点。逐条INSERT要走N次解析+网络往返+日志刷盘,而多值语法把N次压缩为1次执行。
- 每批次控制在
500~1000行以内,避免触发max_allowed_packet限制(默认4MB,超了直接报Packets larger than max_allowed_packet) - 别用字符串拼接 +
addslashes()防注入——它漏掉空字节、无法处理二进制数据;改用PDO::quote()或预处理绑定 - 字段顺序必须和
VALUES中每个元组严格对齐,错一位整条失败,错误信息是Column count doesn't match value count
事务必须显式控制,autocommit必须关死
光用 BEGIN / COMMIT 不够。PDO默认开启 autocommit=1,一旦你在事务中执行了 CREATE TABLE、DROP 或某些DDL,MySQL会自动提交当前事务——你根本不知道它啥时候偷偷提交了。
- 连接一建立就执行
SET autocommit = 0,再查SELECT @@autocommit确认返回0 - 不要依赖
PDO::beginTransaction()自动关autocommit——有些旧版PDO驱动不响应,得手动SET - 分批提交时,每批用独立
BEGIN/COMMIT,别包一个大事务插10万行,否则innodb_lock_wait_timeout很容易炸
LOAD DATA INFILE才是百万级导入的终点
当数据源是本地文件(比如CSV),LOAD DATA INFILE 比任何PHP拼SQL都快,实测10万行从15秒压到3秒内。但它不是“开箱即用”,权限和路径是硬门槛。
立即学习“PHP免费学习笔记(深入)”;
- 文件必须放在MySQL服务端可读路径下,
/tmp/常见但不总是可用;查SHOW VARIABLES LIKE 'secure_file_priv'看白名单目录 - MySQL用户需有
FILE权限:GRANT FILE ON *.* TO 'user'@'%' - 导入前加
SET FOREIGN_KEY_CHECKS = 0; SET UNIQUE_CHECKS = 0;,导完再设回1,否则唯一索引会逐行校验 - 空值要用
\N表示,不是空字符串也不是NULL字符串,否则全变成空字符串
最容易被忽略的是字符集隐式转换:CSV用UTF-8保存,但MySQL连接、表、列如果定义的是 utf8mb4,中间没统一就会乱码或截断。动手前先跑一遍 SET NAMES utf8mb4,再确认文件BOM头已去掉。



















