Как произвести чтение больших файлов в PHP (и не угробить сервер)
От автора: PHP разработчики редко заботятся об управлении памятью. PHP движок превосходно выполняет свою работу и подчищает за нами. Серверная модель кратковыполняемого контекста значит, что даже самый плохой код имеет долгосрочный эффект.
Нам мало когда необходимо выходить за эти комфортные рамки. Например, когда мы пытаемся запустить Composer в большом проекта на минимальном VPS, или когда необходимо произвести в PHP чтение большого файла на все таком же маленьком сервере.
В этом уроке мы обсудим последнюю проблему. Код к уроку можно найти на GitHub.
Измеряем успех
Единственный способ понять, что мы что-то улучшили в коде, это измерить плохой участок, после чего сравнить эти измерения после фикса. Другими словами, если мы не знаем, насколько «решение» помогло нам (если вообще помогло), мы не можем утверждать, что это вообще решение.
Нас заботят два фактора. Первый – потребление CPU. С какой скоростью работает процесс, над которым мы будем работать? Второй фактор – потребление памяти. Сколько памяти выделяется на выполнение скрипта? Зачастую эти два фактора обратно пропорциональны – т.е. мы можем разгрузить память за счет CPU и наоборот.
Онлайн курс «PHP-разработчик»
Изучите курс и создайте полноценный проект — облачное хранилище файлов
С нуля освоите язык программирования PHP, структурируете имеющиеся знания, а эксперты помогут разобраться с трудными для понимания темами, попрактикуетесь на реальных задачах. Напишете первый проект для портфолио.
В асинхронной модели выполнения (многопроцессовые или многопоточные приложения PHP) потребление CPU и памяти важные факторы. В стандартной архитектуре PHP они становятся проблемой, когда один из факторов достигает ограничений сервера.
Внутри PHP измерять потребление CPU непрактично. Если вы хотите сосредоточиться на этой области, попробуйте использовать что-то типа top, Ubuntu или macOS. В Windows попробуйте использовать Linux Subsystem, чтобы использовать top в Ubuntu.
В рамках урока мы будем измерять потребление памяти. Мы посмотрим, сколько памяти используется в «обычных» скриптах. Проведем пару стратегий оптимизации и измерим их. В конце я хочу, чтобы вы могли делать образованный выбор.
Методы, которые мы будем использовать для измерения потребления памяти:
Эти функции мы будем вызывать в конце скриптов и смотреть, какой скрипт использует больше всего памяти.
Какие у нас варианты?
Эффективно читать файлы можно множеством разных способов. Также есть 2 вероятных сценария их использования. Нам может понадобиться считывать и обрабатывать все данные одновременно, выводить обработанные данные или выполнять другие действия на основе считанного. Также нам может понадобиться трансформировать поток данных без необходимости получать к нему доступ.
Представим, что для первого сценария мы хотим иметь возможность читать файл и создавать отдельные очереди фоновых задач каждые 10 000 строк. Нам понадобится хранить в памяти минимум 10 000 строк и передавать их в менеджер очереди фоновой задачи (какую бы форму она не принимала).
Для второго сценария представим, что мы хотим сжать контент определенного большого ответа от API. Нам неважно что в ответе, но нам необходимо, чтобы он был в сжатой форме.
В обоих сценариях нам необходимо читать большие файлы. В первом нам нужно знать, что в данных. Во втором нам неважно, что в данных. Разберем эти варианты…
Чтение файлов построчно
Для работы с файлами существует множество функций. Давайте соединим парочку функций в нативный файл ридер: