Почему функция get_posts() выполняется так долго?

Здравствуйте! <br/> У меня возникла проблема с выполнением функции get_posts() при условии наличия около 2000 постов. Процесс занимает много времени и часто приводит к ошибке 500 на хостинге, даже при установленных параметрах php_memory_limit в 256 МБ. <br/> Вот пример использованного кода: <br/> <pre><code class="php">$posts = get_posts( array(
            'numberposts' =&gt; -1,
            'orderby'     =&gt; 'date',
            'order'       =&gt; 'DESC',
            'post_type'   =&gt; 'post-item',
        ) );</code></pre> <br/> В настоящее время я решил эту проблему, увеличив php_memory_limit. Однако у меня возникла идея оптимизировать запрос, получая данные не со всеми постами, а только относительно определенной категории. Все существующие посты (~ 2000) будут разделены на категории. Сработает ли такая реализация быстрее? <br/> Вот пример кода для нового запроса: <br/> <pre><code class="php">$posts = get_posts( array(
            'numberposts' =&gt; -1,
            'orderby'     =&gt; 'date',
            'order'       =&gt; 'DESC',
            'category_name' =&gt; 'articles',
            'post_type'   =&gt; 'post-item',
        ) );</code></pre> <br/> Также, есть ли другие альтернативные решения для данной ситуации?
  • 7 ноября 2024 г. 11:03
Ответы на вопрос 2
Функция `get_posts()` в WordPress может выполнять запросы медленно по нескольким причинам, особенно при наличии большого количества записей. В вашем случае, увеличение `php_memory_limit` помогло временно, но оптимизация запроса будет более эффективным решением. Вот несколько рекомендаций:

### 1. Фильтрация по категории

Да, фильтрация по категории (как в вашем втором примере) должна улучшить производительность, так как это уменьшит количество постов, которые необходимо обрабатывать. Если у вас всего около 2000 постов, но вы извлекаете только посты определенной категории, это может значительно уменьшить время выполнения функции.

### 2. Используйте параметры `posts_per_page`

Установка `numberposts => -1` заставляет WordPress загружать все посты одновременно, что может вызывать проблемы с памятью. Рассмотрите возможность использования параметра `posts_per_page` с ограничением, например, 100 постов за раз, и используйте `paged` для постраничной навигации:

```php
$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;

$posts = get_posts( array(
    'posts_per_page' => 100,
    'orderby'        => 'date',
    'order'          => 'DESC',
    'category_name'  => 'articles',
    'post_type'      => 'post-item',
    'paged'          => $paged,
) );
```

### 3. Используйте WP_Query

Вместо использования `get_posts()`, вы можете использовать класс `WP_Query`, который предоставляет больше возможностей и гибкости:

```php
$query = new WP_Query( array(
    'posts_per_page' => 100,
    'orderby'        => 'date',
    'order'          => 'DESC',
    'category_name'  => 'articles',
    'post_type'      => 'post-item',
) );

while ( $query->have_posts() ) {
    $query->the_post();
    // Ваш код для обработки постов
}
wp_reset_postdata();
```

### 4. Кэширование запросов

Используйте плагин для кэширования, чтобы уменьшить количество запросов к базе данных. Это может значительно уменьшить время загрузки страниц.

### 5. Оптимизация базы данных

Если в базе данных много постов, стоит рассмотреть возможность оптимизации базы данных. Это может включать в себя очистку неиспользуемых записей, оптимизацию таблиц и т. д.

### 6. Используйте выборку полей

Если вы не нуждаетесь в полной информации о постах, можно указать `fields`, чтобы получать только необходимые данные:

```php
$posts = get_posts( array(
    'numberposts'    => -1,
    'orderby'        => 'date',
    'order'          => 'DESC',
    'category_name'  => 'articles',
    'post_type'      => 'post-item',
    'fields'         => 'ids' // Получаем только ID постов
) );
```

Попробуйте внедрить эти рекомендации, чтобы улучшить производительность вашего кода и минимизировать вероятность ошибки 500.
Но появилась идея запрашивать не все посты, а в зависимости от нужной категории. Все существующие ~ 2000 постов разделить на категории. Будет ли такая реализация работать быстрее?

Хорошая идея, конечно будет.
Либо есть какие-то другие идеи?

Не "либо", а "вместе" - почему бы не кэшировать выборку постов из БД ( Transients API )?
$cache = get_transient('all_posts_cache');

if (!$cache) {
    $posts = get_posts([
        'numberposts' => -1,
        'orderby' => 'date',
        'order' => 'DESC',
        'post_type' => 'post-item',

        // Искать по слагу категории
        'category_name' => 'category_name',

        // Или искать по id категории
        // 'cat' => 123,

    ]);

    // Кэширование на 1 час
    set_transient('all_posts_cache', $posts, HOUR_IN_SECONDS);

}

else {
    $posts = $cache;
}
Похожие вопросы