Расширение WP_Query — оптимизация SQL-запроса

Вопрос или проблема

Я сохраняю посты, которые пользователь подписывает в нестандартной таблице, содержащей столбцы id, post_id и user_id. Чтобы получить посты, которые пользователь подписывает, я расширил WP_Query следующим образом:

class WP_Query_Posts_User_Follows extends WP_Query {
    function __construct($args=array()) {
        if ( !empty($args['followed_by']) ) {
            $this->followed_by = $args['followed_by'];
            add_filter('posts_where', array($this, 'posts_where'));
        }
        parent::query($args);
    }

    function posts_where($where) {
        global $wpdb;
        $table = $wpdb->prefix . 'post_followed_by';
        $where .= $wpdb->prepare(" AND ID IN (SELECT post_id FROM $table WHERE user_id = %d)", $this->followed_by);
        return $where;
    }
}

Если вы заметите, в условии WHERE используется подзапрос. Мое понимание таково, что подзапросы нежелательны, так как они ухудшают производительность, особенно в этом случае, когда подзапрос может потенциально вернуть сотни или тысячи post_ids, на которые подписан пользователь. Какие у меня есть альтернативы, учитывая, что мне нужно работать с WP_Query и я не могу выполнять пользовательский SQL непосредственно с использованием wpdb?

Вот идея, хотя она потребует возможности выполнения пользовательских SQL-запросов – с $wpdb.

Поскольку вы хотите получить посты, на которые подписан пользователь, вы можете создать представление со следующей структурой:

id user_id post_id post_title post_content ... (все другие поля постов)

не беспокойтесь о дублировании постов.

Когда вы выбираете, вам просто нужно выполнять простой select * from the_view where....

Позже вы можете кэшировать результаты этого представления, используя memcache или другую технику кэширования объектов для ускорения.

Вы используете лучший способ:) Может быть, если вы хотите оптимизировать ваш запрос, вы можете использовать плагин posts 2 posts. Вам не нужно будет фильтровать запрос. Ссылка – https://github.com/scribu/wp-posts-to-posts/wiki

Ответ или решение

Оптимизация SQL-запросов является важной задачей при работе с базами данных, особенно когда речь идет о большом объеме данных и высоких требованиях к производительности. В вашем случае, где вы расширяете WP_Query для получения постов, на которые подписан пользователь, использование подзапросов в секции WHERE действительно может негативно сказаться на производительности, особенно если подзапрос возвращает сотни или тысячи идентификаторов постов. Давайте рассмотрим возможные альтернативы и оптимизации для вашего сценария.

Проблема

Вы храните информацию о подписках пользователей в кастомной таблице с колонками id, post_id, user_id и используете подзапрос для получения постов подписок:

function posts_where($where) {
    global $wpdb;
    $table = $wpdb->prefix . 'post_followed_by';
    $where .= $wpdb->prepare(" AND ID IN (SELECT post_id FROM $table WHERE user_id = %d)", $this->followed_by);
    return $where;
}

Возможные альтернативы

  1. Использование JOIN: Вместо подзапроса, вы можете использовать соединение (JOIN), что обычно более эффективно для баз данных. Вам понадобится написать свой собственный метод для соединения таблиц, например:

    function posts_join($join) {
        global $wpdb;
        $table = $wpdb->prefix . 'post_followed_by';
        $join .= " INNER JOIN $table ON $wpdb->posts.ID = $table.post_id ";
        return $join;
    }
    
    function posts_where($where) {
        global $wpdb;
        $where .= $wpdb->prepare(" AND $table.user_id = %d", $this->followed_by);
        return $where;
    }

    Однако при этом нет возможности напрямую реализовать такой подход в WP_Query без использования фильтров posts_join и posts_where.

  2. Создание индексов: Убедитесь, что в вашей кастомной таблице созданы индексы по user_id и post_id. Это значительно увеличит скорость выборки при выполнении запросов.

  3. Предварительная загрузка данных: Иногда проще и быстрее заранее загрузить все post_id, на которые подписан пользователь, и использовать их в несложном WHERE IN() запросе. Этот подход может быть реалистичнее, если общая информация о подписках сравнительно невелика.

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

Другие подходы

Если у вас есть возможность использовать более кастомный подход, например, в виде плагина "Posts 2 Posts" или реализации собственных виджетов, это может также оптимизировать процесс за счет уменьшения логики на уровне SQL-запросов.

Заключение

Каждый из предложенных методов имеет свои плюсы и минусы, и выбор зависит от специфики вашего проекта и объема данных. Применение комбинации этих методов может значительно улучшить производительность ваших запросов в WordPress. Концентрируйтесь на оптимизации за счет уменьшения нагрузки на базу данных и использования кэширования для уменьшения задержек в работе приложений.

Оцените материал
Добавить комментарий

Капча загружается...