Агресивний Autovacuum на PostgreSQL


42

Я намагаюсь отримати PostgreSQL для того, щоб агресивно автоматизувати вакуумну базу даних. Наразі я настроював автоматичний вакуум так:

  • autovacuum_vacuum_cost_delay = 0 # Відключення вакууму на основі витрат
  • autovacuum_vacuum_cost_limit = 10000 # Максимальне значення
  • autovacuum_vacuum_threshold = 50 # За замовчуванням
  • autovacuum_vacuum_scale_factor = 0,2 # За замовчуванням

Я зауважую, що автоматичний вакуум запускається лише тоді, коли база даних не навантажена, тому я потрапляю в ситуації, коли набагато більше мертвих кортежів, ніж живих кортежів. Для прикладу дивіться доданий скріншот. В одному зі столів є 23 живі кортежі, але 16845 мертвих кортежів очікує вакууму. Це божевільно!

Багато мертвих кортежів

Автоматичний вакуум починається, коли тестовий пробіг закінчується, і сервер бази даних працює в режимі очікування. Це не те, що я хочу, як я хотів би, щоб автоматичний вакуум починався, коли кількість мертвих кортежів перевищує 20% живих кортежів + 50, оскільки база даних була налаштовано. Автоматичне вакуумування, коли сервер простоює, для мене марно, оскільки очікується, що виробничий сервер буде отримувати 1000 оновлень / с протягом тривалого періоду, тому мені потрібен автоматичний вакуум, навіть якщо сервер знаходиться під навантаженням.

Чи є щось, чого мені не вистачає? Як змусити роботу автоматичного вакууму, коли сервер знаходиться під великим навантаженням?

Оновлення

Це може бути проблемою блокування? Розглянуті таблиці - це зведені таблиці, які заповнюються через тригер після вставки. Ці таблиці заблоковані в режимі SHARE ROW EXCLUSIVE, щоб запобігти одночасному запису в один рядок.

Відповіді:


40

Eelke майже напевно правильно, що ваше блокування блокує автовакуум. Autovacuum розроблений таким чином, щоб навмисно поступатися місцем діяльності користувачів. Якщо ці таблиці заблоковані, автовакуум не може їх пилососити.

Однак для нащадків я хотів навести приклад набору налаштувань для гіперагресивної автовакуумної системи, оскільки налаштування, які ви надали, не дуже роблять. Зауважте, що зробити автовакуум більш агресивним навряд чи вдасться вирішити вашу проблему. Також зауважте, що налаштування автовакууму за замовчуванням базуються на виконанні понад 200 тестових запусків з використанням DBT2, що шукають оптимальну комбінацію налаштувань, тому типові параметри слід вважати хорошими, якщо у вас немає вагомих причин думати інакше, або якщо ваша база даних значно не знаходиться основний потік для баз даних OLTP (наприклад, крихітна база даних, яка отримує оновлення 10K в секунду, або сховище даних 3TB).

По-перше, увімкніть журнал, щоб ви могли перевірити, чи робить autovacuum те, що, на вашу думку, є:

log_autovacuum_min_duration = 0

Тоді давайте зробимо більше працівників автовакзалу і змусимо їх частіше перевіряти таблиці:

autovacuum_max_workers = 6
autovacuum_naptime = 15s

Давайте знизимо пороги автоматичного вакуумування та автоматичного аналізу, щоб скоріше запустити:

autovacuum_vacuum_threshold = 25
autovacuum_vacuum_scale_factor = 0.1

autovacuum_analyze_threshold = 10
autovacuum_analyze_scale_factor = 0.05 

Тоді давайте зробимо автовакуум менш переривним, тому він закінчується швидше, але ціною більшого впливу на одночасну діяльність користувача:

autovacuum_vacuum_cost_delay = 10ms
autovacuum_vacuum_cost_limit = 1000

Існує ваша повна програма для загальноагресивного автовакууму, яка може бути доречною для невеликої бази даних, що отримує дуже високу швидкість оновлень, але може мати занадто великий вплив на паралельну діяльність користувачів.

Також зауважте, що параметри автовакууму можна регулювати за таблицею , що майже завжди є кращою відповіддю для необхідності коригування поведінки автовакууму.

Знову ж таки, навряд чи можна вирішити вашу справжню проблему.


36

Щоб побачити, які таблиці взагалі мають право на автовакуум, може бути використаний наступний запит (на основі http://www.postgresql.org/docs/current/static/routine-vacuuming.html ). Зауважте, що запит не шукає конкретних параметрів таблиці:

 SELECT psut.relname,
     to_char(psut.last_vacuum, 'YYYY-MM-DD HH24:MI') as last_vacuum,
     to_char(psut.last_autovacuum, 'YYYY-MM-DD HH24:MI') as last_autovacuum,
     to_char(pg_class.reltuples, '9G999G999G999') AS n_tup,
     to_char(psut.n_dead_tup, '9G999G999G999') AS dead_tup,
     to_char(CAST(current_setting('autovacuum_vacuum_threshold') AS bigint)
         + (CAST(current_setting('autovacuum_vacuum_scale_factor') AS numeric)
            * pg_class.reltuples), '9G999G999G999') AS av_threshold,
     CASE
         WHEN CAST(current_setting('autovacuum_vacuum_threshold') AS bigint)
             + (CAST(current_setting('autovacuum_vacuum_scale_factor') AS numeric)
                * pg_class.reltuples) < psut.n_dead_tup
         THEN '*'
         ELSE ''
     END AS expect_av
 FROM pg_stat_user_tables psut
     JOIN pg_class on psut.relid = pg_class.oid
 ORDER BY 1;

11

Так, це питання блокування. Відповідно до цієї сторінки (не повною) VACUUM потребує СПОДІЛЬНОГО ОНОВЛЕННЯ ЕКСКЛЮЗИВНОГО доступу, який блокується рівнем блокування, який ви використовуєте.

Ви впевнені, що вам потрібен цей замок? PostgreSQL сумісний з ACID, тому одночасне записування в більшості випадків не є проблемою, оскільки PostgreSQL припинить одну з транзакцій, якщо буде порушено серіалізацію.

Також ви можете заблокувати рядки, використовуючи SELECT FOR UPDATE для блокування рядків замість усієї таблиці.

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


Це пов’язано з блокуванням підсумкових таблиць, оскільки вони блокуються за допомогою SHARE ROW EXCLUSIVE MODE. Одночасне записування без блокування може бути успішним, але вони, безумовно, виявляться з помилковими значеннями. Уявіть, що я підтримую кількість N рядків типу X. Якщо я одночасно вставляю 2 ряди типу X, не блокуючи, я в кінцевому підсумку замість N + 2. увійде до N + 1. полягає в тому, щоб мати роботу cron, яка вручну пилососить зведені таблиці в моїй базі даних. Це добре працює і, здається, є рекомендованим підходом, але мені здається, що це занадто схоже на злом.
CadentOrange

6

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

http://www.postgresql.org/docs/current/static/runtime-config-autovacuum.html

autovacuum_max_workers = 6              # max number of autovacuum subprocesses
autovacuum_naptime = 10         # time between autovacuum runs
autovacuum_vacuum_cost_delay = 20ms     # default vacuum cost delay for

Я також спробую знизити, cost_delayщоб зробити пилосос більш агресивним.

Я також можу перевірити автокумуляцію, використовуючи pgbench.

http://wiki.postgresql.org/wiki/Pgbenchtesting

Приклад високої суперечності:

Створити базу даних bench_replication

pgbench -i -p 5433 bench_replication

Запустіть pgbench

pgbench -U postgres -p 5432 -c 64 -j 4 -T 600 bench_replication

Перевірте стан автоаккумуляції

psql
>\connect bench_replicaiton
bench_replication=# select schemaname, relname, last_autovacuum from pg_stat_user_tables;
 schemaname |     relname      |        last_autovacuum        
------------+------------------+-------------------------------
 public     | pgbench_branches | 2012-07-18 18:15:34.494932+02
 public     | pgbench_history  | 
 public     | pgbench_tellers  | 2012-07-18 18:14:06.526437+02
 public     | pgbench_accounts | 

6

Існуючий сценарій "кваліфікуватись для автовакууму" дуже корисний, але (як правильно сказано) не вистачало конкретних параметрів таблиці. Ось модифікована його версія, яка враховує ці параметри:

WITH rel_set AS
(
    SELECT
        oid,
        CASE split_part(split_part(array_to_string(reloptions, ','), 'autovacuum_vacuum_threshold=', 2), ',', 1)
            WHEN '' THEN NULL
        ELSE split_part(split_part(array_to_string(reloptions, ','), 'autovacuum_vacuum_threshold=', 2), ',', 1)::BIGINT
        END AS rel_av_vac_threshold,
        CASE split_part(split_part(array_to_string(reloptions, ','), 'autovacuum_vacuum_scale_factor=', 2), ',', 1)
            WHEN '' THEN NULL
        ELSE split_part(split_part(array_to_string(reloptions, ','), 'autovacuum_vacuum_scale_factor=', 2), ',', 1)::NUMERIC
        END AS rel_av_vac_scale_factor
    FROM pg_class
) 
SELECT
    PSUT.relname,
    to_char(PSUT.last_vacuum, 'YYYY-MM-DD HH24:MI')     AS last_vacuum,
    to_char(PSUT.last_autovacuum, 'YYYY-MM-DD HH24:MI') AS last_autovacuum,
    to_char(C.reltuples, '9G999G999G999')               AS n_tup,
    to_char(PSUT.n_dead_tup, '9G999G999G999')           AS dead_tup,
    to_char(coalesce(RS.rel_av_vac_threshold, current_setting('autovacuum_vacuum_threshold')::BIGINT) + coalesce(RS.rel_av_vac_scale_factor, current_setting('autovacuum_vacuum_scale_factor')::NUMERIC) * C.reltuples, '9G999G999G999') AS av_threshold,
    CASE
        WHEN (coalesce(RS.rel_av_vac_threshold, current_setting('autovacuum_vacuum_threshold')::BIGINT) + coalesce(RS.rel_av_vac_scale_factor, current_setting('autovacuum_vacuum_scale_factor')::NUMERIC) * C.reltuples) < PSUT.n_dead_tup
        THEN '*'
    ELSE ''
    END AS expect_av
FROM
    pg_stat_user_tables PSUT
    JOIN pg_class C
        ON PSUT.relid = C.oid
    JOIN rel_set RS
        ON PSUT.relid = RS.oid
ORDER BY C.reltuples DESC;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.