Laravel кусок и удалить - PullRequest
       7

Laravel кусок и удалить

0 голосов
/ 24 сентября 2018

У меня есть большое количество элементов (1M +), которые я хочу удалить из базы данных. Я разбираюсь с фоновым заданием, чтобы позаботиться об этом, чтобы пользователю не пришлось ждать, пока оно завершится, чтобы продолжитьчто бы он / она ни делал, проблема в том, что приложение перестает отвечать на запросы при удалении элементов, поэтому я подумал, что обработаю кусок элементов по частям и посплю пару секунд, а затем продолжу.

Вот код, который обрабатывает удаление:

// laravel job class
// ...
public function handle()
{
    $posts_archive = PostArchive::find(1); // just for the purpose of testing ;)
    Post::where('arch_id', $posts_archive->id)->chunk(1000, function ($posts) {
        //go through the collection and delete every post.
        foreach($posts as $post) {
            $post->delete();
        }
        // throttle
        sleep(2);
    });
}

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

Фактический результат : случайное количество элементов удаляется один раз, затем процесс заканчивается.нет ошибок нет индикаторов, нет подсказки?

Есть ли лучший способ реализовать это?

Ответы [ 3 ]

0 голосов
/ 24 сентября 2018

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

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

do {
    $deleted = Post::where('arch_id', $posts_archive->id)->limit(1000)->delete();
    sleep(2);
} while ($deleted > 0);
0 голосов
/ 10 октября 2018

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

Laravel разбивает на страницы через ваш набор данных по одной странице за раз и пропускаетколлекция Post моделей для вашего обратного вызова.

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

Возьмите следующий сценарий - есть 24 записей , которые вы хотите удалить в кусках по 10 :

Ожидаемый

+-------------+--------------------+---------------------------+
|  Iteration  |   Eloquent query   | Rows returned to callback |
+-------------+--------------------+---------------------------+
| Iteration 1 | OFFSET 0 LIMIT 10  |                        10 |
| Iteration 2 | OFFSET 10 LIMIT 10 |                        10 |
| Iteration 3 | OFFSET 20 LIMIT 10 |                         4 |
+-------------+--------------------+---------------------------+

Фактический

+-------------+--------------------+----------------------------+
|  Iteration  |   Eloquent query   | Rows returned to callback  |
+-------------+--------------------+----------------------------+
| Iteration 1 | OFFSET 0 LIMIT 10  |                         10 | (« but these are deleted)
| Iteration 2 | OFFSET 10 LIMIT 10 |                          4 |
| Iteration 3 | NONE               |                       NONE |
+-------------+--------------------+----------------------------+

После 1-й итерации осталось только 14 записей, поэтому, когда Laravel получил страницу 2, оннайдено только 4 записи.

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

Другое решениек проблеме горе* * * * * * * * * * При использовании курсора используйте для обработки вашего запроса курсор, при этом будет проходить по 1 записи из набора результатов БД за раз, что лучше для использования памяти.

Например,

// laravel job class
// ...
public function handle()
{
    $posts_archive = PostArchive::find(1); // just for the purpose of testing ;)
    $query = Post::where('arch_id', $posts_archive->id);

    foreach ($query->cursor() as $post) {
        $post->delete();
    }
}

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

0 голосов
/ 24 сентября 2018

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

попробуйте получить минимальный и максимальный значения post.id, а затем разделите их на такие, как

for($i = $minId; $i <= $maxId-1000; $i+1000) {
    Post::where('arch_id', $posts_archive->id)->whereBetween('id', [$i, $i+1000])->delete();
    sleep(2);
}

, настройте блок и сонпериод, в зависимости от ресурсов вашего сервера.

...