Слишком много буферов найдено + прочитано во время сканирования индекса - PullRequest
1 голос
/ 19 июня 2020

У меня 2 таблицы User и Info. Я пишу простой запрос с внутренним соединением и вставляю результат в незарегистрированную таблицу.

INSERT INTO Result (
iProfileId,email,format,content
) 
SELECT
  COALESCE(N1.iprofileId, 0),
  Lower(N1.email),
  W0.format,
  W0.content
FROM
  Info W0,
  User N1
where
  (N1.iprofileId = W0.iId);

Info таблица имеет 30 миллионов строк, а таблица User имеет 158 миллионов строк. По какой-то причине этот запрос занимает слишком много времени в одной из моих настроек продукта. На первый взгляд кажется, что он читает / поражает слишком много буферов:

Insert on Result  (cost=152813.60..15012246.06 rows=31198136 width=1080) (actual time=5126063.502..5126063.502 rows=0 loops=1)
   Buffers: shared hit=128815094 read=6103564 dirtied=599445 written=2088037
   I/O Timings: read=2563306.517 write=570919.940
   ->  Merge Join  (cost=152813.60..15012246.06 rows=31198136 width=1080) (actual time=0.097..5060947.922 rows=31191937 loops=1)
         Merge Cond: (w0.iid = n1.iprofileid)
         Buffers: shared hit=96480126 read=5574864 dirtied=70745 written=2009998
         I/O Timings: read=2563298.981 write=562810.833
         ->  Index Scan using user_idx on info w0  (cost=0.56..2984094.60 rows=31198136 width=35) (actual time=0.012..246299.026 rows=31191937 loops=1)
               Buffers: shared hit=481667 read=2490602 written=364347
               I/O Timings: read=178000.987 write=38663.457
         ->  Index Scan using profile_id on user n1  (cost=0.57..14938848.88 rows=158842848 width=32) (actual time=0.020..4718272.082 rows=115378606 loops=1)
               Buffers: shared hit=95998459 read=3084262 dirtied=70745 written=1645651
               I/O Timings: read=2385297.994 write=524147.376
 Planning Time: 11.531 ms
 Execution Time: 5126063.577 ms

Когда я запускал этот запрос в другой настройке, но с аналогичными таблицами и количеством записей, сканирование profile_id использовало только 5M страниц (выполнялось в 3m), тогда как здесь он использовал (чтение + попадание) 100M буферов (запускался за 1.45h). Когда я проверил использование подробного вакуума, эта таблица содержит только 10 миллионов страниц.

INFO:  "User": found 64647 removable, 109184385 nonremovable row versions in 6876625 out of 10546400 pages

Это один из хороших прогонов, но мы также видели, что этот запрос занимал до 4-5 часов. Моя тестовая система, которая работала менее 3 минут, также имела iid, распределенное по диапазону profile_id. Но в ней было меньше столбцов и индексов по сравнению с системой prod. В чем может быть причина такой медлительности?

1 Ответ

1 голос
/ 19 июня 2020

В плане выполнения, который вы показываете, много грязных и написанных страниц. Это указывает на то, что таблицы были недавно вставлены, и ваш запрос был первым читателем.

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

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

Если вы запустите запрос во второй раз, он будет быть быстрее. По этой причине рекомендуется VACUUM таблицы после массовой загрузки, которые также установят биты подсказки.

Однако такой большой запрос всегда будет медленным. Вот что вы можете попробовать еще больше ускорить:

  • иметь много ОЗУ и загружать таблицы в общие буферы с помощью pg_prewarm

  • увеличьте work_mem в надежде получить более быстрый ha sh присоедините

  • CLUSTER к таблицам, используя индексы, так что выборка из кучи станет более эффективной

...