yii 2.0.51 中 ar 默认批量插入性能差,应优先用 batchinsert() 绕过模型层;超千行宜用原生 sql 配合 insert ignore 或 on duplicate key update;禁用 save() 循环、事务包裹等伪批量操作,并配合关闭时间戳、索引检查及启用真实预处理优化。

Yii 2.0.51 中 ActiveRecord(AR)默认的批量插入方式(如循环调用 save() 或 insert())性能极差,本质是逐条执行 INSERT 语句、触发完整生命周期(验证、事件、关系处理等),在插入千条以上数据时会明显成为瓶颈。要真正提升性能,必须绕过 AR 的对象层开销,直连底层 Query Builder 或原生 SQL。
优先使用 batchInsert() 方法
这是 Yii 提供的最轻量、最安全的批量插入方案,不经过 AR 模型,只拼接单条 INSERT ... VALUES 语句,支持自动转义和类型绑定:
- 必须提供完整的字段名数组(顺序与 values 严格对应)
- values 是二维数组,每项是一行数据(不校验、不触发事件、不走验证)
- 示例:
Yii::$app->db->createCommand()->batchInsert('user', ['name', 'email', 'status'], [['Alice','a@b.com',1], ['Bob','b@c.com',1]])->execute(); - 单次最多插入约 500–1000 行较稳妥(避免 SQL 包过大或超 max_allowed_packet)
大容量场景改用原生 SQL + execute()
当单批超 2000 行,或需更高可控性(如忽略重复、ON DUPLICATE KEY UPDATE),建议手写 INSERT 语句:
- 用
str_repeat()和array_merge(...$rows)动态拼接 VALUES 子句,避免字符串拼接 SQL 注入风险 - 利用 MySQL 的
INSERT IGNORE或ON DUPLICATE KEY UPDATE处理冲突 - 注意:主键自增 ID 不会返回,如需获取,改用
lastInsertId()(仅适用于无冲突的连续插入)
慎用 AR 批量操作的常见误区
以下做法看似“批量”,实则毫无性能优势,甚至更慢:
-
User::insertAll()—— Yii 2.0.51 并未内置该方法,属误传 - 循环中 new User() → setAttributes() → save(false) —— 仍逐条执行、建对象、查表结构、开事务
- 用事务包裹循环 save() —— 只减少提交次数,不减少 SQL 条数和 ORM 开销
- 覆盖
beforeSave返回 false 跳过验证 —— 验证跳过了,但事件、属性赋值、SQL 构建等仍执行
配套优化点不能少
光换插入方式不够,还需调整数据库和连接层配合:
- 关闭 ActiveRecord 的自动时间戳填充(
createdAt/updatedAt字段设为NULL或显式传值) - 确保目标表引擎为 InnoDB,关闭唯一索引/外键检查(仅限导入期临时操作)
- 连接配置中开启
'attributes' => [PDO::ATTR_EMULATE_PREPARES => false],启用真正预处理 - 大批次插入前执行
SET autocommit = 0,结束后再COMMIT(通过createCommand('SET autocommit=0')->execute())











