WordPress: выбор случайных постов

Владимир | | MySQL, PHP, Web разработка, WordPress.

wordpress sql rand

Идея написать этот пост у меня появилась после выхода статьи
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 году