Laravel没有原生updateBatch(),但有4种可靠批量更新方式:统一值用DB::table()->whereIn()->update();不同值用upsert()(需唯一索引);数值递增用increment();大数据量需分块事务处理。

直接说结论:Laravel 没有原生的 updateBatch(),但有 4 种真正可用、不踩坑的批量更新方式,选哪一种取决于你更新的是「统一值」还是「每行不同值」,以及数据量大小和是否需要模型逻辑。
统一字段值更新(如 status=2、is_verified=1)
这是最简单也最容易被忽略性能的场景。很多人写循环或手拼 SQL,其实一行就能搞定。
-
\DB::table('orders')->whereIn('id', [1, 2, 4, 6, 8])->update(['status' => 2])是标准解法,底层生成单条UPDATE ... WHERE id IN (?, ?, ?, ?, ?) - 别用
Order::whereIn(...)->update(...)模型调用 —— 如果模型启用了软删除或全局作用域,update()不会触发它们,但语义上容易误以为会走完整生命周期 - WHERE 条件数组超过 5000 项时,MySQL 可能报
max_allowed_packet错误,需分块处理(见下文事务部分) - 该方式跳过 Eloquent 的访问器、修改器、事件,纯 SQL 执行,快且安全
每行更新不同字段值(如 id=1 → name=A, price=99;id=2 → name=B, price=199)
这才是真正考验设计的地方。核心前提是:你得有唯一索引字段(比如 id、sku),否则数据库不知道“按什么匹配更新”。
-
Product::upsert($data, ['id'], ['name', 'price'])是 Laravel 9+ 推荐方案,一条 SQL 完成全部插入或更新,底层对应 MySQL 的INSERT ... ON DUPLICATE KEY UPDATE -
$data必须是二维数组,每个子数组字段名要一致,不能缺字段(如某条没传price,那这列就不会被更新) - 别把冲突字段(如
id)再放进第三个参数里 —— 否则可能意外覆盖主键,例如['id', 'name', 'price']作为更新字段列表是错的 - SQLite 用户注意:
upsert()在 SQLite 3.24+ 才支持,旧版本会抛BadMethodCallException
数值字段批量递增(如库存 +5、阅读数 +1)
这不是普通更新,而是原子性累加。用 update() 或 upsert() 都会出竞态问题,必须用数据库原生算术操作。
Laravel Cloud(cloud.laravel.com)。此技能用于处理所有 Laravel Cloud 请求,包括搜索和读取数据。只要任务涉及 Laravel Cloud,即使用此技能。
-
Product::whereIn('id', [1, 2])->increment('stock', 5)最常用,生成UPDATE SET stock = stock + 5,天然防并发覆盖 - 如果要对不同 ID 加不同值(如 id=1 加 3,id=2 加 7),
increment()不支持,得用upsert()配合数据库表达式,或改用原生CASE WHENSQL - 大数量(>1000 行)慎用循环调
increment()—— 仍是 N+1 查询,应先查出所有 ID,再用DB::statement()手写批量 CASE -
decrement()同理,只是减法;incrementEach()在 Laravel 11+ 才有,目前仍属实验性 API
大数据量或复杂逻辑下的事务与分块
当你要更新 5000+ 行,或操作涉及多个表联动(如扣库存 + 写日志 + 更新订单状态),就不能只看单个方法了。
- 单一大事务锁表太久,容易超时或阻塞其他请求;应使用
collect($data)->chunk(500)分批,每批进独立DB::transaction() - 在事务中避免 HTTP 请求、文件 IO、长时间计算 —— 这些会让锁持有时间不可控
- 若需触发模型事件(如
saving、updated),upsert()和原生update()都不走模型,此时只能用foreach+save(),但务必加DB::beginTransaction()包裹,并控制批次大小 - 禁用模型事件可提速:在批量前临时设
Product::$dispatchesEvents = [],完事再恢复(注意不是线程安全,仅限 CLI 或队列任务中使用)
真正容易被忽略的点是:没有唯一索引就硬上 upsert(),结果变成全量插入;或者把 HTML 表单提交的 roles[0][id] 直接喂给 update() 导致类型错误。批量更新不是堆方法,而是先理清「数据结构」和「数据库约束」,再选工具。










