Как заставить Postgres использовать определенный индекс? - PullRequest
88 голосов
/ 21 ноября 2008

Как заставить Postgres использовать индекс, если в противном случае он будет настаивать на последовательном сканировании?

Ответы [ 7 ]

84 голосов
/ 21 ноября 2008

Предполагая, что вы спрашиваете об общей функции «индексации» во многих базах данных, PostgreSQL не предоставляет такую ​​функцию. Это было сознательное решение, принятое командой PostgreSQL. Хороший обзор того, почему и что вы можете сделать вместо этого, можно найти здесь . Причины в основном состоят в том, что это снижение производительности, которое имеет тенденцию вызывать больше проблем в дальнейшем по мере изменения ваших данных, тогда как оптимизатор PostgreSQL может пересмотреть план на основе статистики. Другими словами, то, что сегодня может быть хорошим планом запросов, вероятно, не будет хорошим планом запросов на все времена, а подсказки индекса заставляют определенный план запросов на все времена.

В качестве очень тупого молотка, полезного для тестирования, вы можете использовать параметры enable_seqscan и enable_indexscan. См:

Эти не пригодны для непрерывного производственного использования . Если у вас есть проблемы с выбором плана запроса, вы должны увидеть документацию для отслеживания проблем производительности запросов . Не устанавливайте enable_ параметров и уходите.

Если у вас нет веских причин для использования индекса, Postgres может сделать правильный выбор. Почему?

  • Для небольших таблиц последовательное сканирование выполняется быстрее.
  • Postgres не использует индексы, когда типы данных не совпадают, вам может потребоваться включить соответствующие приведения.
  • Возможно, настройки вашего планировщика вызывают проблемы.

См. Также этот старый пост группы новостей .

59 голосов
/ 16 ноября 2012

Вероятно, единственная действительная причина для использования

set enable_seqscan=false

- это когда вы пишете запросы и хотите быстро увидеть, каким был бы план запроса, если бы в таблицах были большие объемы данных. Или, конечно, если вам нужно быстро подтвердить, что ваш запрос не использует индекс, просто потому, что набор данных слишком мал.

11 голосов
/ 16 июня 2015

Иногда PostgreSQL не может сделать лучший выбор индексов для определенного условия. В качестве примера, предположим, что есть таблица транзакций с несколькими миллионами строк, из которых несколько сотен на любой данный день, и таблица имеет четыре индекса: транзакция_идентификатор, идентификатор_пользователя, дата и описание Вы хотите выполнить следующий запрос:

SELECT client_id, SUM(amount)
FROM transactions
WHERE date >= 'yesterday'::timestamp AND date < 'today'::timestamp AND
      description = 'Refund'
GROUP BY client_id

PostgreSQL может использовать индекс index_description_idx вместо транзакции_date_idx, что может привести к тому, что запрос займет несколько минут вместо одной секунды. Если это так, вы можете принудительно использовать индекс на дату, выдумав условие, подобное этому:

SELECT client_id, SUM(amount)
FROM transactions
WHERE date >= 'yesterday'::timestamp AND date < 'today'::timestamp AND
      description||'' = 'Refund'
GROUP BY client_id
10 голосов
/ 09 июля 2009

Вопрос сам по себе очень недействителен. Форсировать (например, с помощью enable_seqscan = off) очень плохая идея. Возможно, было бы полезно проверить, будет ли это быстрее, но производственный код никогда не должен использовать такие приемы.

Вместо этого - объясните анализ вашего запроса, прочитайте его и выясните, почему PostgreSQL выбирает плохой (по вашему мнению) план.

В Интернете есть инструменты, которые помогают с чтением объяснить результаты анализа - одним из них является объяснение.depesz.com - написанное мной.

Другой вариант - присоединиться к каналу #postgresql в freenode irc сети и поговорить с парнями, которые помогут вам - так как оптимизация запроса не сводится к «задайте вопрос, получите ответ, будьте счастливы» , это больше похоже на беседу, в которой нужно многое проверить, чему можно научиться.

4 голосов
/ 16 октября 2018

Краткий ответ

Эта проблема обычно возникает, когда оценочная стоимость сканирования индекса слишком высока и не соответствует действительности. Вам может потребоваться уменьшить параметр конфигурации 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 должно быть установлено на 2.0 или ниже для твердотельных накопителей.

2) Сильно кэшированные данные

Соответственно, если ваши данные, вероятно, будут полностью в кеше, [...] может быть целесообразным уменьшение random_page_cost.

Если вы знаете, что индекс полностью кэшируется в ОЗУ (для этого вы также можете использовать расширение pg_prewarm ), для random_page_cost следует даже установить 1.0.


1 голос
/ 27 октября 2017

Существует способ подтолкнуть postgres, чтобы предпочесть seqscan, добавив OFFSET 0 в подзапрос

Это удобно для оптимизации запросов, связывающих большие / огромные таблицы, когда все, что вам нужно, это только элементы n fist / last.

Допустим, вы ищете первые / последние 20 элементов, включающие в себя несколько таблиц, содержащих 100 тыс. (Или более) записей, без необходимости строить точки / связывать весь запрос по всем данным, когда то, что вы ищете, находится в первом 100 или 1000 записей. Например, в этом сценарии последовательное сканирование выполняется в 10 раз быстрее.

см. Как я могу запретить Postgres вставлять подзапрос?

0 голосов
/ 09 июля 2009

Продукт EnterpriseDB PostgresPlus Advanced Server поддерживает синтаксис подсказок Oracle, хотя этот продукт не является бесплатным.

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