Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Чому chunk() пропускає записи під час оновлення і що використати замість нього?

chunk() гортає сторінки через OFFSET. Якщо в циклі змінювати колонку, за якою фільтрується запит, записи «з'їжджають», і частину буде пропущено.

// Погано: після першої порції активних стало на 100 менше,
// а друга порція бере OFFSET 100 - і перескакує через 100 записів
User::where('active', false)->chunk(100, function ($users) {
    $users->each->update(['active' => true]);
});

// Добре: курсор за id, а не номер сторінки
User::where('active', false)->chunkById(100, function ($users) {
    $users->each->update(['active' => true]);
});

chunkById() бере наступну порцію за id > останній, тож зміни в уже обробленому не впливають.

Як обирати інструмент для великих обсягів:

  • chunkById() / lazyById() - порціями з постійною пам'яттю; lazyById() віддає LazyCollection, з якою зручніше працювати як з потоком;
  • cursor() - один запит і по одній моделі в пам'яті, але драйвер бази часто буферизує весь результат, а жадібне завантаження зв'язків неможливе;
  • масовий update() - якщо логіка вміщається в SQL, один запит UPDATE ... WHERE у сотні разів швидший за будь-який цикл (але без подій моделей).

Що ще важливо на мільйонах рядків:

  • обробку виносять у чергу порціями, щоб падіння на 900-тисячному записі не починало все спочатку;
  • запит між порціями має йти за індексом;
  • вимкнений журнал запитів (DB::disableQueryLog()), щоб пам'ять не росла.

Докладніше в документації: Розбиття результатів на частини

Перевір себе

20 випадкових питань за спробу, після завершення - розбір кожної помилки

Схожі питання