Laravel 5.5播种机真的很慢

时间:2018-02-10 18:28:31

标签: php laravel laravel-5

我尝试使用大量点击来测试数据库以测试我的仪表板指标显示。所以我想生成25k命中,并将它们与数据库中的随机Post相关联,以便数据保持一致。

我最初尝试在Post课程中抓取一个随机HitFactory,但这真的非常非常慢(就像生成数千次点击的时间一样)。然后我将随机部分移动到播种器类中,并且只调用一次以最小化DB命中,认为这会大大加快它的速度。但它没有 - 创建一个Hit对象仍需要至少5-10秒。

我不确定这是怎么可能的 - 我是否缺少优化?请注意,我不能只生成1到x之间的随机int,并将其用作链接Post,因为我使用了posts表的UUID样式ID。

这是需要很长时间才能运行的播种机:

use Illuminate\Database\Seeder;

class HitsTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {

        $posts = App\Post::all();

        $i = 0;

        while ($i <= 25000) {
            $post = $posts->random();
            factory(App\Hit::class)->create(
                [
                    'post_key' => $post->post_key,
                    'subject_code' => $post->subject_code,
                    'subject_id' => $post->subject_id,
                ]
            );
            $i++;
        }
    }
}

编辑:我还尝试在HitsTableSeeder中生成1-500之间的随机int,并将其用作$posts集合的索引,以完全消除random()调用。它仍然比我想象的慢。

    while ($i <= 25000) {
        $t = rand(0, 500);
        $post = $posts[$t];

3 个答案:

答案 0 :(得分:3)

只是发布此内容,以防将来对任何人有帮助。我遇到了一个插入速度极慢的类似问题。如果使用MySQL,请尝试使用数据库事务。在您的情况下,请在while循环之前手动启动事务,然后再提交事务。显然,如果您不手动启动并提交整个组的事务(或您愿意的话,则使用大块),则MySQL引擎将分别提交每个插入(增加大插入会引起的开销)。完成此操作后,我看到了巨大的性能提升。不仅仅是块,这实际上对我的性能没有帮助。像这样:

use Illuminate\Database\Seeder;

class HitsTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {

        $posts = App\Post::all();

        $i = 0;

        DB::beginTransaction();

        while ($i <= 25000) {
            $post = $posts->random();
            factory(App\Hit::class)->create(
                [
                    'post_key' => $post->post_key,
                    'subject_code' => $post->subject_code,
                    'subject_id' => $post->subject_id,
                ]
            );
        }

        DB::commit();
    }
}

编辑:请注意,实际上我还没有在工厂尝试过,仅在常规插入时尝试过(例如使用Model :: create())。希望它会有所帮助,但我不确定100%。

答案 1 :(得分:0)

首先尝试make Hit个实例,然后将其分块为$posts->count()大小的批量:

$posts = App\Post::all();
$n = $posts->toArray();
$m = 25000;
$c = $posts->count();

factory(App\Hit::class, $m)->make()->chunk($c, function ($hits) use ($n) {
    foreach ($hits as $i => $hit) {
        $hit->fill([
            'post_key' => $n[$i]['post_key'],
            'subject_code' => $n[$i]['subject_code'],
            'subject_id' => $n[$i]['subject_id']
        ]);
        $hit->save();
    }
});

这样可以使Hit条记录均匀分散到Posts。如果您想要更随机的权重,可以使用array_random来移动内部$hits循环中的帖子。

答案 2 :(得分:0)

$posts = App\Post::orderBy('id')->take(25000)->chunk(250, function ($posts) {
    foreach ($posts as $post) {
        //
    }
});

只是为了确保您不是一次检索所有记录并将它们分成250个每个查询以节省内存。 但是请尝试使用$faker在工厂目录中创建此工厂。

<?php

use Faker\Generator as Faker;

$factory->define(App\Post::class, function (Faker $faker) {
    return [
        'post_key' => $faker->name,
        'subject_code' => $faker->address,
        'subject_id' => $faker->numberBetween(1,25000)/* or either make it auto increment or loop on a counter */,
    ];
});

现在在播种器运行方法中只需调用:

factory(App\Post::class, 25000)->create();