2011-12-19 2 views
7

У меня есть таблица слов с индексом на (language_id, state). Ниже приведены результаты для EXPLAIN ANALYZE:Индекс не используется, когда LIMIT используется в postgres

Нет ограничений

explain analyze SELECT "words".* FROM "words" WHERE (words.language_id = 27) AND (state IS NULL); 

Bitmap Heap Scan on words (cost=10800.38..134324.10 rows=441257 width=96) (actual time=233.257..416.026 rows=540556 loops=1) 
Recheck Cond: ((language_id = 27) AND (state IS NULL)) 
-> Bitmap Index Scan on ls (cost=0.00..10690.07 rows=441257 width=0) (actual time=230.849..230.849 rows=540556 loops=1) 
Index Cond: ((language_id = 27) AND (state IS NULL)) 
Total runtime: 460.277 ms 
(5 rows) 

Limit 100

explain analyze SELECT "words".* FROM "words" WHERE (words.language_id = 27) AND (state IS NULL) LIMIT 100; 

Limit (cost=0.00..51.66 rows=100 width=96) (actual time=0.081..0.184 rows=100 loops=1) 
-> Seq Scan on words (cost=0.00..227935.59 rows=441257 width=96) (actual time=0.080..0.160 rows=100 loops=1) 
Filter: ((state IS NULL) AND (language_id = 27)) 
Total runtime: 0.240 ms 
(4 rows) 

Почему это происходит? Как я могу заставить индекс использоваться во всех случаях?

Спасибо.

+4

LIMIT без ORDER BY может показаться ограниченным (не предназначенным для каламбур) значением. Какие 100 строк вы ожидаете вернуть? –

+0

BTW: какова избирательность выражения language_id = 17 И статус IS NULL? каков общий размер слова-таблицы? – wildplasser

+0

true ... порядок выполняется по идентификатору DESC. Может ли это замедлиться? Нужен ли индекс для этого столбца? Общий объем @wildplasser составляет 10 миллионов строк, избирательность составляет около 500 000 строк. – alste

ответ

7

Я думаю, что планировщик запросов PostreSQL просто думает, что во втором случае - с LIMIT - это не стоит применять индекс, поскольку он [LIMIT] слишком мал. Так что это не проблема.

+0

Вы правы, Но как я могу избежать этого? Фактически я использую индекс jsonb gin, и из-за ограничения 1 этот индекс не привыкает и занимает огромное время. – Anurag

0

Странно, что два запроса возвращают другое количество строк. Я предполагаю, что вы вставляете, хотя ... Э-э, что, если вы сделаете подвыбор?

select * from (select ...) limit 100; 
+1

"(5 строк)" относится к числу строк в плане объяснения, а не к количеству строк, которые возвращает запрос – Anna

3

Без предела: строки = 540556 циклов = 1 Общее время выполнения: 460.277 мс

С пределом: строки = 100 петель = 1 Общее время выполнения: 0.240 мс

Я не вижу проблемы Вот. Если ваш запрос дает 500K строк, ему потребуется больше времени.

3

Взгляните на документацию PostgreSQL о Using EXPLAIN и Query Planning. Причина, по которой планировщик запросов предпочитает последовательное сканирование по сканированию индекса в случае LIMIT 100, объясняется просто тем, что последовательное сканирование дешевле.

В запросе нет предложения ORDER BY, поэтому планировщик в порядке с первыми 100 (случайными) строками, которые соответствуют условию фильтра. Для сканирования индекса потребуется сначала прочитать страницы индекса, а затем прочитать страницы данных для получения соответствующих строк. Для последовательного сканирования требуется только чтение страниц данных для извлечения строк. В вашей таблице статистики таблицы, кажется, предполагается, что существует достаточно (случайных) строк, которые соответствуют условию фильтра. Стоимость последовательного чтения страниц для получения 100 строк считается более дешевой, чем стоимость первого чтения индекса, а затем выборка фактических строк. Вы можете увидеть другой план, когда вы поднимаете лимит или когда меньшее количество строк соответствует условию фильтра.

С настройками по умолчанию планировщик рассматривает стоимость случайной страницы читать (random_page_cost) четыре раза стоимость последовательной страницы чтения (seq_page_cost). Эти настройки можно настроить для настройки планов запросов (например, когда вся база данных находится в ОЗУ, случайное считывание страницы не является более дорогостоящим, чем последовательное чтение страницы, и желательно, чтобы индексная проверка была предпочтительной). Вы также можете попробовать различные планы запросов путем включения/отключения определенных видов сканирования, например:

set enable_seqscan = [on | off] 
set enable_indexscan = [on | off] 

Хотя можно включить/отключить определенные виды сканирования на глобальной основе этого следует использовать только специальные для отладки или устранения неполадок для каждого сеанса.

Также запустите VACUUM ANALYZE words, прежде чем тестировать планы запросов, в противном случае автоматический автоматический (autovaccum) может повлиять на результаты.

Смежные вопросы