Идея написать этот пост у меня появилась после выхода статьи
Random Redirection In WordPress Перевод в Smashing magazine.
В ней рассказывается о том, как выполнить редирект на случайный пост. Но точно такой же подход можно использовать и для отображения случайных постов, например, в каком-нибудь виджете.
Сразу хочу пояснить. Я ничего не имею против решения, описанного в Smashing magazine, это встроенный в WP способ выборки случайных записей, просто при его использовании можно ощутимо снизить скорость формирования страниц.
Для начала рассмотрим, как работает стандартный вариант.
Для этого немного перепишем функцию, приведённую в Smashing magazine.
function show_random_posts($count = 3) { $start = microtime(true); $args = array( 'numberposts' => $count, 'orderby' => 'rand', 'post_type' => 'any', ); $rnd_posts = get_posts( $args ); foreach ( $rnd_posts as $post ) { echo '<p><a href="'.get_permalink($post->ID).'">'.$post->post_title.'</a></p>'; } $stop = microtime(true); echo '<p>Затраченное время: '.($stop - $start).'</p>'; }
Здесь мы выбираем заданное количество случайных записей ($count
) с помощью встроенной функции get_posts
. Эта функция в качестве параметра получает массив с настройками поиска. В данном случае это:
numberposts
– количество записей, которые должен вернуть запрос;
orderby
– сортировка, в данном случае – rand
– случайным образом;
post_type
– типы постов – any
– все.
Затем в цикле (строки 12-14) просто выводим список полученных постов.
Кроме того, в переменных $start
и $stop
сохраняем время начала и завершения кода.
Использовать эту функцию не сложно, достаточно просто вставить её в шаблон нужной страницы.
Но что именно происходит при вызове get_posts?
WP делает один запрос к БД, который возвращает все необходимые данные.
SELECT wp_posts.* FROM wp_posts WHERE 1=1 AND wp_posts.post_type IN ('post', 'page', …) AND (wp_posts.post_status = 'publish') ORDER BY RAND() DESC LIMIT 0, 3;
Просто и элегантно 🙂
Но известно, что функция RAND()
снижает скорость запроса.
Попробуем переписать нашу функцию так, чтобы получить тот же результат, но обойтись без RAND()
.
У меня получился следующий код
function show_random_posts_optimized($count = 3) { $start = microtime(true); $args = array( 'numberposts' => -1, 'fields' => 'ids', 'post_type' => 'any', ); $random_posts_ids = get_posts($args); $rnd_posts = array_rand($random_posts_ids, $count); foreach ( $rnd_posts as $post_index ) { $id = (int)$random_posts_ids[$post_index]; $post = get_post($id); echo '<p><a href="'.get_permalink($post->ID).'">'.$post->post_title.'</a></p>'; } $stop = microtime(true); echo '<p>Затраченное время: '.($stop - $start).'</p>'; }
Идея следующая. Сначала получаем ID всех постов. Обратите внимание, что если вы не укажите параметр 'fields' => 'ids'
, WordPress «вытянет» все посты целиком. Очень неприятная ошибка, в разы увеличивает потребление памяти.
На этом этапе выполняется запрос
SELECT wp_posts.ID FROM wp_posts WHERE 1=1 AND wp_posts.post_type IN ('post', 'page', ...) AND (wp_posts.post_status = 'publish') ORDER BY wp_posts.post_date DESC ;
Затем выбираем случайным образом заданное количество постов, с помощью array_rand
.
И в цикле получаем данные постов с помощью get_post
. На этом этапе выполняются запросы вида
SELECT * FROM wp_posts WHERE ID = 23 LIMIT 1;
Т.е. если мы выводим три случайных поста, то функция show_random_posts_optimized
будет выполнять 4 запроса для получения информации о постах, а show_random_posts
— всего 1.
Но проверим время выполнения.
Первый эксперимент я провел на блоге с ~40 постами.
Результаты:
show_random_posts - 0.021620035171509 с
show_random_posts_optimized - 0.0076930522918701 с
В абсолютном выражении цифры небольшие, но разница заметная — в 2.8 раза.
Теперь посмотрим, что произойдет на реальном блоге с 1700+ постами.
Результаты:
show_random_posts - 0.49169611930847 с
show_random_posts_optimized - 0.039859056472778 с
Как видите, теперь первая функция работает в 12.3 раза медленнее.
Выводы сделать несложно.
Второй вариант работает быстрее и выдаёт тот же результат, но дело не в этом. Подобные проблемы могут быть практически не заметны во время разработки и привести к очень не приятным результатам при запуске на реальном ресурсе. Или ещё хуже, скорость будет падать постепенно, по мере добавления новых постов.
А найти проблемный запрос довольно просто. Установите BlackBox Debug Bar или аналогичный плагин и он вам выведет список всех выполненных запросов и затраченное на них время.
Что делать дальше, зависит от ситуации. В данном случае, оказалось достаточно переделать алгоритм выбора случайных постов. В других, возможно, придется использовать кеширование или, в крайнем случае, вообще отказаться от какой-то возможности. Главное, понять причину проблемы, тогда и выбранное решения будет обоснованным и эффективным… скорее всего 🙂
Happy coding 😉
Интересно почитать:
Скачайте договор безвозмездного пользования нежилым помещением от грамотных юристов
Заполним декларацию 3-НДФЛ при покупке квартиры в 2012 году