Оптимізація продуктивності MODX
MODX Revolution — гнучка CMS, але без правильної настройки кешування та конфігурації сервера вона легко упирається в TTFB 2–4 секунди навіть на простих сайтах. Причина — агресивна робота з базою даних, парсинг тегів [[*]]/[[+]] на кожен запит та відсутність вбудованого об'єктного кешу.
Де теряється час: профайлювання запитів
Перший крок — зрозуміти, що саме сповільнює. MODX логує повільні запити, якщо увімкнути в core/config/config.inc.php:
define('MODX_CONFIG_KEY', 'config');
// у System Settings:
// log_level = 4 (DEBUG)
// log_target = FILE
Більш точний інструмент — xDebug + Blackfire або просто EXPLAIN у MySQL/MariaDB для запитів, які MODX генерує при рендерингу сторінки. Типові проблеми:
-
modResourceчитає всі поля включаючиcontentнавіть коли потрібен лишеpagetitle -
getResourcesбезlimitробитьSELECT *по всім дочірнім ресурсам - сніпети без
&cache=1виконуються на кожен запит
Кешування на рівні MODX
Системний кеш зберігається в core/cache/. Для production переконайтесь, що директорія має права 755 і не монтується через tmpfs без достатнього розміру.
Оптимальні налаштування в System Settings:
| Параметр | Рекомендоване значення |
|---|---|
cache_resource_handler |
xPDOFileCache (за замовчуванням) або Redis |
cache_default_lifetime |
3600–86400 залежно від частоти оновлень |
cache_context_settings |
1 |
compress_js |
1 |
compress_css |
1 |
Для Redis-кешу встановлюється пакет Redis (modmore або аналог) і в core/config/config.inc.php додається:
$config_options = [
'cache_path' => MODX_CORE_PATH . 'cache/',
'cache_default_handler' => 'xPDORedisCache',
'cache_xpdo_handler' => 'xPDORedisCache',
'redis_host' => '127.0.0.1',
'redis_port' => 6379,
'redis_db' => 0,
];
Після переключення на Redis TTFB знижується на 30–50% на сайтах з високим трафіком, тому що відпадають дискові операції на stat() файлів кешу.
Оптимізація сніпетів та чанків
Некешовані виклики [[!SnippetName]] — головне джерело медлительності. Аудит через пошук за шаблонами та чанками:
grep -r '\[\[!' /var/www/modx/core/elements/ | grep -v '.svn'
# або в templates через MODX manager: Elements > Templates > Search
Правила:
- Якщо сніпет не залежить від сесії/корзини/користувача — робимо кешований
[[SnippetName? &cache=1&cacheExpires=3600]] -
getResources,pdoResourcesкешуються через вбудований параметр&cache -
FormItтаLogin— завжди некешовані, виносити в окремі чанки
pdoTools замість getResources — критично для сайтів з великими каталогами. pdoTools використовує JOIN замість множественних запитів:
[[pdoResources?
&parents=`15`
&depth=`2`
&limit=`20`
&sortby=`publishedon`
&sortdir=`DESC`
&cache=`1`
&cacheExpires=`1800`
]]
Настройка PHP-OPcache та конфігурація PHP
; /etc/php/8.1/fpm/conf.d/10-opcache.ini
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.validate_timestamps=0 ; на production — вимкнути
opcache.revalidate_freq=0
opcache.fast_shutdown=1
PHP-FPM pool для MODX:
[modx]
pm = dynamic
pm.max_children = 20
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 8
pm.max_requests = 500
request_terminate_timeout = 30s
pm.max_requests = 500 — запобігає витокам пам'яті в довгоживучих сніпетах.
Nginx + статика + gzip
server {
# Статика з довгим кешем
location ~* \.(js|css|png|jpg|webp|woff2|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# Запрет прямого доступу до core
location ^~ /core/ {
deny all;
}
# gzip
gzip on;
gzip_types text/plain text/css application/json application/javascript image/svg+xml;
gzip_min_length 1024;
gzip_comp_level 5;
}
Індекси бази даних
MODX не додає всі потрібні індекси автоматично. Для сайтів із 10 000+ ресурсів додаємо:
-- Прискорює вибір за батьком + статусом
ALTER TABLE modx_site_content
ADD INDEX idx_parent_published (parent, published),
ADD INDEX idx_context_published (context_key, published);
-- Для сортування за датою
ALTER TABLE modx_site_content
ADD INDEX idx_publishedon (publishedon);
Після додавання індексів EXPLAIN SELECT на типовому запиті getResources показує ref замість ALL.
Терміни робіт
Базова оптимізація (кеш, PHP-FPM, nginx, індекси): 2–3 дні. Переведення некешованих сніпетів на кешовані + аудит шаблонів: 3–5 днів залежно від кількості елементів. Перехід на Redis-кеш з тестуванням: 1 день додатково.







