saveall()适用于需模型层能力(如自动时间戳、验证、事件钩子)的批量插入,须传二维数组且字段名匹配;db::insertall()适用于纯写入场景,更轻快稳,但无模型逻辑。

saveAll() 和 Db::insertAll() 是唯二值得用的批量插入方式,其他写法(比如 foreach + save()、手拼 SQL、逐条 insert())在 100 条以上数据时就会明显拖慢响应、吃光内存或触发 MySQL 报错。
什么时候该用 saveAll()?
需要模型层能力时才选它:自动时间戳、字段类型转换(如 json 字段序列化)、验证器、事件钩子(onAfterInsert 等)、主键自增后返回 ID 列表。
- 必须传二维数组,每个子数组键名要和数据库字段一致;若字段名不匹配,得显式传
field参数映射 - 默认开启验证,可信数据源可加
['validate' => false]跳过,性能提升显著 - 单次别超 500 条,否则容易触发 MySQL 的
Packets larger than max_allowed_packet are not allowed - 主键是自增整型时,返回的是插入后的 ID 数组;如果是 UUID 或字符串主键,只返回
true/false - 别在循环里反复
new User(),复用一个模型实例即可:$user = new User(); $user->saveAll($chunk)
什么时候该用 Db::insertAll()?
只要求“把数据塞进表里”,不关心模型逻辑、不依赖验证、不改时间戳、不需要事件——那就直接上 Db::insertAll(),它更轻、更快、更稳。
- 字段名必须严格等于数据库列名,模型里的
type映射、json自动序列化、create_time自动填充全部失效 - 返回值是影响行数(
int),不是 ID 列表;想拿 ID 得自己查或换回saveAll() - 高并发写入时锁表风险更低,因为不读模型配置、不初始化验证器
- 适合日志归档、离线导入、Excel 解析后落地等纯写入场景
大批次必须分段 + 事务包裹
一次性处理 1 万条?哪怕用 insertAll() 也会爆内存或超时。框架不会自动帮你切片,这事得你来干。
- 推荐每批 200–500 条,用
array_chunk($data, 400)拆分 - 每批必须用
Db::transaction()包裹,异常自动回滚,否则部分失败会导致数据不一致 - CLI 脚本记得加
set_time_limit(0);Web 请求建议扔进队列,Nginx 默认 30 秒就断连 - 每批执行完调
gc_collect_cycles(),尤其在 CLI 下防止内存持续上涨
Excel 导入时最容易踩的坑
不是插不进去,而是插进去的数据不对——空格、科学计数法、日期错乱、空单元格当字符串,这些在 insertAll() 里全会原样入库,报错都未必有。
- 别用
getActiveSheet()->toArray(),它会把整个 Excel 加载进内存,上万行直接 OOM - 用
getRowIterator($start, $chunkSize)流式读取,每块处理完立刻unset()行对象 - 标题行要
trim()+ 清除换行符,再跟字段名对齐;数值列优先用getFormattedValue()防科学计数丢失精度 - 日期必须用
PhpSpreadsheet\Shared\Date::excelToDateTimeObject()转,不能靠strtotime()猜 - 空单元格统一转成
null,别留着'',否则 int 字段会插成 0
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











