Laravel中chunk方法分块处理数据
在一些情况下,我们需要对大批量的数据进行操作,如果这个时候我们使用foreach的话,很可能会遇到操作超时的情况。
在Laravel框架中我们可以很方便的使用chunk方法来解决。
来看一个简单的例子:
$users = User::all();
foreach ($users as $user) {
$some_value = ($user->some_field > 0) ? 1 : 0;
// 一些其他的逻辑
$user->update(['some_other_field' => $some_value]);
}
这段代码看起来并没有什么不对,但是当数据量很大的时候,情况就不那么乐观了,一方面是运行时间,再者要考虑数据存储时内存消耗完。
在Laravel中,应用chunk将数据分块的方法,可以很好的处理这种问题,下面是应用chunk的代码:
User::chunk(100, function ($users) {
foreach ($users as $user) {
$some_value = ($user->some_field > 0) ? 1 : 0;
// might be more logic here
$user->update(['some_other_field' => $some_value]);
}
});
这段代码是执行一个100条的数据进行更新,当执行完成后继续后面的另一百条数据……
也就是说他每次操作的是一个数据块而不是整个数据库。
User::chunk(100, function ($users) {
foreach ($users as $user) {
$some_value = ($user->some_field > 0) ? 1 : 0;
// might be more logic here
$user->update(['some_other_field' => $some_value]);
}
});
需要注意的是:当使用带筛选的条件的chunk时,如果是自更新,那么你会漏掉一些数据,接着看代码:
User::where('approved', 0)->chunk(100, function ($users) {
foreach ($users as $user) {
$user->update(['approved' => 1]);
}
});
如果要运行上面的代码,并不会有报错,但是where条件是筛选approved为0的user然后将approved的值跟新为1。
在这个过程中,档第一数据库的数据被修改后,下一个数据块的数据将是在被
修改后的数据
中选出来的,这个时候数据变了,而page也加了1
。所以执行结束后,只对数据中一半的数据进行了更新操作。
如果没有明白的话,我们来看一下chunk的底层实现。还以上面的代码为例,假如一共有400条数据,数据被按照100条进行分块处理。
page = 1: 最开始的时候page为1,选取1-100条数据进行处理;
page = 2: 这时候前一百数据的approved值全部为1,那么在次筛选的时候数据将从第101条开始,而这个时候的page=2,那么处理的数据将是第200-300之前的数据之后依旧。
chunk的分块机制源码:
public function chunk($count, callable $callback)
{
$results = $this->forPage($page = 1, $count)->get();
while (count($results) > 0) {
// 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 (call_user_func($callback, $results) === false) {
return false;
}
$page++;
$results = $this->forPage($page, $count)->get();
}
return true;
}
如果要在分块结果时更新数据库记录,则块结果可能会和预计的返回结果不一致。 因此,在分块更新记录时,最好使用 chunkById
方法。 此方法将根据记录的主键自动对结果进行分页:
DB::table('users')->where('active', false)
->chunkById(100, function ($users) {
foreach ($users as $user) {
DB::table('users')
->where('id', $user->id)
->update(['active' => true]);
}
});
提示在块的回调里面更新或删除记录时,对主键或外键的
任何更改都可能影响块查询
。 这可能会导致记录没有包含在分块结果中。
Process big DB table with chunk() method
Laravel中chunk方法分块处理数据
Laravel 查询构造器
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。