Оптимизация запросов Postgres (принудительное сканирование индекса)

Ниже мой запрос. Я пытаюсь использовать его для сканирования индекса, но он будет выполнять только сканирование.

Кстати, таблица metric_data содержит 130 миллионов строк. Таблица metrics имеет около 2000 строк.

metric_data столбцы таблицы:

  metric_id integer
, t timestamp
, d double precision
, PRIMARY KEY (metric_id, t)

Как я могу заставить этот запрос использовать мой индекс PRIMARY KEY?

SELECT
    S.metric,
    D.t,
    D.d
FROM metric_data D
INNER JOIN metrics S
    ON S.id = D.metric_id
WHERE S.NAME = ANY (ARRAY ['cpu', 'mem'])
  AND D.t BETWEEN '2012-02-05 00:00:00'::TIMESTAMP
              AND '2012-05-05 00:00:00'::TIMESTAMP;

EXPLAIN:

Hash Join  (cost=271.30..3866384.25 rows=294973 width=25)
  Hash Cond: (d.metric_id = s.id)
  ->  Seq Scan on metric_data d  (cost=0.00..3753150.28 rows=29336784 width=20)
        Filter: ((t >= '2012-02-05 00:00:00'::timestamp without time zone)
             AND (t <= '2012-05-05 00:00:00'::timestamp without time zone))
  ->  Hash  (cost=270.44..270.44 rows=68 width=13)
        ->  Seq Scan on metrics s  (cost=0.00..270.44 rows=68 width=13)
              Filter: ((sym)::text = ANY ('{cpu,mem}'::text[]))

Ответ 1

В целях тестирования вы можете принудительно использовать индекс путем "отключения" последовательного сканирования в текущем сеансе:

SET enable_seqscan = OFF;

Подробности в руководстве здесь. Я процитировал "disabling", потому что вы фактически не можете отключить последовательные сканирование таблицы. Но любой другой доступный вариант теперь предпочтительнее для Postgres. Это докажет, что можно использовать многоколоночный индекс на (metric_id, t) - не так эффективен, как индекс в ведущем столбце.

Вероятно, вы получите лучшие результаты (не создавая больше индексов), если вы переключите порядок в вашем PRIMARY KEY на (t, metric_id).

Обратите внимание, что обычно вам не требуется форсировать лучший план запроса вручную. Если настройка enable_seqscan = OFF приведет к значительно лучшему плану, возможно, что-то не так. Рассмотрим этот ответ:

Ответ 2

Вы не можете принудительно выполнить сканирование индекса в этом случае, потому что оно не ускорится.

В настоящее время у вас есть индекс на metric_data (metric_id, t), но сервер не может воспользоваться этим индексом для вашего запроса, поскольку он должен быть способен распознавать только metric_data.t (без metric_id), но такого индекса нет, Сервер может использовать подполя в составных индексах, но только с самого начала. Например, поиск по metric_id сможет использовать этот индекс.

Если вы создадите еще один индекс в metric_data (t), ваш запрос будет использовать этот индекс и будет работать намного быстрее.

Кроме того, вы должны убедиться, что у вас есть индекс на metrics (id).

Ответ 3

Кажется, вам не хватает подходящих ограничений FK:

CREATE TABLE metric_data
( metric_id integer
, t timestamp
, d double precision
, PRIMARY KEY (metric_id, t)
, FOREIGN KEY metrics_xxx_fk (metric_id) REFERENCES metrics (id)
)

и в табличных метриках:

CREATE TABLE metrics
( id INTEGER PRIMARY KEY
...
);

Также проверьте, достаточно ли вашей статистики (и достаточно тонкой, поскольку вы намереваетесь выбрать 0,2% таблицы metrics_data).

Ответ 4

Вы пытались использовать:

WHERE S.NAME = ANY (VALUES ('cpu'), ('mem')) вместо ARRAY

как здесь