Laravel 里面的 chunk 分块效率问题

laravel里面的chunk分块效率问题

在批处理较大数据数据时,laravel提供了chunk处理大块数据的方法,但数据量大了之后效率会非常慢

本次数据库测试数据供有二十万零一千(201000)条数据

chunk方法

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use App\Models\Test as TestModel;

class Test extends Command
{
    protected $signature = 'db:test';

    /**
     * 处理时间
     * @return float\
     */
    public function microtime_float():float\
    {
        list($usec, $sec) = explode(" ", microtime());
        return ((float)$usec + (float)$sec);
    }

    public function chunkTest()
    {
        // 每次处理
        $speed = 1000;
        // 进度条
        $bar = $this->output->createProgressBar(TestModel::query()->count());
        // 记录开始时间
        $timeStart = $this->microtime_float();
        // chunk 分块处理数据
        TestModel::query()->chunk($speed, function ($item) use ($bar, $speed) {
        // 业务处理逻辑...
        // ....
        // 进度条步进{$speed}步\  $bar->advance($speed);
        });
        $bar->finish();

        // 处理完成,记录结束时间
        $timeEnd = $this->microtime_float();
        $time = $timeEnd - $timeStart;
        // 输出信息
        $this->info('chunk用时:'. $time);
    }
}

执行:

php artisan db:test
201000/201000 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100% chunk用时:138.11135005951

发现出了20w数据用了两分多钟,效率似乎有点低,解决方法

记录最大id方法:

public function idTest()
{
    // 进度条
    $bar = $this->output->createProgressBar(TestModel::query()->count());
    $timeStart = $this->microtime_float();
    // 记录最大的id
    $maxId = 0;
    // 每次处理多少条数据
    $speed = 1000;

    while (true) {
        $models = TestModel::query()
            // 每次循环加上id条件
            ->where('id', '>', $maxId)
            ->limit($speed)
            ->orderBy('id')
            ->get();

        // 处理具体业务逻辑...

        // 如果没有数据就代表处理完成,break;
        if ($models->isEmpty()) {
            break;
        }

        // 记录下本次的最大id,下次循环时当作条件
        $maxId = $models->max(['id']);

        $bar->advance($speed);
    }

    $timeEnd = $this->microtime_float();
    $time = $timeEnd - $timeStart;
    $bar->finish();
    $this->info('id条件用时: '. $time);
}

执行:

php artisan db:test
 201000/201000 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%id条件用时: 7.790333032608

20W数据只用了7秒

《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 9
leo

Laravel 早就给你准备好轮子了,chunkById

1周前 评论
zxr615 (楼主) 1周前
mowangjuanzi 1周前
Timgle

这可以哦

1周前 评论
leo

Laravel 早就给你准备好轮子了,chunkById

1周前 评论
zxr615 (楼主) 1周前
mowangjuanzi 1周前

虽然laravel封装好了,但是我更感谢你分享了你实现的想法

1周前 评论

有大佬解释下chunk 和 chunkById 的使用场景?

1周前 评论

不应该呀,chunk 怎么会慢的呢

1周前 评论
leo
1周前 评论
chuanwen 1周前
命中水

@xxx chunk就是limit 1000,10 chunkById就是where id > ? limit 10

如果查询结果没办法保证有序的话就用chunk,如果可以保持有序就chunkById比较快。

1周前 评论

两个都测试了一下 ,发现并没有你说的那么明显的效果 ,,数据也是二十多万级别的 比你说的数据还要多,,只要id是主键效果没有区别

3天前 评论

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!