Laravel使用cursor及chunk对比

  • 内容
  • 评论
  • 相关

1.使用seeder填充虚假用户数据:

php artisan make:seeder UsersSeeder

<?php

use Illuminate\Database\Seeder;

class UsersSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        DB::table('users')->delete();

        $faker = Faker\Factory::create('zh_CN');
        $total = 100000;
        $batchSize = 100;

        for ($i = 0, $limit = $total / $batchSize; $i < $limit; $i++) {
            DB::table('users')->insert($this->makeData($faker, $batchSize));
            printf("%d/%d\n", $i * $batchSize, $total);
        }
    }

    /**
     * 创建虚拟数据
     * @author ken
     * @since 2019-10-24
     * @param \Faker\Generator $faker
     * @param $batchSize
     * @return array
     */
    private function makeData(\Faker\Generator $faker, $batchSize)
    {
        $data = [];

        for ($i = 0; $i < $batchSize; $i++) {
            $data[] = [
                'name'     => $faker->userName,
                'email'    => mt_rand() . $faker->email,
                'password' => $faker->password(6),
            ];
        }

        return $data;
    }
}

2.DatabaseSeeder加入UsersSeeder:

<?php

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        $this->call(UsersSeeder::class);
    }
}

3.执行填充:
php artisan db:seed

4.创建测试命令:
php artisan make:command CursorChunkTestCommand --command=cursor_chunk:test

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Foundation\Auth\User;

class CursorChunkTestCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'cursor_chunk:test';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = '测试cursor和chunk的内存占用及效率';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        $start = microtime(true);

        // $this->all();
        // $this->chunk(10000);
        // $this->chunk(1000);
        // $this->chunk(100);
        $this->cursor();

        $time = microtime(true) - $start;
        $memory = memory_get_peak_usage(true) / 1024 / 1024;

        $this->output->writeln(sprintf('time: %f memory: %f MB', $time, $memory));
    }

    private function all()
    {
        foreach (User::all() as $user) {
            // do nothing
        }
    }

    private function chunk($count)
    {
        User::query()->chunk(
            $count,
            function ($users) {
                foreach ($users as $user) {
                    // do nothing
                }
            }
        );
    }

    private function cursor()
    {
        foreach (User::query()->cursor() as $user) {
            // do nothing
        }
    }
}

5.逐一测试对比:
php artisan cursor_chunk:test

all()查询:

chunk(10000),chunk(1000),chunk(100),cursor依次查询: