Например, если это магазин на Woocommerce с более чем 10 000 товарами и расширенным личным кабинетом, всяческими многоуровневыми скидочными программами и тому подобным.

Артем Зайцев
EvilmarketerВыключаем WP_Cron
WP_Cron
на сайтах, обвешанных плагинами, создает порой нехилую нагрузку. Встроенный планировщик задач на WordPress работает таким образом, что при каждой загрузке страниц, проходят запросы к wp-cron.php
. Каждый такой запрос проверяет, нет ли задач к выполнению на данное время. Естественно, что такой механизм создает дополнительную ненужную нагрузку, которую можно и нужно убрать.
wp_cron
при этом функционировал, мы его отключим и будем обращаться к файлу wp-cron.php через cron
на сервере:Как выключить
WP-cron
надо в wp-config.php
прописать следующую строчкуdefine('DISABLE_WP_CRON', true);
cron
задачу на обращение к wp-cron.php
каждые 5 минут: #используя cURL. Он доступен «из коробки» */5 * * * * curl https://yoursite.com/wp-cron.php?doing_wp_cron

*/5 * * * * wget -O - -q -t 1 https://yoursite.com/wp-cron.php?doing_wp_cron
-O
выводит в консоль вместо сохранения в файл; -q
делает это тихо, без вывода на экран; -t 1
указывает делать лишь одну попытку соединения.
Включаем объектный кэш
В WordPresse можно использовать 2 решения: Memcached и Redis. Чтобы включить Memcached, потребуется плагин. Я использую на проектах Memcached Redux.
Как подключить
Для начала нужно добавить сервис в панели D2C и скопировать alias с портом. Он понадобится нам позже.

object-cache.php
из плагина Memcached Redux положить в папку wp-content и указать в wp-config.php
массив с адресами серверов кэша. Сам плагин при этом ставить в папку plugins не нужно.dokmemcached
, его и укажу в конфиге:$memcached_servers = array('dokmemcached:11211');
WP_CACHE_KEY_SALT
. Эта константа нужна для того, чтобы записи с разных сайтов, обращающихся к одному кэширующему серверу, не конфликтовали. Можно указывать что угодно, но я предпочитаю указывать название сайта:define('WP_CACHE_KEY_SALT', 'dokmarket');
Эффективность
Страница товара без объектного кэша SQL: 209 за 1.000 sec. Страница товара с объектным кэшем SQL: 45 за 0.000 sec.
Выводим похожие товары без использования woocommerce_related_products()
woocommerce_related_products()
, она используется для реализации блока похожих товаров. Её проблема в том, что запросы к выводу сопутствующей продукции Woocommerce делает крайне затратным способом: подтягивает кучу метаданных, ищет по всем товарам и потом выводит указанное количество рандомно, используя ‘orderby’ => ‘rand’
в wp_query
.‘orderby’ => ‘rand’
надо переписывать нещадно и везде. Этот метод, наверное, один из самых прожорливых в WP. На моей практике был случай, когда подобный способ генерировал аж под 1000 запросов к базе.
Прежде чем реализовать задумку, надо определиться с логикой определения похожих товаров. Если посмотреть с точки зрения пользователя, то видеть рандомные товары среди похожих не особо полезно. В этом случае имеет смысл вывести их по популярности (количеству продаж), рейтингу или стоимости (от дешевого к дорогому). Если проект новый и не накоплено достаточно статистики по продажам или не собрано большого количества отзывов, я использую фильтр по цене. Так и поступим.
Реализация
content-product-related.php
.

content-product-related.php
. Прежде чем приступить непосредственно к написанию кода, запретим в шапке прямой доступ.if ( ! defined( 'ABSPATH' ) ) exit;
global $product; if ( ! $product || ! $product->is_visible() ) { return; }
$post_id = get_the_ID(); $term_list_product_cat = wp_get_post_terms( $post_id, 'product_cat', array("fields" => "all") );//здесь получаю все данные по категориям, которые относятся к данному товару $get_current_productcats = array_slice($term_list_product_cat, 0, 3); //выбираю первые 3 категории, к которым относится продукт, можно увеличить уровень вложенности или выбрать вообще все. Лично мне достаточно сопоставлять по трем.
function current_child_productcat($arr_productcat){ $productcats = ' '; foreach ($arr_productcat as $productcat) { $productcats .= $productcat->slug.','; } return explode(',',$productcats); }
wp_query
, на основе которого будем выводить похожие товары.$query = new WP_Query( array( "post__not_in" => array( $post_id ), //не забудем убрать текущий пост из результатов "post_type" => "product", //брать будем из товаров "post_status" => "publish", //опубликованных "meta_key" => "_regular_price", //сортировать по цене "orderby" => "meta_value_num", "order" => "ASC",//от дешевой к дорогой "posts_per_page" => 3,//выведем три результата "tax_query" => array( //отфильтруем по таксономии array( "taxonomy" => "product_cat", "field" => "slug", "terms" => current_child_productcat($get_current_productcats) //передадим наш массив с категориями ) ) ) );
content-product.php
из папки woocommerce, он отвечает за внешний вид таблицы в листинге товаров.<?php if ( $query->have_posts() ) : ?> <div class="single-page__related"> <h3>Похожие товары</h3> <?php ?> <ul class="block-grid list-unstyled row clearfix row-flex row-flex-wrap"> <?php while ( $query->have_posts() ) { $query->the_post(); ?> <li class="col-lg-4 col-md-4 col-sm-6 col-xs-12 pull-left equal-height item product type-product status-publish has-post-thumbnail instock purchasable product-type-simple" > <div class="products-entry item-wrap clearfix"> <div class="item-detail loading"> <div class="item-img products-thumb"> <?php /** * woocommerce_before_shop_loop_item_title hook * * @hooked woocommerce_show_product_loop_sale_flash - 10 * @hooked woocommerce_template_loop_product_thumbnail - 10 */ do_action( 'woocommerce_before_shop_loop_item_title' ); ?> </div> <div class="item-content products-content"> <div class="item-loop__fixed-height"> <?php /** * woocommerce_shop_loop_item_title hook * * @hooked woocommerce_template_loop_product_title - 10 */ do_action( 'woocommerce_shop_loop_item_title' ); /** * woocommerce_after_shop_loop_item_title hook * * @hooked woocommerce_template_loop_rating - 5 * @hooked woocommerce_template_loop_price - 10 */ //do_action( 'onemall_template_loop_price' ); do_action( 'woocommerce_after_shop_loop_item_title' ); ?> </div> <?php /** * woocommerce_after_shop_loop_item hook * * @hooked woocommerce_template_loop_add_to_cart - 10 */ do_action( 'woocommerce_after_shop_loop_item' ); ?> </div> </div> </div> </li> <?php } ?> </ul> </div> <?php endif; ?>
content-single-product.php
внутри шаблонов магазина, он отвечает за внешний вид товара. <?php get_template_part( 'content-product-related' ); ?>
do_action( 'woocommerce_after_single_product_summary' );
.Очищаем базу данных от ненужного мусора и оптимизируем таблицы
Для решения этой задачи я рекомендую плагин Advanced Database Cleaner. Причем его PRO версию. Если сайт существует давно или вы в период разработки подключали-отключали кучу плагинов, он вам сильно поможет.
Плагин умеет все: находить неиспользуемые таблицы, опции, оптимизировать таблицы, удалять ревизии. Рекомендую пройтись прямо по всем вкладкам и провести необходимые процедуры. Особенно много внимание уделите неиспользуемым опциям (orphan options), это те самые настройки, которые насоздавали все плагины без исключения, в том числе и те, что давно уже удалены.
Неиспользуемые опции, которые находятся в таблице wp_options
, могут создавать нехилую нагрузку и пожирать кучу памяти. Так происходит потому, что настройки со значением autoload
подгружаются при каждом обращении к WordPress.

Ремарка
Продолжение следует
На очистке БД от мусора я закончу рассказ о первых шагах в оптимизации WordPress. Перечисленных методов уже достаточно, чтобы снять приличный пласт нагрузки с сайта. В следующей статье цикла расскажу о генерации статичного кэша с помощью плагина WP Supercache и настройке динамических блоков для неавторизованных пользователей.