postgres использует только отсортированный индекс для запроса таблицы 5m записей

#postgresql

#postgresql

Вопрос:

У меня возникли некоторые проблемы с таблицей записей 5M. у него есть несколько индексов.

это тот, который используется:

 CREATE INDEX emails_ordered_created_and_keys
ON vsko_mailer_api_prod.emails USING btree
("$meta.created" ASC NULLS LAST, key ASC NULLS LAST)
TABLESPACE pg_defau<
 

Я делаю запрос, подобный этому:

 SELECT
    "key",
    "job",
    "sentDate",
    "scheduledDate",
    "status",
    "recipient",
    "opens",
    "clicks",
    "smtpEvents",
    "$meta.deleted",
    "$meta.created",
    "$meta.modified",
    "$meta.version",
    "$meta.deleted",
    "$meta.created",
    "$meta.modified"
FROM
    "emails"
WHERE
    "emails"."$meta.deleted" = FALSE
    AND ("emails"."$meta.created" > '2018-02-13T14:30:35.679075Z'
        OR ("emails"."$meta.created" = '2018-02-13T14:30:35.679075Z'
            AND "emails"."key" > '8c0a3151-bf17-490f-8124-d93f7482624f'))
ORDER BY
    "$meta.created" ASC,
    "key" ASC
LIMIT '500'
 

Проблема в том, что он использует только индекс emails_ordered_created_and_keys .

 "Limit  (cost=0.11..192.72 rows=500 width=764) (actual time=1.122..1.701 rows=500 loops=1)"
"  ->  Index Scan using emails_ordered_created_and_keys on emails  (cost=0.11..1966725.65 rows=5105459 width=764) (actual time=1.120..1.664 rows=500 loops=1)"
"        Filter: ((NOT ""$meta.deleted"") AND ((""$meta.created"" > '2018-02-13 14:30:35.679075 00'::timestamp with time zone) OR ((""$meta.created"" = '2018-02-13 14:30:35.679075 00'::timestamp with time zone) AND (key > '8c0a3151-bf17-490f-8124-d93f7482624f'::uuid))))"
"        Rows Removed by Filter: 1000"
"Planning Time: 0.297 ms"
"Execution Time: 1.749 ms" 
 

читаемая версия здесь: https://explain.depesz.com/s/ajW2

поскольку я иду вниз по таблице, к тому времени, когда я нахожусь в 2019 году, ему приходится отфильтровывать так много строк, что запрос занимает целую вечность.

Я не уверен, какой индекс мне понадобится для этого случая. У меня тоже есть индекс $$meta.created . (пример: https://explain.depesz.com/s/Ju9O )

Комментарии:

1. Не имеет отношения к вашей проблеме, но: вам действительно следует избегать этих страшных идентификаторов в кавычках. Они доставляют гораздо больше хлопот, чем того стоят. wiki.postgresql.org/wiki /…

2. @a_horse_with_no_name хотел бы я!

Ответ №1:

Измените запрос на

 WHERE NOT emails."$meta.deleted" = FALSE
  AND (emails."$meta.created", emails.key) >
      ('2018-02-13T14:30:35.679075Z', '8c0a3151-bf17-490f-8124-d93f7482624f')
 

и используйте этот индекс:

 CREATE INDEX ON emails ("$meta.created", key)
   WHERE NOT emails."$meta.deleted";
 

Объяснение: OR проблема с производительностью. Переписывая его как лексикографическое сравнение пар, вы избавляетесь от OR , и простое сканирование индекса эффективно находит строки. PostgreSQL больше не испытывает соблазна использовать индекс, который поддерживает ORDER BY .

Комментарии:

1. я собираюсь предположить, что = FALSE следует опустить 🙂

2. 0,7 мс. это удивительно. можете ли вы объяснить, почему это имеет такое большое значение?

3. Это = FALSE не так, просто неэлегантно. Я добавил объяснение.