Postgres быстрее автоочистки

У меня есть очень большая таблица, 400 мм записей, которая обычно получает только вставки. Однако в последнее время мне приходится делать много обновлений записей, чтобы выполнить задачу. Это создает много мертвых кортежей. Я обновил глобальные конфиги до следующего:

autovacuum_vacuum_scale_factor = 0
autovacuum_vacuum_threshold = 10000
autovacuum_vacuum_cost_limit = 2000
autovacuum_max_workers = 6

С этими настройками я хотел, чтобы в любое время, когда мертвые кортежи превышали 10 000 записей, автоочистка очищала их.

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

Мне нужно, чтобы автовакуум агрессивно справлялся с мертвыми кортежами в течение дня. Как бы я это сделал? Нужно ли увеличивать количество max_workers?

ОБНОВЛЕНИЕ: пользователь @Laurenz Albe предложил мне запустить некоторые показатели производительности с мертвыми кортежами и без них, чтобы продемонстрировать разницу в производительности.

Я предоставлю запрос sql и результаты EXPLAIN(ANALYZE, BUFFERS). Я изменил имя таблицы и групповой ключ для конфиденциальности.

EXPLAIN (ANALYZE, BUFFERS)
SELECT  ld.upid,
        MAX(ld.lid)
INTO _tt_test_with_dead_tuples
FROM big_table ld
GROUP BY ld.upid;

-- >>> С примерно 1% (3,648 млн из 383,2 млн) мертвых кортежей, результаты ниже.

HashAggregate  (cost=25579746.07..25584552.97 rows=480690 width=8) (actual time=5966760.520..5975279.359 rows=16238417 loops=1)
  Group Key: upid
  Buffers: shared hit=3015376 read=16753169 dirtied=1759802 written=1360458
  ->  Seq Scan on big_table ld  (cost=0.00..23642679.05 rows=387413405 width=8) (actual time=0.024..5593239.148 rows=383753513 loops=1)
        Buffers: shared hit=3015376 read=16753169 dirtied=1759802 written=1360458
Planning time: 2.677 ms
Execution time: 6012319.846 ms

-- >>> При 0 мертвых кортежах результаты ниже.

HashAggregate  (cost=25558409.48..25562861.52 rows=445204 width=8) (actual time=825662.640..835163.398 rows=16238417 loops=1)
  Group Key: upid
  Buffers: shared hit=15812 read=19753809
  ->  Seq Scan on big_table ld  (cost=0.00..23628813.32 rows=385919232 width=8) (actual time=0.020..533386.128 rows=383753513 loops=1)
        Buffers: shared hit=15812 read=19753809
Planning time: 10.109 ms
Execution time: 843319.731 ms

person fcol    schedule 04.09.2018    source источник


Ответы (1)


Мертвые кортежи — не ваша проблема.

Ваша настоящая проблема где-то в другом месте; Я подчеркнул это в следующем.

Последовательное сканирование в медленном запросе:

Buffers: shared hit=3015376 read=16753169 dirtied=1759802 written=1360458

Последовательное сканирование в быстром запросе:

Buffers: shared hit=15812 read=19753809

Похоже, около двух миллионов блоков таблицы содержат кортежи, которые были недавно записаны или обновлены.

Во время записи кортежа PostgreSQL еще не знает, будет ли транзакция зафиксирована или откатана, поэтому эта информация не сохраняется в кортеже. Однако это записывается в журнал фиксации, который хранится в pg_xact (или pg_clog, в зависимости от вашей версии).

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

Это изменяет и, следовательно, загрязняет блок, содержащий кортеж, и если у вас небольшая настройка shared_buffers и закончились доступные буферы, серверной части даже придется записывать блоки в хранилище, чтобы очистить их и освободить место.

Это письмо делает ваш запрос таким медленным.

Очистка таблицы устраняет проблему, потому что VACUUM не только очищает мертвые кортежи, но и устанавливает биты подсказок за вас (это тоже читатель!).

Чтобы убедиться в этом, запустите тот же SELECT второй раз без очистки таблицы, и вы заметите, что это будет так же быстро с 3 миллионами мертвых кортежей, потому что теперь все биты подсказки установлены.

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

Идея: улучшает ли ситуацию увеличение shared_buffers?

Но поскольку очистка таблицы решает проблему, вы также можете использовать автоочистку, чтобы чаще устанавливать биты подсказок.

Для этого вы можете установить autovacuum_vacuum_scale_factor на 0 и установить autovacuum_vacuum_threshold на большую константу (намного больше 10000), чтобы никогда не было слишком много строк без подсказок.

Кроме того, установите autovacuum_vacuum_cost_delay на 0, чтобы автоочистка завершилась быстро.

Не изменяйте эти параметры глобально, используйте ALTER TABLE ... SET (...), чтобы установить их только для этой таблицы.

person Laurenz Albe    schedule 04.09.2018
comment
На самом деле при наличии нескольких миллионов мертвых кортежей производительность значительно падает. Значения по умолчанию не запускают автоочистку до тех пор, пока это не повлияет на производительность. Я обновлю дополнительный параметр, который вы предложили, чтобы увидеть, как это влияет на ситуацию. Спасибо за ваше предложение. - person fcol; 04.09.2018
comment
Конечно, я могу выполнить запрос на выборку в обоих случаях. Позвольте мне сгенерировать эти данные. Если вы говорите, что на производительность не должно влиять, возможно, моя проблема в другом. Доложит. - person fcol; 04.09.2018
comment
Ну, производительность должна быть затронута пропорционально раздуванию. В таблице с 400 миллионами строк 1 миллион мертвых кортежей должен вызвать заметное замедление. Добавьте EXPLAIN (ANALYZE, BUFFERS) вывод для медленного и быстрого запроса к вопросу! - person Laurenz Albe; 05.09.2018
comment
Я обновил вопрос с запрошенной информацией. - person fcol; 05.09.2018
comment
Я обновил ответ желаемым решением. - person Laurenz Albe; 05.09.2018
comment
Спасибо за подробный ответ. Ваш ответ подтвердил, что мне нужно очистить эту таблицу, чтобы избежать медленных запросов. Мой первоначальный вопрос подробно описывает тот факт, что я очищаю таблицу, но она работает слишком медленно, и я хотел бы, чтобы она работала более агрессивно, поскольку обновления и, следовательно, мертвые кортежи происходят в быстром темпе. - person fcol; 05.09.2018
comment
Мертвые кортежи не имеют значения. Если это массовая загрузка, запустите явный VACUUM после этого. Если это просто высокая скорость изменения, сделайте так, чтобы автоочистка работала быстрее, как я указал. Вы можете использовать порог и установить коэффициент масштабирования равным 0, но вам определенно придется использовать более высокий порог, который вы планируете; по крайней мере миллион или около того. - person Laurenz Albe; 05.09.2018
comment
Я реализую ваше первоначальное предложение. Спасибо. - person fcol; 05.09.2018
comment
Я снова пересмотрел ответ, чтобы обеспечить всестороннее обсуждение и решение. Рассматривали ли вы увеличение shared_buffers? - person Laurenz Albe; 05.09.2018
comment
У меня нет, но я обязательно буду. Я внесу изменения в течение недели и сообщу, если замечу улучшение. - person fcol; 06.09.2018
comment
@fcol, решило ли увеличение shared_buffers проблему для вас? - person qris; 05.08.2019