chunk(), lazy() и cursor() в Laravel: замеры памяти и почему chunk() теряет половину строк при обновлении

nikita-practice 15 минут назад Краткое содержание статьи chunk(), lazy() и cursor() в Laravel: замеры памяти и почему chunk() теряет половину строк при обновлении 8 мин 219 PHP * Laravel * Программирование *...
Will Anthropic have the best AI model at the end of October 2026?
Значимый прорыв формирует отрасль ИИ: nikita-practice 15 минут назад Краткое содержание статьи chunk(), lazy() и cursor() в Laravel: замеры памяти и почему chunk() теряет половину строк при обновлении 8 мин 219 PHP * Laravel * Программирование * Высоконагруженные системы * Когда в Laravel нужно обработать большую таблицу, обычно советуют «не делать all(), использовать chunk()». Совет верный, но неполный: у chunk() есть ловушка, из-за которой фоновая задача тихо обрабатывает только часть строк, без ошибок и предупреждений. Ниже замеры пяти способов обхода таблицы на 100 000 строк, разбор того, откуда берётся потеря строк, и правила выбора метода.
1, файловая база;таблица orders: id, status (string), payload (text, 200 символов), amount (integer), 100 000 строк;каждый режим запускается отдельным процессом PHP; память — memory_get_peak_usage() минус memory_get_usage() перед обходом (после memory_reset_peak_usage());запросы считаются через DB::listen(); в счётчик входит один контрольный count() в конце. Время зависит от машины и СУБД, его стоит воспринимать как относительное. Число запросов и число обработанных строк от железа не зависят.
Технические детали
Скрипт целиком приведён в конце статьи. Чтение: сколько памяти стоит каждый способМодель минимальная:use Illuminate\Database\Eloquent\Model; class Order extends Пять вариантов обхода всех строк:// 1. Всё в одну коллекцию foreach (Order::all() as $order) { /* ...
Пачками через LIMIT/OFFSET Order::query()->orderBy('id ->chunk(1000, function ($orders) { /* ... Пачками через WHERE id > последний_id Order::query()->chunkById(1000, function ($orders) { /* ... LazyCollection поверх пачек (lazy — OFFSET, lazyById — по ключу) foreach (Order::query()->lazy(1000) as $order) { /* ...
*/ } foreach (Order::query()->lazyById(1000) as $order) { /* ... Один запрос, модели создаются по одной foreach (Order::query()->cursor() as $order) { /* ... */ } Результаты:МетодСтрокЗапросовВремяПик памятиall()100 00021,52 с+136,0 МБchunk(1000)100 0001020,99 с+2,4 МБchunkById(1000)100 0001020,76 с+2,4 МБlazy(1000)100 0001020,98 с+2,4 МБlazyById(1000)100 0001020,78 с+2,4 МБcursor()100 00020,89 с+1,1 МБЧто из этого следует:all() держит в памяти все 100 000 моделей сразу: 136 МБ на таблицу, где полезных данных около 20 МБ.
Отраслевые последствия
Каждая модель Eloquent хранит массивы attributes и original, плюс служебные свойства. При стандартном memory_limit = 128M этот скрипт упал бы. Пакетные методы делают 101 запрос на выборку: 100 полных страниц и последнюю пустую, по которой цикл понимает, что данные кончились.
Память не растёт с размером таблицы, она определяется размером пачки. Варианты «по ключу» на этом стенде быстрее вариантов с OFFSET. Причина в том, что OFFSET 99000 заставляет СУБД пройти и выбросить 99 000 строк, а WHERE id > ?
сразу идёт по индексу первичного ключа.
Этот прогресс даёт важные сигналы о будущем отрасли, и технологический мир внимательно наблюдает.





