MySQL кэширование WordPress: полное руководство по кэшированию страниц

white and blue printer paper

Введение

Кэширование полной HTML‑страницы на уровне базы данных позволяет сократить время генерации ответа до нескольких миллисекунд даже при высоких нагрузках. В отличие от традиционных плагинов, которые используют файловую систему или объектный кэш, хранение в MySQL даёт атомарный контроль, гибкую инвалидацию и возможность масштабировать кэш совместно с репликацией. В этой статье мы разберём, как построить надёжный кэш‑слой, какие таблицы создать, как сохранять и вытаскивать HTML, а также как автоматически сбрасывать устаревшие записи с помощью триггеров.

Планирование кэша в MySQL

Выбор стратегии хранения

Существует два основных подхода: «ключ‑значение» (одна строка хранит URL и сжатый HTML) и «таблица‑версии» (каждая версия страницы хранится отдельно). Для большинства сайтов достаточно первого варианта, потому что запросы к кэшу происходят по точному совпадению URL.

Оценка объёма и TTL

Рассчитайте примерный объём кэша: средний размер страницы ≈ 120 KB, планируемый объём кэша ≈ 10 000 страниц → ≈ 1.2 GB. При этом TTL (time‑to‑live) обычно ставят от 5 до 30 минут, в зависимости от частоты обновления контента. Не забудьте добавить индекс по полю url_hash для быстрого поиска.

Реализация: таблицы и функции

Создание кэш‑таблицы

Ниже пример SQL‑скрипта, который создаёт таблицу wp_page_cache с полями для URL‑хеша, HTML‑контента, времени создания и срока жизни.

CREATE TABLE IF NOT EXISTS wp_page_cache ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, url_hash CHAR(32) NOT NULL, html MEDIUMBLOB NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, ttl INT UNSIGNED NOT NULL DEFAULT 1800, INDEX idx_url_hash (url_hash) ) ENGINE=InnoDB ROW_FORMAT=COMPRESSED; 

Функция сохранения кэша

Эту функцию удобно разместить в файле wp-content/mu-plugins/db-page-cache.php, чтобы она была доступна независимо от активных плагинов.

function wp_db_cache_set( $url, $html, $ttl = 1800 ) { global $wpdb; $table = $wpdb->prefix. 'page_cache'; $hash = md5( $url ); $data = array( 'url_hash' => $hash, 'html' => $html, 'ttl' => $ttl, 'created_at' => current_time( 'mysql' ), ); $format = array('%s','%s','%d','%s'); // Попытка обновить, если запись уже существует $updated = $wpdb->replace( $table, $data, $format ); return $updated!== false; } 

Функция получения кэша

При каждом запросе WordPress проверяем наличие свежей записи. Если запись найдена и ещё не просрочена – сразу выводим её и завершаем работу.

function wp_db_cache_get( $url ) { global $wpdb; $table = $wpdb->prefix. 'page_cache'; $hash = md5( $url ); $row = $wpdb->get_row( $wpdb->prepare( "SELECT html, created_at, ttl FROM $table WHERE url_hash = %s", $hash ) ); if ( $row ) { $age = strtotime( current_time( 'mysql' ) ) - strtotime( $row->created_at ); if ( $age ttl ) { // Кеш живой – выводим и останавливаем дальнейшую обработку echo $row->html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped exit; } } return false; } 

Инвалидация кэша по триггерам

Почему нужны триггеры

При изменении контента (пост, страница, продукт в WooCommerce) кэш устаревает. Вместо ручного сброса можно создать MySQL‑триггер, который удалит все строки, связанные с изменённым URL.

Триггер на обновление постов

Следующий код удаляет кэш для URL, построенного из пермалинка изменённого поста.

DELIMITER $$ CREATE TRIGGER trg_wp_post_update AFTER UPDATE ON wp_posts FOR EACH ROW BEGIN IF NEW.post_status = 'publish' THEN DECLARE _url VARCHAR(255); SET _url = CONCAT( 'https://', SUBSTRING_INDEX( @@hostname, '/', 1 ), '/', NEW.post_name, '/' ); DELETE FROM wp_page_cache WHERE url_hash = MD5( _url ); END IF; END$$ DELIMITER ; 

Триггер для WooCommerce

Если вы используете WooCommerce, добавьте аналогичный триггер для таблицы wp_posts, где post_type='product'. Это гарантирует, что изменение цены или наличия мгновенно сбросит кэш.

Оптимизация под high load

Индексы и сжатие

Для ускорения выборки убедитесь, что колонка url_hash имеет отдельный B‑tree индекс, а таблица использует ROW_FORMAT=COMPRESSED. Это уменьшит размер данных на диске и ускорит I/O.

Разделение чтения и записи

При нагрузке > 5 000 запросов/сек рекомендуется вынести кэш‑таблицу на реплику‑слейв и направлять запросы SELECT только туда. Записи (INSERT/REPLACE) остаются на мастере, а реплика‑слейв будет обслуживать быстрый read‑only доступ.

Пакетная очистка старых записей

Запланируйте EVENT в MySQL, который будет удалять устаревшие строки раз в 10 минут. Это избавит от скопления «мертвого» кэша.

CREATE EVENT IF NOT EXISTS ev_cleanup_page_cache ON SCHEDULE EVERY 10 MINUTE DO DELETE FROM wp_page_cache WHERE TIMESTAMPDIFF(SECOND, created_at, NOW()) > ttl; 

Совместимость с профилированием

Для контроля влияния кэша используйте Blackfire профилирование. Оцените снижение времени генерации до 0.02 с и убедитесь, что нагрузка на базу не превышает 2 % от общего CPU.

Заключение

Кэширование полной HTML‑страницы в MySQL — мощный инструмент для сайтов с высоким трафиком. Правильно спроектированная таблица, функции вставки/чтения и автоматическая инвалидация через триггеры позволяют сократить время отклика до долей секунды и обеспечить стабильность при нагрузке до 10 000 запросов в секунду. Не забывайте о репликации, сжатии и периодической очистке, а также проверяйте эффективность с помощью WebP‑отдачи и Telegram‑бота для мгновенных уведомлений о сбоях. Следуя этому руководству, вы получаете надёжный кэш‑слой, который выдержит любой пик нагрузки.

❓ Часто задаваемые вопросы

Какой размер таблицы оптимален для кэша?

Оптимальный размер зависит от количества уникальных URL и среднего объёма страницы. При 10 000 страниц по 120 KB таблица будет около 1,2 GB, что удобно для InnoDB с компрессией.

Можно ли использовать Redis вместо MySQL?

Да, Redis обеспечивает более быстрый доступ, но MySQL удобнее, если уже настроена репликация и требуется атомарность транзакций вместе с другими данными сайта.

Как гарантировать, что кэш сбрасывается после изменения поста?

Создайте триггер AFTER UPDATE на таблицу wp_posts, который удаляет запись из кэш‑таблицы по хешу URL. Это автоматизирует инвалидацию без вмешательства кода.

Стоит ли включать кэширование для админ‑панели?

Не рекомендуется кэшировать страницы админки, так как они часто меняются и содержат динамические данные. Ограничьте кэширование только публичными URL.