Чрезвычайно медленный отдельный запрос в индексированном столбце - PullRequest
4 голосов
/ 12 апреля 2019

В базе данных Postgres я запрашиваю различные значения MY_DATE в большой таблице с 300 миллионами строк.Их около 400, и столбец MY_DATE проиндексирован.

Select distinct  MY_DATE from MY_TABLE;

Запрос выполняется в течение 22 мин .

Тот же запрос в моей базе данных Oracleс точно таким же набором данных и тем же определением индекса выполняется 11 секунд.

План запроса показывает, что запрос использует индекс:

EXPLAIN Select distinct  MY_DATE from MY_TABLE LIMIT 200;

дает:

QUERY PLAN
Limit  (cost=0.57..7171644.14 rows=200 width=8)
  ->  Unique  (cost=0.57..15419034.24 rows=430 width=8)
        ->  Index Only Scan using idx_obsdate on my_table  (cost=0.57..14672064.14 rows=298788038 width=8)

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

Select distinct  MY_DATE from MY_TABLE LIMIT 5;

выполняется за доли секунды.

, но:

Select distinct  MY_DATE from MY_TABLE LIMIT 50;

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

Я ожидаю, что запрос Postgres будет выполняться в считанные секунды, как мой OracleDB.20 минут для сканирования индекса - даже для большой таблицы - кажутся неуместными.

Есть предложения, что вызывает проблему и что я могу сделать?

1 Ответ

4 голосов
/ 12 апреля 2019

отдельные значения ... 300 миллионов строк ... около 400 из них ... столбец ... проиндексированы.

Есть намного более быстрых методовза это.Эмулирует сканирование свободного индекса (иначе пропускаемое сканирование) и предполагает, что my_date определено NOT NULL (или мы можем игнорировать значения NULL):

WITH RECURSIVE cte AS (
   SELECT min(my_date) AS my_date
   FROM   my_table

   UNION ALL
   SELECT (SELECT my_date
           FROM   my_table 
           WHERE  my_date > cte.my_date
           ORDER  BY my_date
           LIMIT  1)
   FROM   cte
   WHERE  my_date IS NOT NULL
   )
TABLE  cte;

Связано:

Используя указанный вами индекс, он должен заканчиваться в миллисекундах .

Oracle DB ... 11 секунд.

Поскольку Oracle имеет собственные сканирования индекса, а Postgres - нет. предпринимаются постоянные усилия для реализации аналогичной функциональности в Postgres 12.

В настоящее время (Postgres 11), хотя индекс используется для хорошего эффекта, даже при сканировании только по индексу, Postgres не может пропуститьвпереди и должен читать индексные кортежи в последовательности.Без LIMIT полный индекс должен быть отсканирован.Следовательно, мы видим в вашем EXPLAIN выводе:

Index Only Scan ... <b>rows=298788038</b>

Предлагаемый новый запрос достигает того же самого с чтением 400 индексных кортежей (по одному на отдельное значение). Большая разница.

С LIMIT (и без ORDER BY!), Как вы тестировали, Postgres останавливается, как только будет получено достаточное количество строк.Увеличение лимита имеет эффект linear .Но если количество строк в отдельном значении может варьироваться, увеличивается и добавленная стоимость.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...