优化 Laravel 查询:分块数据的正确方法

优化 laravel 查询:分块数据的正确方法

为什么应该避免使用块?

最好使用 chunkbyid 而不是 chunk 以避免批量更新时丢失行。使用 chunk 可以在更新行后移动后续查询的偏移量,从而导致跳过未处理的行。

例如:

post::where('processed', 0)->chunk(100, function($posts) {
    foreach($posts as $post) {
       $post->processed = 1;
       $post->save();
    }
});

上面的代码生成以下查询。

select * from `posts` where `processed` = 0 limit 100 offset 0
select * from `posts` where `processed` = 0 limit 100 offset 100
...

第一个块更新 100 行。第二个查询没有意识到这一点,因此跳过 100 个未处理的行,因为它仍然使用偏移量。

以上由
详细解释 太阮雄

如何对有限数量的行进行分块?

当尝试使用 laravel 的 chunk() 方法处理有限数量的行时,我们可能期望以下代码以 2 为一组仅处理 5 个用户:

$query  = \app\models\user::query()->take(5);

$query->chunk(2, function ($users) {
  // process users
});

但是,这将处理数据库中的所有用户,一次两个。发生这种情况是因为 laravel 的 chunk() 方法忽略了应用于查询的 take() 限制,导致所有行都以块的形式处理。

为了确保只在块中处理有限数量的行(例如 5 个用户),我们可以实现一个自定义索引计数器,该计数器将在达到指定限制后中断分块循环。以下代码实现了这一点:

class UserProcessor
{
    private int $index = 0;
    private int $limit = 5;

    public function process()
    {
        $query = \App\Models\User::query();

        $query->chunk(2, function ($users) {
            foreach ($users as $user) {
                $this->index++;

                // Process each user here
                // Example: $user->processData();

                if ($this->index >= $this->limit) {
                    // Stop processing after reaching the limit
                    return false;  // This will stop chunking
                }
            }
        });
    }
}

注意

$index 和 $limit 是类的属性,而不是通过 use($index, $limit) 传递给闭包的方法变量。这是因为通过 use() 传递的变量会按值复制到闭包对象中。因此,闭包内的任何修改都不会影响原始值,这就是为什么它们必须是类属性才能正确更新和跟踪迭代之间的更改。

以上就是优化 Laravel 查询:分块数据的正确方法的详细内容,更多请关注其它相关文章!