У меня проблема с использованием индекса Postgresql на двух разных хостах (локальном и удаленном) для одного и того же запроса. Вопрос, о котором идет речь, следующий:
SELECT COUNT(*)
FROM (
SELECT 1 AS one
FROM "big_table"
WHERE "big_table"."user_id" = 13
AND "big_table"."action" = 1
AND (big_table.created_at >= '2018-12-09 23:00:00'::timestamp without time zone)
ORDER BY big_table.created_at desc LIMIT 15 OFFSET 10
) subquery_for_count;
Я не могу изменить этот запрос, так как он генерируется библиотекой, которую мы используем, поэтому я бы хотел найти решение без необходимости Измени это. Если я запускаю команду EXPLAIN
локально с указанным запросом, мой экземпляр Postgres выводит следующее:
local_host=# EXPLAIN SELECT COUNT(*) FROM (SELECT 1 AS one FROM "big_table" WHERE "big_table"."user_id" = 13 AND "big_table"."action" = 1 AND (big_table.created_at >= '2018-12-09 23:00:00'::timestamp without time zone) ORDER BY big_table.created_at desc LIMIT 15 OFFSET 10) subquery_for_count;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------
Aggregate (cost=8.59..8.60 rows=1 width=8)
-> Limit (cost=8.57..8.58 rows=1 width=12)
-> Sort (cost=8.57..8.57 rows=1 width=12)
Sort Key: big_table.created_at DESC
-> Index Scan using big_table_idx_user_action_transfers on big_table (cost=0.56..8.56 rows=1 width=12)
Index Cond: ((user_id = 13) AND (action = 1))
Filter: (created_at >= '2018-12-09 23:00:00'::timestamp without time zone)
(7 rows)
Это нормально, он (частично) использует составной индекс для user_id
и action
как и ожидалось. Однако, если я выполню запрос в удаленной системе, я получу следующий вывод EXPLAIN
:
remote_host=# EXPLAIN SELECT COUNT(*) FROM (SELECT 1 AS one FROM "big_table" WHERE "big_table"."user_id" = 13 AND "big_table"."action" = 1 AND (big_table.created_at >= '2018-12-09 23:00:00'::timestamp without time zone) ORDER BY big_table.created_at desc LIMIT 15 OFFSET 10) subquery_for_count;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------
Aggregate (cost=8472.67..8472.68 rows=1 width=8)
-> Limit (cost=3389.25..8472.48 rows=15 width=12)
-> Index Scan Backward using index_big_table_on_created_at on big_table (cost=0.44..4492554.51 rows=13257 width=12)
Index Cond: (created_at >= '2018-12-09 23:00:00'::timestamp without time zone)
Filter: ((user_id = 13) AND (action = 1))
(5 rows)
Как видно, на удаленном хосте база данных использует индекс на created_at
вместо user_id
и action
как моя локальная установка. Это приводит к тому, что этот запрос становится слишком медленным на удаленном хосте (> 1 минуты до завершения), потому что есть много записей, которые удовлетворяют условию индекса, и фильтрация всех этих операций занимает много времени. Но на моей локальной установке это довольно быстро (~ 1 с до завершения). В моей локальной и удаленной таблицах одинаковое количество записей (~ 25 млн.) И примерно одинаковое распределение данных. Мы запускаем демон Vacuum на удаленном хосте, поэтому VACUUM ANALYZE
делается довольно часто. Кроме того, индексы настроены одинаково в обеих системах.
Я уже пытался найти решения этой проблемы, но до сих пор не нашел ничего полезного, кроме запуска VACUUM ANALYZE
и проверки существуют индексы для связанных атрибутов.
Может быть, у кого-то из вас есть подсказки? Конечно, я мог бы добавить составной индекс для всех используемых атрибутов (user_id
, action
и created_at
), но я все еще сильно озадачен тем, почему «правильный» индекс не используется в этом случае на удаленном компьютере. host.
Оба хоста используют версию 9.6 Postgres (9.6.9
на локальном хосте и 9.6.17
на удаленном хосте, если быть точным).