Почему PostgreSQL выполняет последовательное сканирование индексированного столбца?

151

Очень простой пример - одна таблица, один индекс, один запрос:

CREATE TABLE book
(
  id bigserial NOT NULL,
  "year" integer,
  -- other columns...
);

CREATE INDEX book_year_idx ON book (year)

EXPLAIN
 SELECT *
   FROM book b
  WHERE b.year > 2009

дает мне:

Seq Scan on book b  (cost=0.00..25663.80 rows=105425 width=622)
  Filter: (year > 2009)

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

Алекс Вайда
источник

Ответы:

223

Если SELECT возвращает более 5-10% всех строк в таблице, последовательное сканирование выполняется намного быстрее, чем сканирование индекса.

Это связано с тем, что для сканирования индекса требуется несколько операций ввода-вывода для каждой строки (найдите строку в индексе, затем извлеките строку из кучи). В то время как для последовательного сканирования требуется только один ввод-вывод для каждой строки - или даже меньше, поскольку блок (страница) на диске содержит более одной строки, поэтому более одной строки можно извлечь с помощью одной операции ввода-вывода.

Между прочим: это верно и для других СУБД - некоторые оптимизации, такие как «сканирование только по индексу», не учитываются (но для SELECT * маловероятно, что такая СУБД пойдет для «сканирования только по индексу»)

a_horse_with_no_name
источник
12
5-10% зависит от пары параметров конфигурации и хранения данных. Это не сложный номер.
Фрэнк Хейкенс,
6
@Frank: вот почему я сказал «примерно» :) Но спасибо, что указал на это
a_horse_with_no_name
5
Кроме того, последовательное сканирование может запрашивать несколько страниц из кучи одновременно и запрашивать у ядра выборку следующего фрагмента, пока он работает с текущим - сканирование индекса извлекает одну страницу одновременно. (Растровое сканирование делает компромисс между ними, вы обычно видите, что в плане появляются запросы, которые недостаточно избирательны для сканирования индекса, но все же не настолько неселективны, чтобы заслуживать полного сканирования таблицы)
araqnid
4
Интересный вопрос - как база данных знает, сколько строк запрос возвратит, не выполнив это сначала? Хранит ли она такую ​​статистику, как количество различных значений в зависимости от размера таблицы?
Лоран Грегуар
7
@ LaurentGrégoire: да, база данных хранит статистику о количестве строк и распределении значений. Подробности см. В руководстве: postgresql.org/docs/current/static/planner-stats.html
a_horse_with_no_name
13

Вы анализировали таблицу / базу данных? А как насчет статистики ? Когда существует много записей, где год> 2009, последовательное сканирование может быть быстрее, чем сканирование индекса.

Фрэнк Хейкенс
источник
0

При индексном сканировании считывающая головка переходит из одной строки в другую, что в 1000 раз медленнее, чем при чтении следующего физического блока (при последовательном сканировании).

Таким образом, если (количество извлекаемых записей * 1000) меньше, чем общее количество записей, сканирование индекса будет работать лучше.

Гаурав Нима
источник
0

@a_horse_with_no_name объяснил это довольно хорошо. Также, если вы действительно хотите использовать сканирование индекса, вы должны обычно использовать ограниченные диапазоны в предложении where. например - год> 2019 и год <2020.

Часто статистика не обновляется в таблице, и это может быть невозможно из-за ограничений. В этом случае оптимизатор не будет знать, сколько строк он должен занять в году> 2019. Таким образом, он выбирает последовательное сканирование вместо полного знания. Ограниченные перегородки решат проблему большую часть времени.

Шитий Гоял
источник