Как заставить Postgres использовать индекс, если в противном случае он настаивал бы на последовательном сканировании?
Как заставить Postgres использовать индекс, если в противном случае он настаивал бы на последовательном сканировании?
Ответы:
Предполагая, что вы спрашиваете об общей функции «подсказки индекса», которая есть во многих базах данных, PostgreSQL не предоставляет такой возможности. Это было сознательное решение команды PostgreSQL. Хороший обзор того, почему и что вы можете сделать вместо этого, можно найти здесь . Причины в основном в том, что это взлом производительности, который, как правило, вызывает больше проблем позже, когда ваши данные изменяются, тогда как оптимизатор PostgreSQL может повторно оценить план на основе статистики. Другими словами, то, что могло бы быть хорошим планом запроса сегодня, вероятно, не будет хорошим планом запроса на все времена, а подсказки индексов навязывают конкретный план запроса на все времена.
Как очень тупой молоток, полезно для тестирования, вы можете использовать enable_seqscan
и enable_indexscan
параметры. Видеть:
Они не подходят для постоянного производственного использования . Если у вас возникли проблемы с выбором плана запроса, вам следует ознакомиться с документацией по отслеживанию проблем с производительностью запроса . Не просто устанавливайте enable_
параметры и уходите.
Если у вас нет веских причин для использования индекса, Postgres может сделать правильный выбор. Зачем?
См. Также это старое сообщение группы новостей .
Вероятно, единственная веская причина для использования
set enable_seqscan=false
это когда вы пишете запросы и хотите быстро увидеть, каким был бы план запроса, если бы в таблице (ах) были большие объемы данных. Или, конечно, если вам нужно быстро подтвердить, что ваш запрос не использует индекс просто потому, что набор данных слишком мал.
set enable_seqscan=false
, запустите свой запрос, а затем быстро запустите, set enable_seqscan=true
чтобы вернуть postgresql его правильное поведение (и, очевидно, не делайте этого в производственной
SET SESSION enable_seqscan=false
чтобы повлиять только на себя
Иногда PostgreSQL не может выбрать наилучший индекс для определенного условия. В качестве примера предположим, что есть таблица транзакций с несколькими миллионами строк, из которых несколько сотен на любой день, и таблица имеет четыре индекса: transaction_id, client_id, date и description. Вы хотите выполнить следующий запрос:
SELECT client_id, SUM(amount)
FROM transactions
WHERE date >= 'yesterday'::timestamp AND date < 'today'::timestamp AND
description = 'Refund'
GROUP BY client_id
PostgreSQL может выбрать использование индекса transaction_description_idx вместо transaction_date_idx, что может привести к тому, что запрос займет несколько минут вместо менее одной секунды. Если это так, вы можете принудительно использовать индекс по дате, подставив условие следующим образом:
SELECT client_id, SUM(amount)
FROM transactions
WHERE date >= 'yesterday'::timestamp AND date < 'today'::timestamp AND
description||'' = 'Refund'
GROUP BY client_id
your_wanted_index
, возможно, что механизм postgresql просто выполнит вместо этого сканирование последовательности / первичного ключа. Заключение - не существует 100% надежного метода принудительного использования индекса для сервера PostgreSql.
where
условия, кроме двух таблиц или соединенных, и Postgres не может принять индекс.
Эта проблема обычно возникает, когда оценочная стоимость сканирования индекса слишком высока и не соответствует действительности. Вам может потребоваться уменьшить random_page_cost
параметр конфигурации, чтобы исправить это. Из документации Postgres :
Уменьшение этого значения [...] приведет к тому, что система предпочтет сканирование индекса; его повышение сделает сканирование индекса относительно более дорогим.
Вы можете проверить, действительно ли более низкое значение заставит Postgres использовать индекс (но используйте это только для тестирования ):
EXPLAIN <query>; # Uses sequential scan
SET random_page_cost = 1;
EXPLAIN <query>; # May use index scan now
Вы можете SET random_page_cost = DEFAULT;
снова восстановить значение по умолчанию, нажав .
Для сканирования индекса требуется непоследовательная выборка страниц с диска. Postgres использует random_page_cost
для оценки стоимости таких непоследовательных выборок по сравнению с последовательными выборками. Значение по умолчанию равно 4.0
, таким образом, предполагается, что средний коэффициент затрат равен 4 по сравнению с последовательными выборками (с учетом эффектов кэширования).
Однако проблема в том, что это значение по умолчанию не подходит в следующих важных реальных сценариях:
1) твердотельные накопители
Как говорится в документации:
Хранилище, которое имеет более низкую стоимость произвольного чтения по сравнению с последовательным, например твердотельные накопители, может быть лучше смоделировано с более низким значением для
random_page_cost
.
Согласно последнему пункту этого слайда из выступления на PostgresConf 2018, random_page_cost
следует установить что-то среднее 1.0
и 2.0
для твердотельных накопителей.
2) Кешированные данные
Если необходимые данные индекса уже кэшированы в ОЗУ, сканирование индекса всегда будет значительно быстрее, чем последовательное сканирование. В документации говорится:
Соответственно, если ваши данные, вероятно, полностью находятся в кеше, [...] уменьшение
random_page_cost
может быть целесообразным.
Проблема в том, что вы, конечно, не можете легко узнать, кэшированы ли уже соответствующие данные. Однако, если конкретный индекс запрашивается часто и если в системе достаточно ОЗУ, данные, скорее всего, будут кэшироваться, и random_page_cost
их следует установить на более низкое значение. Вам придется поэкспериментировать с разными значениями и посмотреть, что вам подходит.
Вы также можете использовать расширение pg_prewarm для явного кэширования данных.
Сам по себе вопрос очень некорректный. Принудительное использование (например, enable_seqscan = off) - очень плохая идея. Было бы полезно проверить, будет ли он быстрее, но производственный код никогда не должен использовать такие уловки.
Вместо этого - объясните анализ вашего запроса, прочтите его и выясните, почему PostgreSQL выбирает плохой (на ваш взгляд) план.
В Интернете есть инструменты, которые помогают с чтением результатов объяснения и анализа - один из них - execute.depesz.com - написанный мной.
Другой вариант - присоединиться к каналу #postgresql в сети freenode irc и поговорить с парнями, чтобы они вам помогли - поскольку оптимизация запроса не сводится к тому , чтобы «задать вопрос, получить ответ и быть счастливым». это больше похоже на беседу, в которой нужно многое проверить и многому научиться.
Существует трюк, чтобы заставить postgres предпочесть seqscan, добавив OFFSET 0
в подзапрос
Это удобно для оптимизации запросов, связывающих большие / огромные таблицы, когда все, что вам нужно, это только n первых / последних элементов.
Допустим, вы ищете первые / последние 20 элементов, включающих несколько таблиц, содержащих 100 тыс. (Или более) записей, без создания / связывания всего запроса по всем данным, когда то, что вы будете искать, находится в первых 100 или 1000 записи. В этом случае, например, последовательное сканирование оказывается более чем в 10 раз быстрее.