Postgres быстрее, автоочистка - PullRequest
       23

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

0 голосов
/ 04 сентября 2018

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

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

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

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

Мне нужен автовакуум, чтобы агрессивно бороться с мертвыми кортежами в течение дня. Как бы я это сделал? Нужно ли мне увеличивать количество 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

1 Ответ

0 голосов
/ 04 сентября 2018

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

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

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

Buffers: shared hit=3015376 read=16753169 <strong>dirtied=1759802 written=1360458</strong>

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

Buffers: shared hit=15812 read=19753809

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

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

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

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

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

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

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

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

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

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

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

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

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

...