MySQL кэширование WordPress: полное руководство по кэшированию страниц
Введение
Кэширование полной 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.