在 Laravel 中播种大数据的最佳案例

问题描述 投票:0回答:5

我有一个包含 30,000 多条记录的文件,另一个包含 41,000 条记录。是否有使用 laravel 4 的

db:seed
命令播种的最佳案例研究?一种使插入更加快速的方法。

感谢您的帮助。

laravel laravel-4
5个回答
11
投票

别害怕,40K 行的表有点小。我有一个 100 万行的表,种子很顺利地完成了,我只需要在做之前添加这个:

DB::disableQueryLog();

在禁用它之前,Laravel 浪费了我所有的 PHP 内存限制,无论我给了多少内存限制。

我使用

fgets()
从 .txt 文件读取数据,以编程方式构建数组并执行:

DB::table($table)->insert($row);

一个接一个,可能会特别慢。

我的数据库服务器是 PostgreSQL,插入大约需要 1.5 小时才能完成,可能是因为我使用的虚拟机内存不足。有一天我会在一台更好的机器上做一个基准测试。


9
投票

2018更新

我遇到了同样的问题,经过 2 天的头痛,我终于可以编写脚本在不到 30 秒的时间内播种 42K 条目!

你问如何?

第一种方法

此方法假设您有一个包含一些条目的数据库(在我的例子中是 42k 个条目),并且您希望将其导入到其他数据库中。将您的数据库导出为带有标题名称的 CSV 文件,并将该文件放入项目的公共文件夹中,然后您可以解析该文件并通过 seeder 将所有条目一一插入到新数据库中。

所以你的播种机看起来像这样:

<?php

use Illuminate\Database\Seeder;

class {TableName}TableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        $row = 1;
        if (($handle = fopen(base_path("public/name_of_your_csv_import.csv"), "r")) !== false) {
            while (($data = fgetcsv($handle, 0, ",")) !== false) {
                if ($row === 1) {
                    $row++;
                    continue;
                }
                $row++;

                $dbData = [
                    'col1' => '"'.$data[0].'"',
                    'col2' => '"'.$data[1].'"',
                    'col3' => '"'.$data[2].'"',
                    so on...how many columns you have
                ];

                $colNames = array_keys($dbData);

                $createQuery = 'INSERT INTO locations ('.implode(',', $colNames).') VALUES ('.implode(',', $dbData).')';

                DB::statement($createQuery, $data);
                $this->command->info($row);
            }
            fclose($handle);
        }
    }
}

简单又容易:)


第二种方法

如果您可以修改 PHP 的设置并为特定脚本分配较大的大小,那么此方法也将起作用。

基本上你需要关注三个主要步骤:

  • 为脚本分配更多内存
  • 关闭查询记录器
  • 将数据分成 1000 块
  • 迭代数据并使用
    insert()
    一次创建 1K 的块。

因此,如果我将上述所有步骤结合到播种机中,您的播种机将如下所示:

<?php

use Illuminate\Database\Seeder;

class {TableName}TableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        ini_set('memory_limit', '512M');//allocate memory
        DB::disableQueryLog();//disable log
        //create chunks
        $data = [
           [
              [
                 'col1'=>1,
                 'col2'=>1,
                 'col3'=>1,
                 'col4'=>1,
                 'col5'=>1
              ],
              [             
                 'col1'=>1,
                 'col2'=>1,
                 'col3'=>1,
                 'col4'=>1,
                 'col5'=>1
              ],
              so on..until 1000 entries 
           ],
           [
              [
                 'col1'=>1,
                 'col2'=>1,
                 'col3'=>1,
                 'col4'=>1,
                 'col5'=>1
              ],
              [             
                 'col1'=>1,
                 'col2'=>1,
                 'col3'=>1,
                 'col4'=>1,
                 'col5'=>1
              ],
              so on..until 1000 entries 
           ],
           so on...until how many entries you have, i had 42000
      ]
      //iterate and insert
      foreach ($data as $key => $d) {
          DB::table('locations')->insert($d);
          $this->command->info($key);//gives you an idea where your iterator is in command line, best feeling in the world to see it rising if you ask me :D
      }
  }
}

瞧,你可以走了:)

希望对你有帮助


0
投票

我从不同的数据库迁移,我必须使用原始sql(从外部文件加载)和批量插入语句(我通过navicat导出结构,它可以选择每250KiB分解插入语句)。例如:

$sqlStatements = array(
"INSERT INTO `users` (`name`, `email`)
VALUES
    ('John Doe','[email protected]'),.....
    ('Jane Doe','[email protected]')",
"INSERT INTO `users` (`name`, `email`)
VALUES
    ('John Doe2','[email protected]'),.....
    ('Jane Doe2','[email protected]')"
);

然后我循环插入语句并使用

执行
DB::statement($sql). 

我无法一次插入一行。我确信有更好的替代方案,但这至少有效,同时让我将其保留在 Laravel 的迁移/播种中。


0
投票

我今天也遇到了同样的问题。 禁用查询日志还不够。看起来一个事件也被解雇了。

DB::disableQueryLog();

// 插入

// 重置事件以释放内存。

DB::setEventDispatcher(new Illuminate\Events\Dispatcher());


0
投票

就我而言:

  • 约10.000条记录,
  • 我的源 .csv 在第一行有标题,
  • 大约20秒后结束


public function run(): void
    {
        DB::disableQueryLog();
        $row = 0;
        $now = Carbon::now();

        if (($handle = fopen(database_path('seeders/areas_italy.csv'), "r")) !== false) {
            while (($data = fgetcsv($handle, 0, ",")) !== false) {
                if ($row === 0) {
                    $row++;
                    continue;
                }
                $row++;

                $dbRowData = [
                    'id' => $data[0],
                    'region_id' => $data[1],
                    'region_name' => $data[2],
                    'province_id' => $data[3],
                    'province_code' => $data[4],
                    'province_name' => $data[5],
                    'town' => $data[6],
                    'capoluogo' => $data[7],
                    'created_at' => $now
                ];

                DB::table('areas')->insert($dbRowData); // don't use eloquent but query builder for performace
            }
            fclose($handle);
        }
© www.soinside.com 2019 - 2024. All rights reserved.