2013-12-04 8 views
5

У меня есть четыре стола; два для текущих данных, два для архивных данных. Одна из архивных таблиц содержит десятки миллионов строк. Все таблицы имеют несколько узких индексов и очень похожи.postgres не использует индекс для SELECT COUNT (*) для большой таблицы

Учитывая следующие запросы:

SELECT (SELECT COUNT(*) FROM A) 
UNION SELECT (SELECT COUNT(*) FROM B) 
UNION SELECT (SELECT COUNT(*) FROM C_LargeTable) 
UNION SELECT (SELECT COUNT(*) FROM D); 

А, В и D выполняют сканирование индекса. C_LargeTable использует seq scan, и запрос занимает около 20 секунд. Таблица D имеет также миллионы строк, но составляет лишь около 10% от размера C_LargeTable

Если я затем изменяю свой запрос на выполнение, используя следующую логику, которая достаточно сужает количество отсчетов, я все равно получаю те же результаты, используется индекс и запрос занимает около 5 секунд, или 1/4th времени

... 
SELECT (SELECT COUNT(*) FROM C_LargeTable WHERE idx_col < 'G') 
     + (SELECT COUNT(*) FROM C_LargeTable WHERE idx_col BETWEEN 'G' AND 'Q') 
     + (SELECT COUNT(*) FROM C_LargeTable WHERE idx_col > 'Q') 
... 

Это не имеет смысл для меня, чтобы иметь I/O накладные расходы полного сканирования таблицы для подсчета, когда отлично хорошо существуют индексы, и есть первичный ключ покрытия, который обеспечивал бы уникальность. Мое понимание postgres заключается в том, что PRIMARY KEY не похож на индекс кластеризации SQL Server, поскольку он определяет сортировку, но он неявно создает индекс btree для обеспечения уникальности, который, как я полагаю, должен требовать значительно меньше ввода-вывода, чем полное сканирование таблицы ,

Является ли это потенциально индикатором оптимизации, которую может потребоваться для организации данных в C_LargeTable?

+2

Какую версию Postgres вы используете? Только индекс 9.2 и выше сможет использовать индекс. Это также FAQ: https://wiki.postgresql.org/wiki/FAQ#Why_is_.22SELECT_count.28.2A.29_FROM_bigtable.3B.22_slow.3F –

+0

Я запускаю 9.3. Ваш ответ объяснил, почему это происходит. Есть ли рекомендация сделать что-то еще? Я попытался установить enable_seqcan = false, и он, похоже, не дал большой разницы в производительности (после этого я вернул его в true). –

+1

Вы также можете прочитать: https://wiki.postgresql.org/wiki/Slow_Query_Questions –

ответ

7

На первичный ключ нет индекса покрытия, потому что PostgreSQL не поддерживает их (правда до 9.4 включительно).

Сканирование кучи требуется из-за MVCC visibility. Индекс не содержит информации о видимости. Pg может выполнять сканирование индексов, но по-прежнему необходимо проверять информацию о видимости из кучи и с помощью сканирования индекса, которое было бы случайным вводом-выводом для чтения всей таблицы, поэтому seqscan будет намного быстрее.

Убедитесь, что вы используете 9.2 или новее, и что autovacuum является configured to run frequently на столе. Затем вы можете выполнить сканирование только по индексу, где используется карта видимости. Это работает только при ограниченных обстоятельствах, таких как конные ноты; см. the wiki page on count и на index-only scans. Если вы не позволяете автоавтокуму работать достаточно регулярно, visibility map будет устаревшим, и Pg не сможет выполнить сканирование только по индексу.

В будущем обязательно отправьте сообщение explain или желательно explain analyze с любыми запросами.

+0

«Индекс не содержит информации о видимости». Это имеет смысл! С учетом сказанного, есть ли способ выполнить подсчет, не заботясь о том, совершены ли данные или нет? –

+1

@Alan Нет, это не так, потому что это не просто нефиксированные данные - это также удаленные строки, старые версии обновленных строк и т. Д. Вы можете получить приближение от pg_statistic, если ваш autovacuum работает достаточно часто; см. ссылку на страницу wiki count выше. –

+0

Для уточнения, прежде чем я отвечу на этот ответ, проблема, которую я испытываю, является результатом дизайна postgresql. Если для меня было реальным требованием выполнить быстрый подсчет, я полагаю, что стандартная практика заключается в сохранении таких данных в другом месте и обновлении этого количества, например, при добавлении сохраняемого значения в триггер строки INSERT и уменьшении с помощью триггера DELETE? –

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