insert()适合批量写入几十至几万条结构相同、无需模型事件/自动填充/时间戳的纯数据;需二维关联数组且key统一,手动补时间戳;单次建议≤1000行,超量用chunk分批;冲突场景应选upsert()并确保索引存在。

insert() 方法适合什么场景
当你要一次性写入几十到几万条结构相同的数据,且不需要触发模型事件(如 creating、saving)、不走 Eloquent 生命周期、也不需要自动填充($fillable 或访问器/修改器)时,insert() 是最直接的选择。它绕过模型实例化,直接拼 SQL 批量插入,性能比循环 save() 高 5–50 倍(取决于数据量和字段数)。
常见误用:拿 insert() 去插带时间戳、关联 ID 或需校验的业务数据——它不会自动加 created_at/updated_at,也不会检查 $casts 或调用 setXXXAttribute()。
怎么正确构造 insert() 的数据格式
insert() 只接受二维数组,每项必须是关联数组(key 为字段名,value 为值),且所有子数组的 key 必须完全一致。数据库会按第一个子数组的 key 顺序绑定字段,后续数组缺 key 会导致 SQLSTATE[HY093]: Invalid parameter number 错误。
- ✅ 正确:
DB::table('users')->insert([['name' => 'A', 'email' => 'a@b.c'], ['name' => 'B', 'email' => 'b@b.c']]); - ❌ 错误:
[['name' => 'A'], ['name' => 'B', 'email' => 'b@b.c']](key 不统一) - ⚠️ 注意:
created_at和updated_at不会自动生成,需手动补全:now()或Carbon::now()
单次 insert 最多能插多少行
没有 Laravel 层面的硬限制,但受 MySQL 的 max_allowed_packet 和 PHP 内存控制。默认 MySQL 包大小是 4MB,按平均每行 200 字节估算,单次最多插约 2 万行。超过后会报错:Packets larger than max_allowed_packet are not allowed。
实操建议:
- 生产环境单次控制在 1000 行以内,平衡性能与稳定性
- 用
array_chunk($data, 1000)分批处理,避免超限或事务过长 - 如果表有唯一索引,批量插入失败时整批回滚,无法定位哪一行出错——此时不如改用
upsert()(Laravel 8.5+)或分批 + try/catch
insert() 和 upsert() 怎么选
如果你的数据可能重复(比如同步第三方用户),且希望「存在则忽略/更新」,别硬套 insert()。Laravel 的 upsert() 底层用 INSERT ... ON DUPLICATE KEY UPDATE,支持指定冲突字段和更新列,但要求数据库引擎支持(MySQL ≥5.6,PostgreSQL ≥9.5)。
关键区别:
-
insert():只插入,冲突直接报错(Integrity constraint violation) -
upsert(['email' => 'x@y.z'], ['email'], ['name']):以email为唯一判定依据,冲突时更新name字段 -
upsert()仍不触发模型事件,但会尊重$casts(因为参数经由模型静态方法解析)
真正容易被忽略的是:即使你用了 upsert(),如果数据库没在对应字段建 UNIQUE 或 PRIMARY KEY 索引,它就退化成普通 insert,还多一次查询开销。











