Проблема
Следующий запрос занимает 42 секунды, если большая часть данных не кэширована:
EXPLAIN (ANALYZE, BUFFERS) select count(*) from packages where company_id = 178381;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------
Aggregate (cost=395914.63..395914.63 rows=1 width=8) (actual time=42411.940..42411.942 rows=1 loops=1)
Buffers: shared hit=21775 read=94888
I/O Timings: read=39723.315
-> Bitmap Heap Scan on packages (cost=1053.07..395761.41 rows=306442 width=0) (actual time=83.104..42336.765 rows=322432 loops=1)
Recheck Cond: (company_id = 178381)
Heap Blocks: exact=116385
Buffers: shared hit=21775 read=94888
I/O Timings: read=39723.315
-> Bitmap Index Scan on packages_company_id_index (cost=0.00..1037.75 rows=306442 width=0) (actual time=45.846..45.847 rows=325795 loops=1)
Index Cond: (company_id = 178381)
Buffers: shared hit=1 read=277
I/O Timings: read=7.090
Planning:
Buffers: shared hit=2
Planning Time: 0.237 ms
Execution Time: 42413.042 ms
Повторный запуск запроса сразу после этого, естественно, намного быстрее:
distru_prod=> EXPLAIN (ANALYZE, BUFFERS) select count(*) from packages where company_id = 178381;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------
Aggregate (cost=395914.63..395914.63 rows=1 width=8) (actual time=416.943..416.957 rows=1 loops=1)
Buffers: shared hit=116589
-> Bitmap Heap Scan on packages (cost=1053.07..395761.41 rows=306442 width=0) (actual time=78.925..395.495 rows=322432 loops=1)
Recheck Cond: (company_id = 178381)
Heap Blocks: exact=116308
Buffers: shared hit=116589
-> Bitmap Index Scan on packages_company_id_index (cost=0.00..1037.75 rows=306442 width=0) (actual time=46.359..46.360 rows=325351 loops=1)
Index Cond: (company_id = 178381)
Buffers: shared hit=281
Planning:
Buffers: shared hit=448
Planning Time: 1.375 ms
Execution Time: 418.321 ms
Больше информации
Вот некоторая основная информация об этой таблице:
select count(distinct company_id) from packages;
count
-------
691
select count(*) from packages;
count
----------
10764441
select count(*) from packages where company_id = 178381;
count
--------
322432
select pg_size_pretty(pg_total_relation_size('packages'));
pg_size_pretty
----------------
12 GB
select pg_size_pretty(pg_total_relation_size('packages_company_id_index'));
pg_size_pretty
----------------
79 MB
Используемый индекс:
CREATE INDEX packages_company_id_index ON public.packages USING btree (company_id);
Даже при использовании pg_hint_plan
для принудительного сканирования только индекса на packages_company_id_index
, buffers
по-прежнему такие же высокие, как при сканировании кучи растровых изображений:
/*+ IndexOnlyScan(packages packages_company_id_index) */ EXPLAIN (ANALYZE, BUFFERS) select count(*) from packages where company_id = 178381;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------------------
Aggregate (cost=517401.31..517401.32 rows=1 width=8) (actual time=172.448..172.450 rows=1 loops=1)
Buffers: shared hit=116586
-> Index Only Scan using packages_company_id_index on packages (cost=0.09..517248.09 rows=306442 width=0) (actual time=0.034..150.510 rows=322432 loops=1)
Index Cond: (company_id = 178381)
Heap Fetches: 325351
Buffers: shared hit=116586
Planning:
Buffers: shared hit=2
Planning Time: 0.238 ms
Execution Time: 172.546 ms
Вопросы
buffers
, сколько и для исходного запроса, хотя размер индекса на диске составляет всего 79 МБ?packages
?Сканирование только по индексу считывает так много буферов, потому что на самом деле это не сканирование только по индексу. VACUUM
таблица, чтобы обновить карту видимости, и вы увидите разницу.
Вероятно, потому, что карта видимости не обновляется, а возможно, потому, что вы оставили random_page_cost
со слишком высоким значением.
Согласно выложенным вами данным, таблица должна иметь размер не менее 900 МБ (116589 страниц по 8 КБ каждая).
Ожидаете ли вы, что ваш диск будет читать со скоростью более 19 МБ/с?
Невозможно ответить, потому что это зависит от неизвестного «если».
См. 1 и 2 выше.
Большое спасибо, Лоренц! Вы были в точку. Запуск vacuum
позволил Postgres выполнять сканирование только индекса без выборки данных из кучи. Я не знал о карте видимости. Просто прочитайте об этом в двух замечательных статьях, которые вы написали (1 2), и теперь все имеет смысл. Еще раз спасибо!
В зависимости от вашей подсистемы хранения увеличение значения effect_io_concurrency может существенно повысить производительность сканирования растровых изображений с холодным кэшем, позволяя одновременно обрабатывать несколько запросов ввода-вывода.
Конечно, обновление карты видимости, чтобы сканирование только по индексу могло работать правильно, и поэтому избавление или значительное сокращение «Выборки кучи: 325351» может сделать это бессмысленным.
Обратите внимание, что вопросы о переполнении стека должны содержать один вопрос, а не шесть.