跳转到主内容
新派编程网 - 新潮编程理念,全新技术课堂!

Laravel怎么处理批量更新_Laravel高效更新多条记录【操作】

Laravel批量更新应优先用upsert()实现单SQL多行插入或更新,需唯一索引支撑;updateOrCreate()适用于小批量且条件各异的单条操作;大数据量简单更新建议原生SQL;事务中谨慎加锁,避免死锁与性能损耗。 用
upsert()
一次性插入或更新,避免 N+1 批量更新最常踩的坑是写个循环调用
save()
,结果生成 N 条 SQL —— 数据量一上来就卡死。Laravel 9+ 原生支持
upsert()
,它底层走的是数据库的 INSERT ... ON DUPLICATE KEY UPDATE(MySQL)或 ON CONFLICT(PostgreSQL),一条语句搞定多行。 前提是你得有唯一索引(比如
id
或业务字段如
sku
),否则
upsert()
不知道按什么去“匹配更新”。 只传需要更新的字段:第二个参数指定哪些字段用于判断冲突(
['id']
),第三个参数指定哪些字段允许被更新(
['name', 'price']
) 别把主键当普通字段塞进更新列表里 ——
id
在冲突判断里用了,就别再放进第三个参数,否则可能意外覆盖 SQLite 用户注意:
upsert()
在 SQLite 3.24+ 才支持,旧版会抛出
BadMethodCallException
$data = [ ['id' => 1, 'name' => 'iPhone', 'price' => 6999], ['id' => 2, 'name' => 'iPad', 'price' => 3299], ]; Product::upsert($data, ['id'], ['name', 'price']);
用
updateOrCreate()
更新单条但逻辑复杂时 当你每条数据的“唯一性判断条件”不统一(比如有的靠
email
,有的靠
phone
),或者需要在创建时额外处理逻辑(比如生成默认头像、触发事件),
updateOrCreate()
比
upsert()
更灵活,但它本质仍是单条操作 —— 别误以为能批量用。 常见错误是把它塞进 foreach 循环还觉得“我用了 Laravel 方法就很优雅”,其实和手写
save()
性能没区别。 只适合小批量(≤ 50 条),且每条的 where 条件差异大 第二个参数必须是完整待存字段数组,第一个参数只是“查找条件”,别漏掉必填字段 如果数据库没有对应记录,它会执行 insert;但不会自动填充
created_at
等时间戳字段,除非你显式传入或模型开启了
$timestamps = true
Product::updateOrCreate( ['sku' => 'IPHONE15'], ['name' => 'iPhone 15', 'price' => 7999, 'status' => 'active'] );
手动写原生 SQL 批量更新(绕过 Eloquent 开销) 当你要更新几千甚至上万条,且字段逻辑简单(比如全设为某个值、按规则加减)、不需要模型事件或访问器/修改器时,硬写 SQL 是最快方案。Eloquent 的属性转换、关系加载、事件触发全被跳过。 Laravel 13.2.0 PHP中文网提供Laravel 13.2.0版本下载,Laravel框架 是基于 PHP 8.3+ 的高性能框架,官方推荐通过 Composer 安装。它内置 AI SDK、JSON:API Resources 及原生向量搜索,支持属性驱动开发与队列路由,大幅提升开发效率。相比旧版,13.2.0 优化了缓存 TTL 管理与实时通信,无需 Redis 即可横向扩展。作为现代 Web 开发首选,它兼顾安全与极速体验,助您快速构建企业级应用。 下载 风险在于:绕过了模型层校验,也拿不到更新后的模型实例;如果字段名拼错或类型不匹配,错误信息不如 Eloquent 友好。 用
DB::statement()
而不是
DB::select()
,后者只用于查询 WHERE 条件尽量走索引字段,否则容易锁表;线上环境务必先在测试库压测 MySQL 中
UPDATE ... WHERE id IN (...)
的
IN
列表长度别超 1000,超了拆成多个批次
DB::statement("UPDATE products SET status = ? WHERE category_id = ?", ['draft', 5]);
用事务包住批量操作,但别盲目加锁 很多人一想到“批量更新要安全”,立马 wrap 一个
DB::transaction()
,这没错;但紧接着加
sharedLock()
或
lockForUpdate()
就容易翻车 —— 多数场景根本不需要行锁。 锁只在你明确要防止并发修改同一行(比如库存扣减)时才有意义。普通后台批量改状态、分类,加锁反而拖慢整体吞吐,还可能引发死锁。 事务内别做 HTTP 请求、文件读写等耗时操作,否则锁持有时间不可控 如果真要锁,用
whereKey()
显式限定范围,别直接
Product::lockForUpdate()->get()
全表扫 PostgreSQL 用户注意:
lockForUpdate()
在批量更新中可能升级为页锁,影响并发度 批量更新真正的复杂点不在语法,而在于你是否清楚:这条更新会不会被其他请求同时修改?要不要触发模型事件?失败时需不需要精确到哪一行?—— 这些决定了你该选
upsert()
、手写 SQL 还是干脆拆成队列异步跑。

相关文章