Table of Contents
1 О чём речь
Работая с сайтами на WordPress, я обнаруживаю промахи, допущенные на ранних стадиях создания. Получается так, что в сайтах изначально заложен непрочный фундамент, подводящий в наиболее неудачный момент — когда высока посещаемость. Из-за недостаточной продуманности архитектуры часть посетителей вместо контента получат ошибку 500, 502, 503 (их там много) или начнут страдать из-за медленной загрузки страниц.
Не обещаю, что охвачу все проблемы. Более того, могу где-то ошибиться — все мы люди. Тем не менее, даже если вы владелец готового на 100% проекта, имеет смысл прочитать то, о чём я пишу, и провести ревизию своих владений, чтобы выявить и устранить недостатки, доведя сайт до идеала.
Об уровне знаний
Вам нужно знать как минимум основы PHP. Запредельно сложных технических подробностей не будет, но и азы разъяснять не собираюсь. Рассчитываю на то, что вы хотя в общих чертах понимаете, как устроены сайты.
Блог, магазин, каталог, форум или фотоальбом — не важно, проблема со скоростью открытия страниц общая. Поэтому, чтобы не хлопать недоумённо глазами и не биться головой о стену, когда страницы начнут открываться с задержкой в полминуты, а то и дольше, нужно понимать внутреннюю «кухню» этой CMS. Без понимания ни из одного продукта нельзя выжать все возможности.
2 Вордпресс, у нас проблема
WordPress, на мой взгляд, — один из лучших инструментов для создания сайтов любого уровня: от простеньких блогов (как у меня) до сложных интернет-магазинов и сервисов. В данной CMS много полезных функций, которые при необходимости легко расширить плагинами или собственным кодом. Каждый, кто не поленится изучить WordPress Codex, сможет с минимальными денежными и временными затратами создать сайт, решающий поставленную бизнес-задачу.
Следует понимать, что Вордпресс разработан для создания блогов. Существование таких плагинов, как WooCommerce для магазинов и bbPress для форумов не означает, что с их помощью можно создать идеально работающий интернет-магазин или сообщество. Увы, ни один плагин не меняет ядро CMS, то есть любой сайт на WP — это по-прежнему блог.
Ориентация — мешает
WP не позволяет сделать быстро работающие интернет-магазины, форумы и новостные сайты со сложной рубрикацией. Система ориентирована на блоги.
Проблемы обнаруживаются не сразу, а спустя некоторое время, когда сайт наполнен контентом и набегают посетители. Точные свойства «критической массы» зависят от многих факторов, я их предсказывать не берусь. Это могут быть 10 000 пользователей в сутки и 2 000 постов, 20 тысяч посетителей и 100 постов — зависит от производительности сервера, контента, программного кода и много чего ещё. Симптомы: страницы начинают открываются с задержкой либо часть посетителей видит ошибки из серии 50x.
Так почему же происходит замедление? Почему сайты на WP так зависят от количества контента?
Как-никак это старая система, первая версия которой вышла аж в 2003 году, поэтому очевидно, что некоторые идеи реализованы архаично. Так получилось, что наиболее проблемный компонент — хранение контента в базе данных. С фотографиями, стилями, шрифтами и прочими файлами, хранящимися на диске, проблем нет, но всё, что касается БД, в WordPress работает так себе. Справедливости ради замечу: истинно универсальных решений не существует, так что ругать WP не следует. Мы же не ругаем велосипеды за то, что они не для автобанов?
Сайт на WordPress работает медленно?
Проблема может скрываться и в серверном софте, и в CMS — ядре, шаблонах, плагинах. Грамотная настройка сервера важна, но всегда будет потолок, который можно преодолеть только оптимизацией на уровне сайта.
Четыре технических проблемы с CMS, которые встретятся вам на пути развития проекта:
- Использование малого количества таблиц для совершенно разного контента.
- Использование сериализации данных там, где это не нужно.
- Отсутствие индексов.
- Низкое качество плагинов и шаблонов.
Симптом всегда один: медленная загрузка или, как уже писал выше, спонтанное появление ошибок с кодами пятисотой серии. Если понять, откуда растут ноги, с проблемами можно справиться.
Отказ от ответственности
Внимание! Любые действия вы выполняете на свой страх и риск. Я не несу ответственности за возможную поломку сайта. Предполагается, что вы сделали резервную копию файлов сайта и базы данных, а также знаете, как восстановить сайт из бэкапа в случае проблем.
3 1. Контента — много, таблиц — мало
Сайты с большим количеством постов (товаров, комментариев — не суть важно) и/или сложной рубрикацией тормозят так, что никакое кэширование не спасает. Генерация страниц выполняется медленно из-за одной странности, заложенной в ядре CMS.
В WordPress есть несколько глобальных переменных, функций и классов, которые рекомендуется использовать вместо прямого взаимодействия с базой данной. Не нужно знать язык запросов SQL для взаимодействия с базой данных, CMS берёт задачу на себя. Например, в шаблонах часто используется WP_Query() — абстракция, упрощающая работу с таблицами. Те, кому нужно манипулировать с базой как им заблагорассудится, всегда могут воспользоваться классом wpdb{}, опять-таки не залезая в дебри языка запросов SQL.
За удобством спрятана проблемная концепция: все данные сосредоточены в ограниченном количестве таблиц БД. Из-за этого серверу MySQL приходится обрабатывать слишком много ненужной инфы.
Базы данных хороши тем, что к ним можно отправить специально сформулированный вопрос и ответ будет содержать только то, что нужно, ничего более. Это сильно экономит время процессора и оперативную память сервера. Однако не все команды в языке запросов — быстрые. А те, которые отрабатывают быстро, всегда можно замедлить, подсунув большой объём данных. От правильной структуры зависит скорость выборки. В WordPress структура хранения данных весьма неудачная.
Практически все сущности — это, грубо говоря, посты. Комментарии, товары WooCommerce, изображения в галерее — всё это посты и хранятся в общих таблицах, отвечающих за контент, его метаданные и категории.
Приведу самый простой пример, чтобы объяснить суть проблемы. В БД проекта на WP вы всегда найдёте две таблицы:
- wp_posts — тут хранятся заголовки, текст, даты создания и изменения, ID (уникальный номер) и тип постов.
- wp_postmeta — сюда складываются метаданные, связанные с постами. Например, плагины счётчиков просмотра создают строчки с количеством просмотревших страницы. Плагины могут создавать десятки записей, то есть суммарное количество строк в этой таблице иногда в разы больше количества строк в wp_posts.
Само по себе хранение текста в ячейках таблиц — норма. Недостаток WP в том, что дополнительная информация засунута слишком «далеко». Принадлежность к рубрикам и авторам, права доступа, цвет и размеры товара — всё хранится практически абы как. Здесь вина как разработчиков ядра, так и сторонних плагинов.
Представим две таблицы со структурой, схожей с парадигмой Вордпресса. В одной — заголовки, текст и дата создания статей, в другой — дополнительные свойства:
ID поста | Заголовок | Текст | Дата | |
1 | Привет, мир! | Первый пост в блоге | 1 января 2019 | |
2 | Второй пост | Текст второго поста | 22 февраля 2019 | |
… | … | … | … |
ID поста | Свойство | Значение | |
1 | Посещений | 120 | |
1 | Закрепить на главной | Да | |
2 | Посещений | 300 | |
2 | Закрепить на главной | Нет | |
1 | Оценка читателей | 3 | |
2 | Оценка читателей | 5 | |
… | … | … |
Повторю ещё раз: это только пример, в настоящей базе количество столбцов больше.
Связность между двумя таблицами обеспечивает столбец ID. Благодаря общему ID всегда понятно, какое свойство к какому посту относится. Чтобы вывести содержимое и свойства самой свежей записи в блоге, нужно отправить базе данных два запроса:
- Получаем самую свежую запись: «Дай-ка мне только первую строчку из таблицы wp_posts, отсортированной по столбцу «Дата».
- Получаем свойства этой записи: «Нужны все строки из таблицы wp_postmeta, где значение столбца «ID поста» будет таким-то».
Мы узнаем из wp_posts содержимое и ID поста, затем запросим его дополнительные свойства из таблицы wp_postmeta: количество посещений, оценку читателей и всё другое, где так или иначе фигурирует указанный ID.
Ответ из wp_posts:
ID поста | Заголовок | Текст | Дата | |
1 | Привет, мир! | Первый пост в блоге | 1 января 2019 |
Ответ из wp_postmeta:
ID поста | Свойство | Значение | |
1 | Посещений | 120 | |
1 | Закрепить на главной | Да | |
1 | Оценка читателей | 3 |
На простой вопрос ответ приходит быстро, скорость зависит от объёма данных. Наибольшее время займёт процесс перебора строк в таблице wp_postmeta, чтобы среди всех отобрать с нужным ID поста. То есть чем больше постов и их свойств, тем дольше запрос, зависимость линейна.
А теперь приблизим задачу к действительности. Допустим, в сайдбаре блога выводится виджет «Топ-5 популярных статей». Увы, сложность запроса к базе возрастает на порядок:
«Выведи мне 5 строк из wp_posts, но только чтоб они были отсортированы по столбцу «Значение» из wp_postmeta, где колонка «ID поста» в wp_posts будет совпадать с колонкой «ID поста» в wp_postmeta и колонка «Свойство» будет равно «Посещений».
При перекрёстных условиях логика запроса усложняется, и намного. Базе данных придётся метаться туда-сюда, отбирая из обоих таблиц строки, сортируя их, снова отбирая и так далее. Нетрудно догадаться, что по времени это может занять секунды или даже минуты, если запрос содержит много условий, а количество данных — не 10 строчек, а сотни тысяч.
А ведь реальность гораздо суровее моих примитивных примеров:
- В отборе участвуют дополнительные таблицы, содержащие названия рубрик, тегов и их метаданные.
- Новые плагины стремятся заполнить таблицы wp_*meta новыми строками.
- Товары, фотографии, вообще любые объекты в WordPress — это те же посты, просто тип у них другой. А значит — таблица wp_posts разрастается очень быстро. И даже если в запросе они не участвуют, своим фактическим присутствием ухудшают производительность.
Невозможность выйти за пределы строго заданных таблиц мешает развивать сайт, когда он входит в стадию активного наполнения контентом. Всякие шаблоны оформления могут имитировать внешний вид новостных изданий, досок объявлений и форумов, однако ограничения WP они не снимут.
Универсального решения нет
Очевиднейший совет — «подкрутите» настройки сервера БД MySQL (MariaDB, Percona… не важно), что косвенно ускорит работу с большими таблицами. В подборе настроек сильно поможет утилита MySQLTuner. Ещё можно добавить индексы, но об этом позже (индексы в БД — не панацея, ибо при ускорении чтения замедляется запись).
Со стороны кода, увы, сложно что-то исправить. В теории решение простое: хранить разные типы постов в разных таблицах. Так как все объекты в WP — это по сути посты, то разделение на таблицы по признаку типа объекта существенно сократит накладные расходы при выборке данных. В самом деле, почему все записи на форумах, логи, свойства товара и платёжные данные хранятся вместе? Почему нельзя сделать для товаров wp_tovar и wp_tovarmeta?
Об этом задумывался не только я. Предложение внедрить поддержку отдельных таблиц #14558 в ядро WP было вынесено на обсуждение в далёком 2010 году… и сразу же закрыто ведущим разработчиком с формулировкой:
Это не то, что мы могли бы обсудить.
Питер Вествуд, один из разработчиков WordPress
Мы довольны сохранением пользовательских типов записей в существующих структурах таблиц.
Мы не рекомендуем добавлять дополнительные таблицы — в некоторых сценариях установки это даже невозможно.
Отказ понятен: в WordPress разделение на уровне БД трудно спрогнозировать с точки зрения производительности и совместимости с имеющимися сайтами, плагинами и шаблонами. Но, на мой взгляд, такой консерватизм сродни регрессу, потому что другие CMS на месте не стоят.
Удивительная новость
В WooCommerce, начиная с 7-й версии, появилась экспериментальная опция «Высокопроизводительное хранилище заказов (COT)», которая находится в разделе WooCommerce > Настройки > Дополнительно > Функции. На английском называется HPOS (Settings > Advanced > Features). Она делает ровно то, что нужно для быстрой обработки заказов: выделяет для них отдельные таблицы. Конечно, путем потери совместимости со многими плагинами. Подробнее об этой опции написано здесь: High Performance Order Storage Upgrade Recipe Book.
Есть и другой путь. Примерно в то же время проходило обсуждение #14513 о способе связывания отношений постов по принципу «многое ко многому». В результате родился плагин Posts 2 Posts, который с помощью промежуточных таблиц позволял быстро отбирать записи по нескольким критериям. Несмотря на энтузиазм участников сообщества, развитие плагина практически остановлено, работа на современных версиях WordPress не гарантируется. При наличии достаточных знаний возможно адаптировать плагин под современные реалии.
На данный момент подвижки в данной области не внушают оптимизм. Да, можно работать напрямую с БД, создать вручную отдельные таблицы, но все «плюшки» WP_Query — удобная сортировка и отбор, сохранение и запись метаданных — недоступны без написания собственной реализации WP_Query. Если вам нужно обрабатывать миллионы сущностей, возможно, это обсуждение подтолкнёт разработку сайта в нужном направлении.
4 2. Сериализация не позволяет манипулировать данными
Сериализация — это преобразование массива значений в одну строку. Это позволяет засовывать массивы любого размера в одну ячейку таблицы БД. Однако данные внутри таких ячеек БД не видит, соответственно — отбирать по ним строки не может.
Как правило, сериализация происходит при попытке записать в БД какой-то массив значений. Я часто вижу, как авторы плагинов и шаблонов зачем-то сваливают дополнительные свойства поста вроде SEO-атрибутов и дополнительной рубрикации в одну общую переменную.
$svoistva = [ "title_caption" => "foo", "category" => "bar", ]; update_post_meta( $post_id, 'svoistva', $svoistva );
Затем это извлекается в переменную, чтобы вывести в шаблоне, и вроде проблемы тут нет, но… попробуйте-ка отобрать посты по такому свойству!
Чтобы показать, насколько бредово использовать сериализацию для хранения свойств контента в WordPress, приведу пример. Допустим, у вас есть интернет-магазин фруктовых соков. Причём соки могут смешиваться: есть отдельно из яблок, из вишни, а есть яблочно-вишнёвый. Примерно таким будет хранение состава и объёма напитков в сериализованном виде:
ID | Название | Свойства |
1 | «Яблочко-сладочко» | [Состав:Яблоко; Объём:100] |
2 | «Вишня-кислишня» | [Состав:Вишня; Объём:1000] |
3 | «Яблочно-вишнёвый микс» | [Состав:Яблоко,Вишня; Объём:500] |
4 | «Красный дождь» | [Состав:Томат; Объём:100] |
5 | «Радость вегана» | [Состав:Яблоко,Томат,Вишня; Объём:1500] |
В базе данных такие свойства — это строки примерно в таком виде, какими я написал выше. При извлечении вернётся массив, но БД об этом не знает и рассматривает свойства как обычные строки.
Как из такой таблицы отобрать соки объёмом 100 мл? Попросить базу отобрать строки, где есть совпадение с сочетанием символов «100», не получится, потому что туда попадёт лишняя строка «{Состав:Вишня; Объём:1000}». Ну не может база данных «понять», что в столбце «Свойства» — вложенные свойства, и обрабатывать отдельно.
Придётся получить весь список соков, а затем в коде PHP сделать десериализацию содержимого каждой ячейки, чтобы уже не в базе данных, а в коде пройтись по каждому значению в поисках точного совпадения. Но товаров-то может быть десятки и сотни тысяч! Отбор информации средствами языка PHP потребует гораздо больших ресурсов по сравнению с использованием функций БД. В лучшем случае загрузка страницы с товаром замедлится на несколько секунд, в худшем — часть посетителей увидят сообщение об ошибке 50x серии из-за нехватки ресурсов сервера.
Я совершенно не понимаю, каким местом думают разработчики, пихающие в одну ячейку сериализованные наборы значений, прекрасная зная, что позже по ним будет идти отбор! Из-за сериализации там, где это не надо, владельцы интернет-магазинов, созданных плагином WooCommerce, сталкиваются с долгой (минуты!) загрузкой страниц категорий с отбором по цвету товара, его размеру или какому-нибудь подобному полю.
Решение: будьте проще
Когда пишете собственный плагин, всегда предусматривайте возможность лёгкого извлечения данных. В одной ячейке таблицы должно храниться одно значение. Лучше создать сотню свойств товара через add_post_meta(), чем засовывать в одну ячейку весь массив. Всегда следует помнить, что отбор постов по сериализованным свойствам в 5-20 раз дольше по сравнению с классическими запросами.
Исключение — хранение настроек через update_option(). Как правило, они не участвуют ни в каких отборах, поэтому можно хранить опции плагинов и шаблонов в сериализованном виде, если таково желание.
Но даже с хранением настроек перегибать палку не стоит. Например, плагин WP Memberships, предназначенный для ограничения контента в зависимости от того, какой товар купил пользователь, хранит в едином массиве правила доступа к контенту. Этот нюанс — что вместо свойств товара используются наборы правил — слишком запутали логику ограничений доступа, поэтому работать с WP Memberships сложно.
Если работаете с чужими плагинами… Ну, сочувствую. Переписывать чужие плагины — работа неблагодарная. Обычно в таких случаях я выделяю ключевую функцию плагина и реализовываю её сам, чтобы избавиться от исходного решения. Как правило, при этом сайт начинает работать немного быстрее, ибо избавляется от ненужных довесков.
5 3. Отсутствие индексов в БД
В попытке обойти ограничения WP_Query() разработчики для своих нужд создают отдельные таблицы, но делают это зачастую неумело. Решения бывают крайне неудачные.
Я приведу, пожалуй, худший пример, который только видел: платный плагин управления рекламой AdsPlace’r Pro. Для своих нужд он плодит аж 23 таблицы:
В самой большой — wp_adsplacer_pro_reference_country_ip — хранится список адресов IP с привязкой к стране и городу. Это нужно для узнавания местоположения посмотревшего рекламу. База IP-адресов России содержит 49 407 строк. Это очень много по меркам любого сайта, поэтому для быстрого поиска пары «IP = город» необходимо использовать индексы.
Индексы в БД — это специальные структуры, позволяющие ускорить поиск и сортировку по определенному полю или набору полей в таблице. Индексы — то же самое, что и оглавление в книгах. Если в книге его нет, придётся прочитать всю книгу с начала до нужного места. С оглавлением поиск проходит намного быстрее.
И всё бы ничего, только:
- Индексов в таблице не оказалось (как и в некоторых других). То есть авторы даже не задумались над оптимизацией.
- Поиск по таблице срабатывал при загрузке любой страницы, даже если опция GeoIP была отключена.
По сути, плагин AdsPlace’r Pro, обзоры которого почему-то сплошь хвалебные, на деле замедлял открытие страниц на ~0,8 секунды на том сайте, где я заметил проблему. Это существенная задержка, негативно влияющая на посещаемость, ибо поисковым системам не нравятся такое замедление.
Добавление индекса помогло сократить время отдачи страницы сервером до 0,09 секунд, но осадочек остался. Добавим несовместимость с PHP 7.3 из-за использования PHP-библиотеки Simple HTML DOM Parser версии 1.5 (2008 года!) и получим ну такое себе решение, которое вроде решает задачу, но делает это плохо, мешая выжать из сайта максимальную производительность.
В ответ на мой тикет о проблемах авторы AdsPlace’r Pro пообещали исправить недочёты в конце 2019-го, привязав новую версию к выходу WordPress без поддержки старых версий PHP. Это странно, потому что решить вопрос с индексированием и таким образом ускорить разом сайты клиентов можно было бы крохотным изменением кода. В общем, пока не выйдет версия 3.0 с ожидаемыми исправлениями, AdsPlace’r Pro к покупке не рекомендую.
Как обнаружить проблему и добавить нужный индекс
Надеюсь, вы прочли текст выше, а не перескочили сразу до этой главы.
Нужно понять, что именно тормозит, ибо «на глазок» сложно определить, где нужно создать индекс. Ставьте и активируйте плагин Query Monitor.
Затем откройте проблемную страницу и обратите взор на админ-панель WP. Если пункт с таймингами жёлтый — значит, во время открытия выполняются медленные запросы к БД. Если красный — присутствуют ошибки PHP. Ошибки — разговор для отдельной статьи, на скорость они не влияют. Нас интересуют медленные запросы.
Жмите по пункту «Медленные запросы». Внизу страницы появится панель плагина Query Monitor со вкладкой » Медленные запросы». Там будет видно, что за медленный запрос мешает быстрому открытию. В моём случае был такой на 0,6 секунды:
То есть до того, как отдать страницу посетителю, сервер баз данных MySQL, помимо обработки быстрых запросов по несколько миллисекунд, запнулся на одном аж на 0,6 секунды. В колонке «Компонент» видно, что виноват плагин similar-posts. Он отвечает за вывод списка похожих постов внизу страницы, в том числе той, которую сейчас читаете.
Самый простой способ избавиться от медленного SQL-запроса — когда вы поняли, какой плагин или функция шаблона мешает, отключить его/её и наслаждаться ускорением работы сайта на WordPress. Но потеря функций не всегда приемлема. Поэтому разберёмся, почему запрос выполняется долго, и оптимизируем.
Откройте phpMyAdmin или любой аналогичный менеджер БД. PMA мне нравится тем, что в нём легко добраться до консоли ввода SQL команд. Откройте её (кликните по названию базы сайта, затем по вкладке «SQL») и вставьте скопированный из панели Query Monitor запрос, добавив в самом начале команду «EXPLAIN«:
После нажатия «Вперёд» благодаря команде EXPLAIN вы увидите расшифровку запроса:
В колонке «table» — участвующие в запросе таблицы. Если там присутствуют только wp_posts и wp_postmeta, то, увы, текст в этой главе можете не читать — вы столкнулись с самым медленным видом сложного запроса, о котором я рассказывал выше в проблеме №1. Чтобы такой оптимизировать, требуется переписать программный код плагина/шаблона и, быть может, поменять структуру БД, что совсем непросто. К счастью, на практике в медленном открытии страниц виновато банальное отсутствие подходящего индекса. В случае из примера надо что-то делать с индексами в таблице wp_similar_posts, которая создаётся плагином Similar Posts.
База данных — умная система, она самостоятельно ищет способ наибыстрейшего выполнения задачи. В колонке «possible_keys» — список из колонок и индексов, которые могли бы использоваться, в «key» — фактически используемая в запросе колонка. В моём случае везде NULL — это значит, что с таблицей что-то не так, перебираются все значения. Косвенно это подтверждает число в «rows» — 2400 — примерно столько строк в таблице. Просмотр каждой строки — занятие долгое. Неудивительно, что запрос к базе получился медленным.
Чтобы сократить время, в таблице должна быть колонка с уникальными значениями, лучше — индекс. По аналогии с книгой колонкой с уникальными значениями будут номера страниц, а индекс — оглавлением. Вкупе они сильно облегчают поиск информации.
Глянем содержимое wp_similar_posts, чтобы оценить, много ли колонок, найдутся ли среди них с уникальным значением. И сразу видим проблему, о ней phpMyAdmin сообщает в верхней части страницы:
Если в таблице нет колонки с уникальными значениями, работать с ней тяжко: редактировать сложно, чтение медленное. Поэтому нужно либо указать базе данных, что одна из колонок содержит уникальные значения, либо создать новую колонку с автоинкрементным значением, проще говоря — счётчиком строк. Тогда каждая строка станет уникальной, поиск станет однозначно быстрее.
В некоторых реализациях БД в таблицах с уникальными строками индексы создаются автоматически, но полагаться на этот механизм нельзя — иногда эффективней будет создать индекс по нескольким столбцам одновременно. Подробности об этом вы узнаете из видео в конце главы.
Почему в моём случае нет уникальных строк и отсутствуют индексы — не знаю. Возможно, таблица каким-то образом потеряла флаг уникальности в колонке pID, потому что в ней все значения уникальные. Пробуем назначить упомянутой колонке «уникальность»:
Получилось! Напротив названия столбца появился серый ключ.
На вкладке «SQL» выполните следующий запрос, подставив вместо tablename название своей таблицы, а вместо newid — произвольное название колонки, где будут уникальные значения:
ALTER TABLE tablename ADD newid MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST;
В 99% случаев это решит проблему производительности.
Если вернуться на вкладку «Обзор», ошибка «Данное выделение не содержит уникального столбца» пропадёт. Теперь страницы открывают быстрее:
Вместо 0,6 секунд стало 0,0454 — ускорение в 13 раз! Можно ли оптимизировать ещё больше? Надо проверить!
Делаем снова выполнение команды на вкладке SQL базы данных сайта с добавлением EXPLAIN и смотрим, какие ключи (колонки) теперь используются для выборки значений. По факту получилось это:
Это говорит о том, что БД стала использовать значения колонки pID как уникальные указатели, из-за чего выборка из всей таблицы сокращается до одной строки (см. значение «rows»). Это то же самое, как если в поисках нужной страницы в книге не листать все страницы по очереди, а сразу открыть нужную, потому что все номера страниц на виду. То есть оптимизировать в моём практическом примере некуда (wp_posts со 117 значениями игнорируем, потому что не трогаем ядро WP с его дурацким WP_Query()).
Но представим, что будет, если уникальный столбец не помог бы и rows по-прежнему много (допустим, вместо 2400 стало 900, то есть выбора сократилась-ускорилась в 2,5 раз). В такой ситуации вывод EXPLAIN запроса получился бы таким:
То есть база данных видит, что есть две колонки — pID и content (их может быть больше). В одной, допустим, 900 уникальных строк, в другой — 2000. Выбор очевиден — надо создать более компактное «оглавление» таблицы.
Но как сократить ещё сильнее? Запомните, какие ключи указаны в «possible_keys» и откройте вкладку «Структура таблицы». Выделите два первых ключа из запомненных и нажмите «Уникальный» («Unique»):
В списке индексов ниже появится новый:
Если индекс бесполезен, то в запросе он участвовать не будет. Тогда нужно удалить его и создать с другим сочетанием. Да, получается примитивный перебор без понимания сути происходящего, но это работает.
Для того, чтобы с первого раза создать индекс, надо почитать теорию и попрактиковаться. Полезные материалы с просторов Ютуба:
6 4. Низкое качество плагинов и шаблонов.
WordPress — популярнейшая CMS с предельно низким порогом входа. Система предлагает доработку поведения через хуки и фильтры. Хуки позволяют реагировать на какие-либо события вроде добавления товара в корзину, а фильтры модифицируют объекты «на лету». Это просто, это удобно, но оставляет очень много простора для фантазий. Выстрелить в ногу при доработке сайта в таких условиях легко.
Статистически получается, что большинство плагинов и шаблонов Вордпресса — мусор. Тем не менее, если этот мусор решает какую-то задачу, то он популярен. Например, плагин Ultimate Member используется на более 100 000 сайтов, хотя представляет собой скопление костылей, багов и уязвимостей. Даже если разработчики допустили ну очень много странных и откровенно плохих решений, плагином будут пользоваться.
Критерии «мусорности» у каждого свои. Оценить проблематичность кода без опыта разработки невозможно.
Например, я считаю неприемлемым для хранения настроек создавать отдельную таблицу с одной строчкой, но безымянный автор, погодный виджет которого я чинил, работал исключительно через отдельные таблицы. Кто-то не приемлет использование jQuery на фронтенде, а мне всё равно — так или иначе, в WP он подгружается по умолчанию, не считаю зазорным его использовать даже в 2019 году. Кто-то передаёт значения через глобальные переменные без wp_nonce, я же предпочитаю задействовать транзиентные настройки для хранения промежуточных данных.
В низком качестве кода виноваты в первую очередь не начинающие разработчики, на каждый чих создающие свои плагины-костыли (в попытках писать код нет ничего зазорного), а кураторы WordPress, ответственные за наполнение каталогов шаблонов и плагинов — сотрудники Automattic и волонтёры. В погоне за количеством качество ушло куда-то дальше второго плана.
Отдельного словца заслуживают универсальные шаблоны. Да-да, я говорю о Divi и прочих подобных темплейтах-конструкторах! Авторы в погоне за функциями громоздят громадные файлы скриптов, в которых для конкретного сайта полезны две-три функции. В итоге благодаря таким шаблонам быстро и просто создаются неповоротливые страницы на десятки мегабайт. Это неприемлемо даже в эпоху повсеместного развития 4G сетей — трафик по-прежнему не резиновый, а мобильные устройства имеют свойство разражаться быстрее из-за чрезмерных вычислений. Поэтому обязательно уделяйте внимание технической оптимизации сайта. Хотя бы картинки жмите посильнее каким-нибудь скриптом.
Как оценить количество проблем на сайте
Невозможно рассмотреть все возможные проблемы. Наборы плагинов разные, шаблоны у всех не одинаковые. Расскажу вместо этого, как оценить количество имеющихся проблем через режим отладки.
Для включения отладки в wp-config.php в корне сайта перед строчкой «/* That’s all, stop editing! Happy publishing. */» добавьте:
define('WP_DEBUG', true); define('WP_DEBUG_LOG', true); define('WP_DEBUG_DISPLAY', false);
После этого в каталоге /wp-content создастся файл debug.log, в который будут записываться ошибки в скриптах PHP, возникающие при работе сайта. Режим отладки замедляет сайт, поэтому не держите его включённым долго. После оценки ситуации удалите строчки и файл.
Далее можно действовать по-разному. Если сайт ведёт себя не так, как должен, а во время неверной реакции в debug.log фиксируется какая-то ошибка, стоит заняться починкой — посмотреть код, понять суть проблемы и переписать проблемный участок кода.
Бывает и так, что ошибки есть, но визуально проблемы никак не появляются. Это распространённое явление. Иногда количество ошибок может зашкаливать, но сайт продолжит работать не смотря ни на что. Медленнее, чем могло быть, но тем не менее. Всё зависит от вашего решения — тратить ли ресурсы на исправление проблем или забить.
7 Оставление сайта без обслуживания и развития
Не то чтобы проблема, но знать необходимо.
Любой сайт требует периодических вложений. Траты не обязательно денежные: иногда достаточно уделить час в неделю, чтобы грамотно обновить все плагины и проверить статистику посещений. Кстати, увеличение количества посетителей — одна из причин, которая заставляет владельцев начать беспокоиться, ибо в какой-то момент под нагрузкой страницы начинают отдаваться слишком медленно, что не нравится ни посетителям, ни поисковым системам.
Итак, какие бывают траты у владельцев сайтов?
Обязательные, регулярные:
- Оплата хостинга. Это может быть сверхдешёвый виртуальный сервер за 60 рублей в месяц у VDSina, сервер за 15$ в месяц у Digital Ocean либо какой-нибудь shared-хостинг от SmartApe. Вариантов полно, их роднит одно — бесплатно ваш сайт на WordPress никто размещать не будет, придётся платить денежки. О том, какой хостинг можно назвать хорошим, я рассказал в отдельной заметке «Как выбрать VPS хостинг — несколько советов».
- Оплата доменного имени. Сайту нужно имя, которое люди запомнят и наберут в адресной строке. Тоже не бесплатная услуга, но можно очень, очень сильно сэкономить, если знать, где искать («Покупка домена: как придумать имя, где купить дешевле и что настроить»). На аренду домена glashkoff.com я трачу примерно 800 рублей в год, но для зоны .ru цены существенно ниже — от 150 рублей.
- Обслуживание. Если сайт размещён на VPS, то кому-то придётся обновлять софт. Потому что в софте регулярно находят уязвимости и ошибки, нужно переходить на более защищённые, совершенные версии. Владельцам shared-хостинга легче: серверный софт за них обновляют инженеры компании, предоставляющей место для сайта. В обоих случаях надо и сам WordPress обновлять, и плагины к нему — тут есть нюансы, о них позже.
Не обязательные, иногда однократные:
- Установка сайта, работа над дизайном и функциональностью. Можно самостоятельно накатить софт, выбрать шаблон оформления из бесплатных, понаставить плагинов, как-то их настроить и вот сайт готов. Бюджетно, но работоспособно — вот она, сильная сторона WordPress! Однако, если вы видите сайт в качестве источника дохода, в режиме «тяп-ляп» создать сайт не получится, нужны вложения.
- SEO-оптимизация и наполнение контентом. Хотите, чтобы по поисковым запросам сайт удерживался на первых строчках? Извольте регулярно наполнять сайт качественными текстами, интересными посетителям. SEO — вопрос обширный, сложный и неоднозначный. Затраты по нему сложно оценить. Если хотите заниматься этим самостоятельно, как минимум должны знать о 101 способе сделать сайт лучше.
- Поведенческая аналитика и улучшение UX. Оптимизация так называемого UX (User Experience), пользовательского опыта по-нашему. Независимо от того, сколько миллиардов долларов вы потратили на дизайн и функциональность, посетители всегда будут делать что-то не так, как вы этого ожидали. И чего-то всегда им будет не хватать. Для сглаживания таких углов и нужна непрекращающаяся работа над сайтом. Больше всего это требуется интернет-магазинам и сайтам, продающим услуги, где посетителя нужно довести до определённого действия.
- Затраты на рекламу. Те, для кого сайт — часть бизнеса, могут не дожидаться годами, пока их проект получит первые места в поисковой выдаче, а начать рекламировать свой сайт (или услугу) во Вконтакте, тех же поисковых системах, на других сайтах с баннерной рекламной и так далее. Грамотно настроенная рекламная кампания способна дать нужное количество клиентов, таким образом снизив важность трафика из поисковых систем.
Затраты со временем чаще растут, чем снижаются, но, если проект развивается в верном русле, прибыль будет расти быстрее затрат. Важно не забрасывать сайт, а постоянно поддерживать его контентом, наращиванием функций, рекламой и другими полезными активностями.
8 Есть и хорошее
На самом деле WordPress — это круто. Проект живёт много лет, документация подробна, все проблемы обсуждены и решены миллион раз (кроме организации данных в БД). Исходные коды CMS и плагинов открыты, можно анализировать чужие решения и изобретать правильный подход к решению задач.
При должном умении можно творить по-настоящему классные штуки любым инструментом.
Все проблемы — в головах тех, кто не умеет правильно распорядиться возможностями.