Форма запроса ОБНОВЛЕНИЕ пока цикл, код очень медленный - PullRequest
1 голос
/ 11 июня 2019

В моем приложении Laravel есть такой код: я читаю каждую строку в файле .csv и хочу обновить значение. Но запрос на обновление множественного числа очень медленный с CSV из 8 тыс. Строк. Как я могу ускорить этот код? Спасибо

DB::beginTransaction();

        try {

            $delimiter = ",";
            $firstLine = true;

            if ($handle !== FALSE) {
                $position = 1;
                while (($csv_line = fgetcsv($handle, 1000, $delimiter)) !== FALSE) {

                    if ($firstLine == true) {
                        $firstLine = false;
                        continue;
                    }

                    $player_uid = $csv_line[0];

                    DB::table('scores')
                        ->where('season_uid', $season_uid)
                        ->where('day', $day)
                        ->where('player_uid', $player_uid)
                        ->update(['position' => $position]);

                    $position++;

                }
                fclose($handle);
            }

            DB::commit();
            return true;

        } catch (\Exception $e) {
            Log::error($e);
            DB::rollBack();
            return false;
        }

Ответы [ 2 ]

0 голосов
/ 11 июня 2019

Использование отдельных заданий для этого было бы действительно рекомендуемым способом, но вы могли бы попробовать следующий код. Идея создания одного запроса на обновление была найдена в https://github.com/laravel/ideas/issues/575. Парень уменьшил время загрузки, и в итоге оказался в 13 раз быстрее.

Обратите внимание, что это не было проверено раньше.

DB::beginTransaction();

try {
    $csv = array_map('str_getcsv', file('data.csv'));

    // remove the first line
    array_shift($csv);

    // grab only the players uids and their positions
    $positions = array_flip(array_column($csv, 0));

    array_walk($positions, static function(&$position, $id) {
        $position = "WHEN {$id} THEN {$position}";
    });

    DB::update("UPDATE `scores` 
                SET `position` = CASE `player_uid` " . implode(' ', $positions) . " END 
                WHERE `player_uid` in (" . implode(',', array_keys($positions)) . ") 
                  AND `session_uid` = ? 
                  AND `day` = ?", [$season_uid, $day]);

    DB::commit();

    return true;

} catch (\Exception $e) {
    Log::error($e);
    DB::rollBack();

    return false;
}

PS: Было бы неплохо написать комментарий об изменении производительности при таком подходе

0 голосов
/ 11 июня 2019

MySQL не поддерживает массовые обновления, но есть хитрый способ заменить обновления вставками с помощью предложения ON DUPLICATE KEY UPDATE . Таким образом, вы можете обновлять свои записи в больших количествах. Проверьте этот ответ для некоторых примеров .

Насколько мне известно, Laravel не поддерживает это предложение в построителе запросов, поэтому вам придется сгенерировать запрос вручную и выполнить его с помощью DB::statement(). Обязательно разбейте входящие строки на части (например, на 100), и вы увидите заметное повышение скорости.

Но вы должны понимать, что обновление 8k строк не является дешевой операцией. Рекомендуется делегировать его на отдельное задание и настроить очереди в приложении, чтобы ваши работники могли обрабатывать эти обновления в фоновом режиме. Подробнее о заданиях и очередях вы можете прочитать в официальной документации .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...