Как вставить большие данные в laravel?

Я использую ларавель 5.6

Мой скрипт для вставки больших данных выглядит так:

...
$insert_data = [];
foreach ($json['value'] as $value) {
    $posting_date = Carbon::parse($value['Posting_Date']);
    $posting_date = $posting_date->format('Y-m-d');
    $data = [
        'item_no'                   => $value['Item_No'],
        'entry_no'                  => $value['Entry_No'], 
        'document_no'               => $value['Document_No'],
        'posting_date'              => $posting_date,
        ....
    ];
    $insert_data[] = $data;
}
\DB::table('items_details')->insert($insert_data);

Пробовал вставить 100 запись скриптом, работает. Он успешно вставляет данные

Но если я пытаюсь вставить 50000 записей с помощью скрипта, он становится очень медленным. Я ждал около 10 минут, и это не сработало. Существует такая ошибка:

504 Gateway Time-out

Как я могу решить эту проблему?


person Success Man    schedule 23.07.2018    source источник
comment
Вы можете фрагментировать данные, используя функции фрагментации Laravel. Возможно, этот пост поможет вам вставить большие данные   -  person Dennis    schedule 24.07.2018
comment
Вы также можете использовать систему очередей Laravel и просто создавать 50 000 заданий для каждой вставки. Но если вы настаиваете на отправке больших количеств, вам нужно увеличить время ожидания PHP (вам нужно будет погуглить, какой у вас стек сервера для получения инструкций)   -  person Rob Fonseca    schedule 24.07.2018
comment
Да, чанк в этом случае особо не поможет. chunk поможет решить проблемы с использованием памяти, но не для вашего приложения, работающего слишком долго. Как предложил Роб, рассмотрите возможность использования очереди и создания задания для каждого отдельного элемента. Затем вы можете запустить кучу воркеров, чтобы обработать данные для вас.   -  person Dwight    schedule 24.07.2018
comment
@ Дуайт, я все еще в замешательстве. Попробуйте ответить на этот вопрос развернутым ответом(со скриптом)   -  person Success Man    schedule 24.07.2018
comment
@ Роб Фонсека, я все еще в замешательстве. Попробуйте ответить на этот вопрос развернутым ответом(со скриптом)   -  person Success Man    schedule 24.07.2018
comment
@ Деннис, я запутался, чтобы применить к моему делу   -  person Success Man    schedule 24.07.2018
comment
@SuccessMan попробуйте прочитать документы laravel, это очень поможет вам понять использование очередей. laravel.com/docs/5.6/queues   -  person Dennis    schedule 24.07.2018
comment
@ Дуайт, спасибо, я этого не знал, сегодня я тоже кое-что узнал ;)   -  person Dennis    schedule 24.07.2018
comment
Рассматривали ли вы возможность использования очереди?   -  person haakym    schedule 24.07.2018
comment
@haakym Да. я тоже это считаю   -  person Success Man    schedule 24.07.2018
comment
@haakym Пожалуйста, ответьте на этот вопрос, используя очередь. Так что есть какой-то вариант   -  person Success Man    schedule 25.07.2018


Ответы (4)


Как было сказано, чанки не помогут вам в этом случае, если это проблема времени выполнения. Я думаю, что массовая вставка, которую вы пытаетесь использовать, не может обрабатывать такой объем данных, поэтому я вижу 2 варианта:

1 - Реорганизуйте свой код, чтобы правильно использовать фрагменты, это будет выглядеть примерно так:

$insert_data = [];

foreach ($json['value'] as $value) {
    $posting_date = Carbon::parse($value['Posting_Date']);

    $posting_date = $posting_date->format('Y-m-d');

    $data = [
        'item_no'                   => $value['Item_No'],
        'entry_no'                  => $value['Entry_No'], 
        'document_no'               => $value['Document_No'],
        'posting_date'              => $posting_date,
        ....
    ];

    $insert_data[] = $data;
}

$insert_data = collect($insert_data); // Make a collection to use the chunk method

// it will chunk the dataset in smaller collections containing 500 values each. 
// Play with the value to get best result
$chunks = $insert_data->chunk(500);

foreach ($chunks as $chunk)
{
   \DB::table('items_details')->insert($chunk->toArray());
}

Таким образом, ваша массовая вставка будет содержать меньше данных и сможет обрабатывать ее довольно быстро.

2 - Если ваш хост поддерживает перегрузку во время выполнения, вы можете добавить директиву прямо перед тем, как код начнет выполняться:

ini_set('max_execution_time', 120 ) ; // time in seconds

$insert_data = [];

foreach ($json['value'] as $value)
{
   ...
}

Чтобы узнать больше, перейдите к официальной документации.

person Vit Kos    schedule 24.07.2018
comment
Во втором варианте вы все еще можете использовать куски, чтобы избежать большого объемного куска. - person Vit Kos; 24.07.2018
comment
Спасибо за Ваш ответ. Я попробую. Пожалуйста, обновите свой ответ с помощью chunk. Так что есть какой-то вариант - person Success Man; 24.07.2018
comment
Я должен был попробовать ваш сценарий. Существует такая ошибка: Argument 1 passed to Illuminate\Database\Query\Builder::insert() must be of the type array, object given - person Success Man; 24.07.2018
comment
@SuccessMan просто использует метод ->toArray() - person Vit Kos; 24.07.2018
comment
ОБНОВЛЕНО ^^. Проверьте кусок - person Vit Kos; 24.07.2018
comment
Ok. Ваш второй вариант, он такой же, как ответ на мой вопрос. Что изменилось? На самом деле я тоже добавил это ini_set('max_execution_time', 120 ) ;? - person Success Man; 24.07.2018
comment
@VitKos stackoverflow не поддерживает уценку, вам нужно сделать отступ в тексте для блоков кода - person haakym; 24.07.2018
comment
@SuccessMan, вы можете использовать его с любым кодом, который вам нравится, с вашей версией или версией фрагмента - вам решать. Я просто указал, как увеличить время выполнения во время выполнения. - person Vit Kos; 24.07.2018
comment
@Vit Kos Я попробовал ваш вариант использования чанка, и он работает. Итак, я получаю ваш ответ. Спасибо - person Success Man; 25.07.2018

Нет смысла использовать массив, а затем преобразовывать его в коллекцию.

Мы можем избавиться от массивов.

$insert_data = collect();

foreach ($json['value'] as $value) {
    $posting_date = Carbon::parse($value['Posting_Date']);

    $posting_date = $posting_date->format('Y-m-d');

    $insert_data->push([
        'item_no'                   => $value['Item_No'],
        'entry_no'                  => $value['Entry_No'], 
        'document_no'               => $value['Document_No'],
        'posting_date'              => $posting_date,
        ....
    ]);
}

foreach ($insert_data->chunk(500) as $chunk)
{
   \DB::table('items_details')->insert($chunk->toArray());
}
person Anton Vlasenko    schedule 11.08.2019

Вот очень хорошее и очень быстрое решение для вставки данных

$no_of_data = 1000000;
$test_data = array();
for ($i = 0; $i < $no_of_data; $i++){
    $test_data[$i]['number'] = "1234567890";
    $test_data[$i]['message'] = "Test Data";
    $test_data[$i]['status'] = "Delivered";
}
$chunk_data = array_chunk($test_data, 1000);
if (isset($chunk_data) && !empty($chunk_data)) {
   foreach ($chunk_data as $chunk_data_val) {
    DB::table('messages')->insert($chunk_data_val);
  }
}
person piyush anques    schedule 20.02.2020

Я использовал приведенный ниже код для проверки обновления или вставки данных из 11 тысяч строк. Я надеюсь, что это полезно для вас.

$insert_data = [];
for ($i=0; $i < 11000; $i++) { 
    $data = [
        'id' =>'user_'.$i,
        'fullname' => 'Pixs Nguyen',
        'username' => '[email protected]',
        'timestamp' => '2020-03-23 08:12:00',
    ];
    $insert_data[] = $data;
}

$insert_data = collect($insert_data); // Make a collection to use the chunk method

// it will chunk the dataset in smaller collections containing 500 values each. 
// Play with the value to get best result
$accounts = $insert_data->chunk(500);
// In the case of updating or inserting you will take about 35 seconds to execute the code below
for ($i=0; $i < count($accounts); $i++) { 
    foreach ($accounts[$i] as $key => $account)
    {
        DB::table('yourTable')->updateOrInsert(['id'=>$account['id']],$account);
    }
}
// In the case of inserting you only take about 0.35 seconds to execute the code below
foreach ($accounts as $key => $account)
{
    DB::table('yourTable')->insert($account->toArray());
}
person Pixs Nguyen    schedule 26.03.2021