Почему этот запрос не использует только сканирование индекса в postgresql
у меня есть таблица с 16 столбцов, в которых есть первичный ключ и столбец для хранения значений. Я хочу выбрать все значения в определенном диапазоне. Столбец значений (easyid) был проиндексирован.
create table tb1 (
id Int primary key,
easyid Int,
.....
)
create index i_easyid on tb1 (easyid)
другая информация: postgresql 9.4, отсутствие автоматического вакуума. Sql выглядит так.
select "easyid" from "tb1" where "easyid" between 12183318 and 82283318
теоретически postgresql должен использовать index only scan on i_easyid
. он только индексирует только сканирование, когда диапазон "easyid" between A and B
мал.
Когда диапазон большой, а именно B-A
- довольно большое число, postgresql использует сканирование растрового индекса на i_easyid
а затем сканирование кучи бит на tb1
.
Я ошибся, сказав, что только сканирование индекса или нет зависит от размера диапазона. Я попробовал тот же запрос с разными параметрами, иногда это сканирование индекса только иногда это не так.
в таблице tb1
очень большой до 17G. i_easyid
это 600мб.
вот объяснение sql. И я не понимаю, почему 4000 строк могут стоить дороже чем за 10 секунд.
sample_pg=# explain analyze select easyid from tb1 where "easyid" between 152183318 and 152283318;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on tb1 (cost=97.70..17227.71 rows=4416 width=4) (actual time=1.155..14346.311 rows=5004 loops=1)
Recheck Cond: ((easyid >= 152183318) AND (easyid <= 152283318))
Heap Blocks: exact=4995
-> Bitmap Index Scan on i_easyid (cost=0.00..96.60 rows=4416 width=0) (actual time=0.586..0.586 rows=5004 loops=1)
Index Cond: ((easyid >= 152183318) AND (easyid <= 152283318))
Planning time: 0.080 ms
Execution time: 14348.037 ms
(7 rows)
вот пример сканирования только индекса:
sample_pg=# explain analyze verbose select easyid from tb1 where "easyid" between 32280318 and 32283318;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------
Index Only Scan using i_easyid on public.tb1 (cost=0.44..281.82 rows=69 width=4) (actual time=14.585..160.624 rows=33 loops=1)
Output: easyid
Index Cond: ((tb1.easyid >= 32280318) AND (tb1.easyid <= 32283318))
Heap Fetches: 33
Planning time: 0.085 ms
Execution time: 160.654 ms
(6 rows)
2 ответов
autovacuum не работает
PostgreSQL index-только для сканирования требуется некоторая информация о том, какие строки "видны" текущим транзакциям - т. е. не удалены, не старые версии обновленных строк, а также незафиксированные вставки или новые версии обновлений.
эта информация хранится в "карты видимости".
карта видимости поддерживается VACUUM
, обычно в фоновом режиме работниками autovacuum.
если autovacuum не поспевает за активностью записи, или если autovacuum был отключен, то сканирование только индекса, вероятно, не будет использоваться, потому что PostgreSQL увидит, что карта видимости не имеет данных для достаточного количества таблицы.
снова включить autovaccum. Затем вручную VACUUM
стол, чтобы получить его немедленно.
BTW, в дополнение к информации о карте видимости, autoVACUUM
также можно написать подсказку-битную информацию, которая может сделать SELECT
s недавно добавлены/обновлены данные быстрее.
Autovacuum также поддерживает статистику таблицы, которые жизненно важны для эффективного планирования запросов. Отключение приведет к тому, что планировщик будет использовать все более устаревшую информацию.
также абсолютно необходимым для предотвращения проблемы под названием transaction-ID wrap-around, которая является аварийным состоянием, которое может привести к аварийному отключению всей базы данных до тех пор, пока не будет занята вся таблица VACUUM
is выполненный.
не выключайте автовакуум.
Что касается того, почему он иногда использует только сканирование индекса, а иногда и нет, несколько возможностей:
текущего
random_page_cost
настройка заставляет его думать, что случайный ввод-вывод будет медленнее, чем на самом деле, поэтому он пытается его избежать;статистика таблицы, особенно предельные значения, устарели. Так что он не понимает, что есть хороший шанс искомое значение будет быстро обнаружено при сканировании только по индексу;
карта видимости устарела, поэтому она считает, что сканирование только индекса найдет слишком много значений, которые потребуют проверки выборки кучи, что делает его медленнее, чем другие методы, особенно если он думает, что доля значений, вероятно, будет найдена высока.
большинство из этих проблем устраняются оставив автовакуум в покое. На самом деле, часто добавляется таблицы вы должны установить autovacuum для запуска гораздо чаще чем по умолчанию, поэтому он обновляет статистику лимита больше. (Это помогает обойти проблемы планировщика PostgreSQL с таблицами, где наиболее часто запрашиваемые данные являются последними вставленными с увеличивающимся идентификатором или меткой времени, что означает, что наиболее желаемые значения никогда не находятся в гистограммах таблицы и предельной статистике).
Go включите автовакуум обратно - тогда сделай погромче.
Я не уверен на 100%, но я подозреваю, что PostgreSQL считает, что он будет быстрее читать таблицу, чем индекс, из-за random_page_cost. Чтение индекса потенциально дороже из-за необходимости найти в нем по существу случайные страницы.
данные, полученные из таблицы, нуждаются в сортировке, но расчеты, вероятно, предполагают, что общая стоимость (последовательное чтение таблицы + сортировка) больше, чем (случайное чтение индекса).
этот частично тестируется путем изменения значения random_page_cost, которое стоило бы исследовать, используете ли вы очень быстрые диски или SSD в любом случае.