#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
не так, просто неэлегантно. Я добавил объяснение.