MODX под нагрузкой: как ускорить вывод страниц
На тяжёлой странице MODX тормозит не из-за одного медленного сниппета. Запросы к базе, обработка TV-полей, некешируемые вызовы и слишком большой HTML складываются в очередь и первыми проявляются под нагрузкой.

Каталог на MODX может открываться приемлемо для одного посетителя и начать отдавать 502 под параллельными запросами. Обычно причина не в самом шаблонизаторе, а в цепочке вывода: сниппет выбирает лишние записи, для каждой записи читает TV-поля, затем повторно обрабатывает плейсхолдеры и обходит кеш. Разбирать такую страницу нужно по этапам — от SQL и генерации списка до размера ответа и поведения PHP-FPM.
Что именно тормозит вывод в MODX
Нагрузка возникает не только в момент чтения страницы из базы. MODX Revolution собирает документ из шаблона, чанков, сниппетов и плейсхолдеров. Каждый некешируемый вызов проходит этот путь заново.
- Выборка ресурсов. Сниппет получает больше записей, чем нужно странице, или сортирует их по полю без подходящего индекса.
- TV-поля. Изображения, цены, списки и дополнительные параметры обрабатываются для каждой карточки. Десятки карточек превращают одну выборку в серию дополнительных операций.
- Повторный парсинг. Вызов сниппета внутри чанка или вложенного цикла может запускать обработку MODX-тегов для каждого элемента.
- Обход кеша. Вызов с восклицательным знаком, например
[[!pdoResources]], принудительно делает результат некешируемым. - Большой ответ. Даже быстрый SQL не спасает страницу, если сервер генерирует несколько тысяч карточек, а браузер получает мегабайты HTML.
Главный вопрос под нагрузкой — не «какой сниппет быстрее», а «сколько работы MODX выполняет для одного запроса и почему эта работа повторяется».
Сноска. TV-поле (Template Variable) — дополнительное поле ресурса MODX: например, изображение, бренд или цена. Внутри оно хранится отдельно от основных полей документа, поэтому массовая обработка TV требует отдельного внимания.
Сначала измерьте полный путь запроса
Не начинайте с замены одного сниппета на другой. Зафиксируйте исходное состояние страницы: время до первого байта, полное время ответа, размер HTML, число SQL-запросов и пиковое потребление памяти PHP. Один замер в браузере недостаточен: он смешивает серверное время, сеть и работу JavaScript.
Что проверять до изменений
- Откройте страницу без браузерного кеша. Сравните первый запрос и повторный. Если второй заметно быстрее, часть проблемы связана с кешированием, а не с HTML.
- Посмотрите журнал MODX. Ошибки соединения с базой, переполнение памяти и медленные внешние вызовы важнее косметической оптимизации шаблона.
- Проверьте SQL-профиль. Найдите запросы с большим числом строк, сортировкой и повторяющимися обращениями к одной таблице.
- Снимите нагрузочный сценарий. Используйте тот же URL, размер страницы и число параллельных запросов, что и в рабочей ситуации. Не сравнивайте результаты разных страниц.
В качестве результата нужен не абстрактный показатель «стало быстрее», а таблица до и после: время ответа, число запросов, размер ответа, память и доля ошибок. Если инструмент профилирования не показывает источник задержки, сначала настройте сбор этих данных, а не меняйте код вслепую.
Уменьшите объём выборки
Для MODX Revolution 2.8.x и 3.x принцип одинаков: запрос должен выбирать только те ресурсы и поля, которые попадают на текущую страницу. Для списков ресурсов чаще используют pdoTools, например сниппет pdoResources. Он не отменяет ограничение выборки: параметр limit должен соответствовать числу карточек, которое действительно выводится.
[[pdoResources?
&parents=`12`
&limit=`24`
&page=`[[+page]]`
&tpl=`catalog.card`
&includeTVs=`image,price`
&processTVs=`1`
&sortby=`publishedon`
&sortdir=`DESC`
]]
Это пример для установленного pdoTools, а не встроенная возможность каждой сборки MODX. Проверьте версию pdoTools и описание свойств в документации перед переносом на другой проект.
Опасный вариант — выбрать сотни ресурсов, а затем показать первые 24 через условие в чанке. В таком случае база и PHP уже обработали лишние записи. Пагинация должна ограничивать запрос, а не только скрывать элементы в HTML.
Сравните подходы к выводу каталога
| Подход | Что происходит | Цена решения | Когда выбирать |
|---|---|---|---|
| getResources без TV | Простая выборка ресурсов и полей документа | Меньше данных, но дополнительные поля нужно получать отдельно | Для коротких списков и простого вывода |
| pdoResources с limit и пагинацией | Выборка ограничивается на уровне запроса | Нужно проверить совместимость чанков и пагинации | Для каталогов и архивов с большим числом ресурсов |
| pdoResources с обработкой TV | В список добавляются значения TV-полей | Больше работы с данными и выше риск тяжёлой выборки | Когда карточке действительно нужны эти поля |
| Готовый кешированный HTML | Сниппет не выполняется на каждом запросе | Нужно продумать очистку кеша после изменений | Для публичных списков, меняющихся не каждую секунду |
Не включайте в список все TV «на будущее». Оставьте только поля, которые используются в конкретном чанке. Если карточке нужна ссылка на изображение, не передавайте ей одновременно описание, галерею и служебные параметры.
Уберите дорогую работу из цикла карточек
Самая частая архитектурная ошибка — вызов сниппета внутри чанка ресурса. Например, каждая карточка отдельно ищет связанные товары, формирует меню или обращается к внешнему API. При 24 карточках одна страница запускает эту операцию 24 раза.
Сначала получите список идентификаторов, затем загрузите связанные данные одной групповой операцией или заранее подготовьте их. Если групповой запрос невозможен, ограничьте число связанных объектов и кешируйте результат. Внешний API не должен быть обязательным этапом генерации публичной страницы: его недоступность иначе превращается в задержку или ошибку всего сайта.
Особенно осторожно используйте некешируемые вызовы:
[[!pdoResources?
&parents=`12`
&limit=`24`
&tpl=`catalog.card`
]]
Восклицательный знак нужен, когда результат зависит от текущего пользователя, сессии или других динамических данных. Для общего публичного каталога он часто лишает страницу одного из главных преимуществ MODX — кеширования. Убирать его можно только после проверки, что в выводе нет персональных данных.
Кешируйте не всё подряд, а стабильные участки
Кеширование списка товаров помогает, если одинаковый HTML могут получить разные посетители. Но у него есть цена: после публикации ресурса, изменения цены или изображения посетитель может увидеть старую версию до очистки кеша.
Разделите страницу на части:
- шапка, меню и общий список — кешируемые;
- корзина, имя пользователя и персональные рекомендации — динамические;
- редко меняющиеся подборки — с понятным сроком обновления или очисткой после публикации;
- результаты поиска и фильтров — с отдельными правилами кеша, потому что комбинаций параметров может быть очень много.
Не делайте всю страницу некешируемой из-за одного динамического блока. Обычно лучше вынести этот блок в отдельный запрос или клиентскую загрузку, если это не ухудшает доступность и индексацию.
Где оптимизация ломается
TV-поля начинают съедать выигрыш
После ограничения ресурсов страница может остаться медленной из-за обработки TV. Сравните вариант без TV, с одним полем и с полным набором. Если задержка растёт непропорционально числу карточек, ищите лишние TV и преобразования их значений.
Сортировка не соответствует индексу
Пагинация не гарантирует быструю работу. База всё равно может сортировать большой набор перед тем, как вернуть 24 строки. План выполнения запроса покажет, используется ли индекс. Названия индексов и команды зависят от СУБД и схемы, поэтому добавляйте их только после проверки конкретной базы.
Кеш очищается слишком широко
Если после каждого изменения удаляется весь кеш сайта, следующий поток посетителей одновременно пересобирает множество страниц. Проверьте, какие документы действительно зависят от изменённого ресурса. Для каталога с частыми обновлениями полезнее точечная стратегия, чем постоянная полная очистка.
Сервер упирается не в MODX
PHP-FPM может исчерпать workers, база — соединения, а диск — операции записи кеша. В этом случае замена сниппета даст небольшой эффект. Сопоставьте время ответа с загрузкой CPU, памятью, числом процессов PHP-FPM и активными соединениями базы.
Что делать дальше
Возьмите одну тяжёлую страницу и соберите для неё профиль до изменений. Затем последовательно отключите обработку TV, ограничьте выборку, уберите вложенные сниппеты и включите кеш для общего списка. После каждого шага повторяйте один и тот же нагрузочный сценарий.
В продакшене наблюдайте не только среднее время ответа, но и 95-й перцентиль, долю ответов 5xx, число SQL-запросов на страницу и время заполнения кеша после очистки. Если после этих изменений страница всё ещё упирается в базу, следующий шаг — разбор плана конкретных SQL-запросов и схемы индексов, а не дальнейшее усложнение чанков.
Создатель презентаций: Структура и текст каждого слайда: заголовки-утверждения, одна мысль на слайд, заметки докладчику.
Спросить за 150 ₽Источники: MODX Documentation: Caching, pdoTools на GitHub
Частые вопросы
Какая версия MODX указана в рекомендациях?
Почему pdoResources не ускорил страницу?
Можно ли убрать восклицательный знак из [[!pdoResources]]?
Что будет, если кеш устарел после изменения цены?
Нужно ли добавлять индексы в базу сразу?
Что проверить, если под нагрузкой появляются 502?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.