PostgreSQL: последняя строка в DISTINCT ON менее производительная, чем строка max в GROUP BY - PullRequest
0 голосов
/ 21 февраля 2019

У меня есть ситуация, которую я хотел бы лучше понять:

У меня есть таблица t с двумя строками и одним индексом:

CREATE TABLE t (
  refid                 BIGINT NOT NULL,
  created               TIMESTAMPTZ NOT NULL
);
CREATE INDEX t_refid_created ON t (refid, created);

Чтобы получитьСамая последняя (с наибольшим created значением) строка для каждого отдельного refid, я скомпоновал два запроса:

-- index only scan t_refid_created_desc_idx
SELECT DISTINCT ON (refid) * FROM t
ORDER BY refid, created DESC;

-- index scan t_refid_created_idx 
SELECT refid, max(created) FROM t GROUP BY refid;

Когда t имеет около 16M строк и дисперсия в refid составляет около 500При разных значениях второй запрос возвращается значительно быстрее, чем второй.

Сначала я подумал, что, поскольку я упорядочиваю по created DESC, необходимо выполнить сканирование индекса в обратном направлении и при запуске со значения с высокимДисперсия (создана).Поэтому я добавил следующий индекс:

CREATE index t_refid_created_desc_idx ON t (refid, created DESC);

Он действительно использовался (вместо сканирования назад по предыдущему индексу), но улучшения не было.

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

Очевидно, что это не так, и запрос SELECT DISTINCT занимает в два раза больше времени, чем GROUP BY.

Что мне здесь не хватает?

Вот EXPLAIN ANALYZE выходы для первого и второгозапросы:

Unique  (cost=0.56..850119.78 rows=291 width=16) (actual time=0.103..13414.913 rows=469 loops=1)
  ->  Index Only Scan using t_refid_created_desc_idx on t  (cost=0.56..808518.47 rows=16640527 width=16) (actual time=0.102..12113.454 rows=16640527 loops=1)
        Heap Fetches: 16640527
Planning time: 0.157 ms
Execution time: 13415.047 ms
Finalize GroupAggregate  (cost=599925.13..599932.41 rows=291 width=16) (actual time=3454.350..3454.884 rows=469 loops=1)
  Group Key: refid
  ->  Sort  (cost=599925.13..599926.59 rows=582 width=16) (actual time=3454.344..3454.509 rows=1372 loops=1)
        Sort Key: refid
        Sort Method: quicksort  Memory: 113kB
        ->  Gather  (cost=599837.29..599898.40 rows=582 width=16) (actual time=3453.194..3560.602 rows=1372 loops=1)
              Workers Planned: 2
              Workers Launched: 2
              ->  Partial HashAggregate  (cost=598837.29..598840.20 rows=291 width=16) (actual time=3448.225..3448.357 rows=457 loops=3)
                    Group Key: refid
                    ->  Parallel Seq Scan on t  (cost=0.00..564169.53 rows=6933553 width=16) (actual time=0.047..2164.459 rows=5546842 loops=3)
Planning time: 0.157 ms
Execution time: 3561.727 ms

Первый запрос выполняется примерно за 10 секунд, а второй - за 2 секунды!И даже без использования индекса!

Я использую PostgreSQL 10.5.

1 Ответ

0 голосов
/ 21 февраля 2019

Я не могу ответить на загадку, почему DISTINCT ON не учитывает второй план.Из оценки стоимости мы видим, что PostgreSQL считает ее более дешевой.

Я полагаю, что никто не реализовал опускание DISTINCT в параллельные планы.Вы могли бы спросить список рассылки.

Однако проблема с первым запросом - 16 миллионов выборок кучи.Это означает, что это на самом деле нормальное сканирование индекса!На стороне планировщика это выглядит как плохая недооценка.

Если я прав, то VACUUM в таблице, которая очищает карту видимости , должно значительно улучшить первый запрос.

...