记一次使用Laravel chunk时遇到的坑
作者: 分类: 经验分享 发布于: 2023-04-14 18:02:58 浏览:1,332 评论(0)
背景
最近在使用laravel 自定义命令行工具批量更新订单状态的时候,因为订单量差不多三千个,就使用Eloquent提供的chunk方法分块处理。
该方法一次获取结果集的一小块,并将其传递给闭包
函数进行处理。我的大概代码如下:
use App\Models\Order;
.
.
.
Order::query()
->where('status', 0)
->orderByDesc('id')
->chunk(50, function ($orders, $page) {
foreach ($orders as $order) {
$this->info('正在处理第' . $page . '页:' . $order->order_no);
if (something) {
continue;
}
Order::query()->where('order_no', $order->order_no)->update(['status' => 1]);
}
});
问题
每次取50个订单来更新状态,结果当我运行完一次的时候发现还有将近一半的订单没有运行处理到,翻了下chunk的源码:
public function chunk($count, callable $callback)
{
$this->enforceOrderBy();
$page = 1;
do {
// We'll execute the query for the given page and get the results. If there are
// no results we can just break and return from here. When there are results
// we will call the callback with the current chunk of these results here.
$results = $this->forPage($page, $count)->get();
$countResults = $results->count();
if ($countResults == 0) {
break;
}
// On each chunk result set, we will pass them to the callback and then let the
// developer take care of everything within the callback, which allows us to
// keep the memory low for spinning through large result sets for working.
if ($callback($results, $page) === false) {
return false;
}
unset($results);
$page++;
} while ($countResults == $count);
return true;
}
大概意思就是从第一页开始,每次从数据库查询 $count
条数据,当查询的记录$countResults
等于需要查询记录的时候,就继续查询下一页,直到查询结果数小于需要查询的数量为止。
这样是没有问题的,那么问题只能出在自己的代码逻辑上,想了很久发现我在检索订单的时候用到了status
字段,最后操作订单的时候也更新了这个字段,这样导致更新后符合条件的订单数变小了,在执行下一页limit
的时候会跳过上一次符合条件的数量,
导致要执行很多次脚本才能处理完成
结论
在使用 chunk 分块处理的数据的时候,要避免更新的字段和检索数据时的字段相同。
转载时请注明出处及相应链接。
本文永久链接: http://www.baigei.com/articles/laravel-chuck