• Время чтения ~1 мин
  • 27.02.2023

Иногда необходимо обновить данные в базе данных.  Самая простая возможность — просто запустить обновление в базе данных MySQL. Это не всегда работает. Особенно, когда вы используете события или вы также хотите возобновить отношения ....

Команды

В этом случае я рекомендую создать Command. Даже для разовых изменений.

php artisan make:command YourCommandName

Индикатор прогресса

Первым советом будет использование индикатора выполнения. В долгосрочных командах полезно видеть, что прогресс есть.

Чтобы показать вам, как я просто копирую пример из Laravel Documentation.

$users = App\Models\User::all();

$bar = $this->output->createProgressBar(count($users));

$bar->start();

foreach ($users as $user) {
    $this->performTask($user);

    $bar->advance();
}

$bar->finish();

Он

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

Проблема в том, что если вы загрузите все в свою красноречивую коллекцию, ваш ягненок будет ограничением. Чтобы избежать этого, вы можете использовать встроенную часть функции Laravel в своих запросах для последовательного повторения таблицы.

App\Models\User::chunk(200, function ($users){
    foreach($users as $user){
        $user->name .= ' :)';
        $user->save();
    }
});

Одна важная вещь, которую следует понимать в функции фрагмента, заключается в том, чтобы понять, как выполняются запросы. В этом примере после итерации 200 пользователей базовый запрос снова выполняется с функцией LMIT в таблице. 

Представьте, что у вас этот случай

App\Models\User::where('active', true)
    ->chunk(200, function ($users){
        foreach($users as $user){
            $user->active = false;
            $user->save();
        }
    });

В этом коде он превысит 200 пользователей, изменив активное значение на false.  Во втором запуске он снова запросит базу данных для пользователей, у которых есть активная правда. Проблема в том, что, поскольку мы только что изменили активный статус 200 пользователей, мы бы получили список без них. Но функция Limit ограничит результат от 200 до 400 в результатах. Это означает, что нам будет не хватать 200 пользователей, которых мы действительно хотели изменить.

Laravel имеет функцию преодоления проблемы, просто важно понимать, когда его использовать. Так что решение в данной ситуации будет.

App\Models\User::where('active', true)
    ->chunkById(200, function ($users){
        foreach($users as $user){
            $user->active = false;
            $user->save();
        }
    });

Транзакции базы данных

Теперь мы можем вносить много изменений в наши модели, и мы избегаем проблемы того, что наши красноречивые коллекции становятся слишком большими. 

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

Это позволяет нам повторно использовать наши части и обновлять БД до фрагмента.

App\Models\User::where('active', true)
    ->chunkById(200, function ($users){
        try {
            DB::beginTransaction();
            
            foreach($users as $user){
                $user->active = false;
                $user->save();
            }
           
            DB::commit();

        } catch (\Exception $e) {
            //handle your error (log ...)
            DB::rollBack();
        }
    });

В этом примере кода мы объединяем chunkById с транзакциями базы данных. Это может сэкономить много времени на обновление базы данных. Подробнее о транзакциях Database можно прочитать в документации Laravel.

Транзакции могут вызвать проблемы, если они используются неправильно. Если вы забудете сделать или откатитесь назад, вы создадите вложенные транзакции. Вы можете прочитать больше в Blogpost

Объедините его вместе

Чтобы завершить этот пример кода, мы можем повторно ввести индикатор

$count = App\Models\User::where('active', true)->count();

$bar = $this->output->createProgressBar($count);
$bar->start();

App\Models\User::where('active', true)
    ->chunkById(200, function ($users){
        try {
            DB::beginTransaction();
            
            foreach($users as $user){
                $user->active = false;
                $user->save();
                $bar->advance();
            }
           
            DB::commit();

        } catch (\Exception $e) {
            //handle your error (log ...)
            DB::rollBack();
            $bar->finish();
        }
    });

$bar->finish();

выполнения. Итак, это моя стратегия обработки обновлений больших наборов данных. Вы можете изменить размер фрагмента в соответствии с вашими потребностями и экспериментами, что принесет вам хорошие результаты. По моему опыту, что-то из 200 - 1000 - это нормально.

Иногда, особенно когда расчет для одной записи усложняется, я вижу, что весь процесс замедляется после каждой обработки. Он начинается примерно с 2 секунд на перекладине, чтобы двигаться до 30 или 40 секунд. Поскольку я испытал это в разных командах, я не уверен, что это общая тема. Если у кого-то есть какая-либо информация об этом, дайте мне знать.

Я надеюсь, что эта статья поможет вам.

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

Про мене

Professional Fullstack Developer with extensive experience in website and desktop application development. Proficient in a wide range of tools and technologies, including Bootstrap, Tailwind, HTML5, CSS3, PUG, JavaScript, Alpine.js, jQuery, PHP, MODX, and Node.js. Skilled in website development using Symfony, MODX, and Laravel. Experience: Contributed to the development and translation of MODX3 i...

Об авторе CrazyBoy49z
WORK EXPERIENCE
Контакты
Ukraine, Lutsk
+380979856297