<? phpukraine СПІВБЕСІДИ
Пошук по платформі
WORDPRESS · MIDDLE

Як пришвидшити WP-сайт із важким каталогом?

Спершу профілювання через Query Monitor, потім persistent object cache у Redis, правильні аргументи WP_Query, індекси або власні таблиці замість meta_query і повне кешування сторінок на рівні nginx.

Чому сторінка каталогу з фільтрами відкривається 6 секунд?
Чим transients відрізняються від object cache?
З чого почати оптимізацію WooCommerce на 50 000 товарів?
продуктивність object cache WP_Query

Оптимізація починається з вимірювання. Query Monitor показує кожен SQL-запит сторінки з часом і стеком викликів, і в каталозі майже завжди винен один тип запиту: WP_Query з meta_query по кількох ключах. Таблиця wp_postmeta має EAV-структуру, кожен ключ у фільтрі додає JOIN на її копію, а індексу по meta_value немає. На 50 000 товарів такий запит триває секунди.

Далі йдуть три рівні. Перший — persistent object cache у Redis: без нього WordPress на кожен запит перечитує опції, мету й терми, а transients живуть у wp_options і самі стають повільними запитами. Другий — правильні аргументи WP_Query: no_found_rows там, де не потрібна пагінація, fields => ids для списків, вимкнені update_post_meta_cache і update_post_term_cache, коли мета не читається. Третій — структура даних: атрибути з обмеженим набором значень переносяться в таксономії, числові діапазони й багатовимірні фільтри у власну таблицю з індексами або в зовнішній пошуковий рушій.

Останній шар — повне кешування сторінок на nginx або Varnish для всього, що не персоналізоване, з інвалідацією по save_post. Кошик, чекаут і кабінет із цього кешу виключаються, а їхні дорогі фрагменти кешуються через wp_cache з контекстом у ключі.

// Повільно: meta_query по трьох ключах = три JOIN на wp_postmeta без індексу по meta_value
$q = new WP_Query([
    'post_type'  => 'product',
    'meta_query' => [
        ['key' => '_color', 'value' => 'red'],
        ['key' => '_size',  'value' => 'M'],
        ['key' => '_price', 'value' => [100, 500], 'compare' => 'BETWEEN', 'type' => 'NUMERIC'],
    ],
]);

// Швидше: атрибути в таксономіях, лише id, без підрахунку found_rows і зайвих кешів
$ids = (new WP_Query([
    'post_type'              => 'product',
    'fields'                 => 'ids',
    'posts_per_page'         => 24,
    'no_found_rows'          => true,   // без SQL_CALC_FOUND_ROWS
    'update_post_meta_cache' => false,
    'update_post_term_cache' => false,
    'tax_query'              => [
        ['taxonomy' => 'pa_color', 'field' => 'slug', 'terms' => 'red'],
        ['taxonomy' => 'pa_size',  'field' => 'slug', 'terms' => 'm'],
    ],
]))->posts;

// Дорогий результат у object cache (Redis), а не в wp_options
$facets = wp_cache_get('catalog_facets', 'shop');
if ($facets === false) {
    $facets = shop_build_facets();
    wp_cache_set('catalog_facets', $facets, 'shop', 10 * MINUTE_IN_SECONDS);
}
Що починаєте з вимірювання: Query Monitor або New Relic показують найповільніший запит, а не здогадки про кеш-плагіни.
Що головний ворог каталогу це meta_query по кількох ключах: wp_postmeta з EAV-структурою робить JOIN на кожен ключ і не має індексу по значенню.
Що persistent object cache у Redis чи Memcached кешує результати wp_cache і get_option між запитами, без нього WordPress перечитує опції та мету щоразу.
Що аргументи WP_Query мають значення: no_found_rows, fields => ids, update_post_meta_cache і update_post_term_cache вимкнені там, де не потрібні.
Що для фільтрів каталогу масштабується лише власна таблиця з індексами або зовнішній пошуковий рушій, а повне сторінкове кешування знімає навантаження з усього, що не персоналізоване.
Ставити ще один кеш-плагін замість пошуку повільного запиту.
Плутати transients і object cache: transient без persistent cache живе в wp_options і сам стає повільним запитом.
Використовувати posts_per_page => -1 і потім фільтрувати в PHP.
Робити фільтри каталогу через meta_query з пʼятьма ключами й дивуватись JOIN на пʼять копій wp_postmeta.
Кешувати сторінку цілком разом із кошиком чи іменем користувача й показувати чужі дані.
ПОРАДА

Скажіть, що починаєте з профілювання Query Monitor, а не з встановлення кеш-плагіна навмання. І назвіть, який саме запит зазвичай виявляється винним.

оновлено 3 вересня 2026 · ліцензія CC-BY-SA-4.0 Знайшли неточність? Напишіть →
ПЕРЕВІРТЕ СЕБЕ

Спочатку профілювання запитів, потім object cache і правильні аргументи WP_Query.