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秒

本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由系统于 2年前 自动加精
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
讨论数量: 9
leo

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

4年前 评论
zxr615 (楼主) 4年前
mowangjuanzi 4年前
leo
4年前 评论
chuanwen 4年前
leo

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

4年前 评论
zxr615 (楼主) 4年前
mowangjuanzi 4年前

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

4年前 评论

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

4年前 评论

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

4年前 评论
leo
4年前 评论
chuanwen 4年前
命中水

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

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

4年前 评论

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

4年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!