PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Когато автовакуумът не вакуумира

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

За бързо обобщение, autovacuum е фонов процес, който почиства мъртвите редове, напр. стари версии на изтрити редове. Можете също да извършите почистването ръчно, като стартирате VACUUM , но autovacuum прави това автоматично в зависимост от количеството мъртви редове в таблицата, в точния момент – не твърде често, но достатъчно често, за да поддържате количеството „боклук“ под контрол.

Най-общо казано, autovacuum не може да се изпълнява твърде често – почистването се извършва само след достигане на определен брой мъртви редове, натрупани в таблицата. Но може да се забави по различни причини, което води до това, че таблиците и индексите ще станат по-големи от желаното. И точно това е темата на тази публикация. И така, какви са често срещаните виновници и как да ги идентифицираме?

Дроселиране

Както е обяснено в основите на настройката, autovacuum работниците са ограничени да извършват само определено количество работа за интервал от време. Ограниченията по подразбиране са сравнително ниски – около 4MB/s запис, 8MB/s четене. Това е подходящо за малки машини като Raspberry Pi или малки сървъри отпреди 10 години, но сегашните машини са много по-мощни (както по отношение на процесора, така и по отношение на I/O) и обработват много повече данни.

Представете си, че имате няколко големи маси и няколко малки. Ако и трите autovacuum работниците започват да почистват големите маси, нито една от малките маси няма да бъде почистена с прахосмукачка, независимо от количеството мъртви редове, които натрупват. Идентифицирането на това не е особено трудно, ако приемем, че имате достатъчно наблюдение. Потърсете периоди, когато всички autovacuum работниците са заети, докато масите не се почистват с прахосмукачка, въпреки натрупването на много мъртви редове.

Цялата необходима информация е в pg_stat_activity (брой на autovacuum работни процеси) и pg_stat_all_tables (last_autovacuum и n_dead_tup ).

Увеличаване на броя на autovacuum работници не е решение, тъй като общият обем работа остава същият. Можете да посочите ограничения за регулиране на маса, като изключите този работник от общия лимит, но това все още не гарантира, че ще има налични работници, когато е необходимо.

Правилното решение е настройка на дроселирането, като се използват разумни граници по отношение на хардуерната конфигурация и моделите на работното натоварване. Някои основни препоръки за дроселиране са споменати в предишната публикация. (Очевидно, ако можете да намалите количеството мъртви редове, генерирани в базата данни, това би било идеално решение.)

От този момент ще приемем, че дроселирането не е проблем, т.е. че autovacuum работниците не са наситени за дълги периоди от време и че почистването се задейства на всички маси без неоправдано забавяне.

Дълги транзакции

Така че, ако масата се почиства редовно, със сигурност не може да натрупа много мъртви редове, нали? За съжаление не. Редовете всъщност не са „отстраняеми“ веднага след като бъдат изтрити, а само когато няма транзакции, които биха могли да ги видят. Точното поведение зависи от това какво правят (вършат) другите транзакции и нивото на сериализация, но като цяло:

ЧЕТЕТЕ СЕ ОТВЕЖЕНО

  • почистване на блока на изпълняваните заявки
  • неактивните транзакции блокират почистването само ако са извършили запис
  • неактивните транзакции (без никакви записи) няма да блокират почистването (но така или иначе не е добра практика да ги държите наоколо)

СЕРИАЛИЗИРАНЕ

  • почистване на блока на изпълняваните заявки
  • неактивните транзакции блокират почистването (дори ако са извършвали само четене)

На практика, разбира се, е по-нюансирано, но обясняването на всички различни битове ще изисква първо да се обясни как работят XID и моментните снимки, а това не е целта на тази публикация. Това, което наистина трябва да вземете от това, е, че дългите транзакции са лоша идея, особено ако тези транзакции може да са направили запис.

Разбира се, има напълно основателни причини, поради които може да се наложи да поддържате транзакции за дълги периоди от време (например, ако трябва да осигурите ACID за всички промени). Но внимавайте да не се случва ненужно, напр. поради лош дизайн на приложението.

Донякъде неочаквана последица от това е високото използване на процесора и I/O, поради autovacuum тичане отново и отново, без да почиствате мъртви редове (или само няколко от тях). Поради това масите все още отговарят на условията за почистване в следващия кръг, причинявайки повече вреда, отколкото полза.

Как да открием това? Първо, трябва да наблюдавате дългосрочните транзакции, особено неактивните. Всичко, което трябва да направите, е да четете данни от pg_stat_activity . Дефиницията на изгледа се променя малко с версията на PostgreSQL, така че може да се наложи да промените това малко:

SELECT xact_start, state FROM pg_stat_activity;

-- count 'idle' transactions longer than 15 minutes (since BEGIN)
SELECT COUNT(*) FROM pg_stat_activity
 WHERE state = 'idle in transaction'
  AND (now() - xact_start) > interval '15 minutes'

-- count transactions 'idle' for more than 5 minutes
SELECT COUNT(*) FROM pg_stat_activity
 WHERE state = 'idle in transaction'
  AND (now() - state_change) > interval '5 minutes'

Можете също така просто да използвате някакъв съществуващ плъгин за наблюдение, напр. check_postgres.pl. Те вече включват този тип проверки за здравина. Ще трябва да решите каква е разумната продължителност на транзакцията/заявката, която е специфична за приложението.

От PostgreSQL 9.6 можете също да използвате idle_in_transaction_session_timeout така че транзакциите неактивни твърде дълго се прекратяват автоматично. По същия начин за дълги заявки има statement_timeout .

Друго полезно нещо е VACUUM VERBOSE което всъщност ще ви каже колко мъртви реда все още не могат да бъдат премахнати:

db=# VACUUM verbose z;
INFO:  vacuuming "public.z"
INFO:  "z": found 0 removable, 66797 nonremovable row versions in 443 out of 443 pages
DETAIL:  12308 dead row versions cannot be removed yet.
...

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

Забележка: . Не можете лесно да получите тази информация от autovacuum защото се регистрира само с DEBUG2 по подразбиране (и със сигурност не искате да стартирате с това ниво на регистрационния файл в производството).

Дълги заявки при горещ режим на готовност

Да предположим, че таблиците се почистват с прахосмукачка навреме, но не се премахват мъртвите кортежи, което води до раздуване на таблица и индекс. Вие наблюдавате pg_stat_activity и няма дългосрочни транзакции. Какъв може да е проблемът?

Ако имате реплика за поточно предаване, има вероятност проблемът да е там. Ако репликата използва hot_standby_feedback=on , заявките към репликата действат почти като транзакции на първичния, включително блокиране на почистване. Разбира се, hot_standby_feedback=on се използва точно при изпълнение на дълги заявки (напр. анализи и BI работни натоварвания) върху реплики, за да се предотвратят отмяната поради конфликти при репликация.

За съжаление, ще трябва да изберете – или да запазите hot_standby_feedback=on и приемете закъснения в почистването или се справете с анулирани заявки. Можете също да използвате max_standby_streaming_delay за да ограничите въздействието, въпреки че това не предотвратява изцяло анулирането (така че все пак трябва да опитате отново заявките).

Всъщност сега има трета опция - логическа репликация. Вместо да използвате физическа поточно репликация за BI репликата, можете да копирате промените, като използвате новата логическа репликация, налична в PostgreSQL 10. Логическата репликация отслабва връзката между първичната и репликата и прави клъстерите предимно независими (почистват се независимо, и др.).

Това решава двата проблема, свързани с репликацията на физическото поточно предаване – забавено почистване на първични или отменени заявки в репликата на BI. Въпреки това, за реплики, обслужващи DR цели, стрийминг репликацията остава правилният избор. Но тези реплики не изпълняват (или не трябва) дълги заявки.

Забележка: Въпреки че споменах, че логическата репликация ще бъде налична в PostgreSQL 10, значителна част от инфраструктурата беше налична в предишни издания (особено PostgreSQL 9.6). Така че може да сте в състояние да направите това дори при по-стари версии (направихме това за някои от нашите клиенти), но PostgreSQL 10 ще го направи много по-удобно и удобно.

Проблем с autoanalyze

Детайл, който може да пропуснете, е този autovacuum работниците всъщност изпълняват две различни задачи. Първо почистването (все едно изпълнявате VACUUM ), но също и събиране на статистически данни (като че ли изпълнява ANALYZE). ). Ии дветете части се дроселират с помощта на autovacuum_cost_limit .

Но има голяма разлика в обработката на транзакции. Всеки път, когато VACUUM част достига autovacuum_cost_limit , работникът пуска моментната снимка и спи за известно време. ANALYZE обаче трябва да се изпълнява в една моментна снимка/транзакция, което прави почистване на блока.

Това е елегантен начин да се простреляте в крака, особено ако правите и нещо от това:

  • увеличете default_statistics_target за изграждане на по-точни статистически данни от по-големи извадки
  • по-нисък autovacuum_analyze_scale_factor за по-често събиране на статистически данни

Непредвидената последица, разбира се, е ANALYZE ще се случва по-често, ще отнеме много повече време и ще (за разлика от VACUUM част) предотвратяване на почистването. Решението обикновено е доста просто – не намалявайте autovacuum_analyze_scale_factor твърде много. Изпълнява се ANALYZE всеки път, когато 10% от промените в таблицата трябва да са повече от достатъчни в повечето случаи.

n_dead_tup

Последното нещо, което бих искал да спомена, е за промените в pg_stat_all_tables.n_dead_tup стойности. Може да си помислите, че стойността е обикновен брояч, който се увеличава всеки път, когато се създаде нов мъртъв кортеж, и намалява при всяко почистване. Но всъщност това е само оценка на броя на мъртвите кортежи, актуализирана от ANALYZE . За малки таблици (по-малко от 240MB) всъщност не е голяма разлика, защото ANALYZE чете цялата таблица и така е доста точно. За големи таблици обаче може да се промени доста в зависимост от това кое подмножество от таблица е взето. И намаляване на autovacuum_vacuum_scale_factor го прави по-случаен.

Така че бъдете внимателни, когато разглеждате n_dead_tup в система за наблюдение. Внезапните спадове или увеличения на стойността може да се дължат просто на ANALYZE преизчисляване на различна оценка, а не поради действително почистване и/или нови мъртви кортежи, появяващи се в таблицата.

Резюме

За да обобщим това в няколко прости точки:

  • autovacuum може да свърши работата си само ако няма транзакции, които може да се нуждаят от мъртвите кортежи.
  • Дългосрочните заявки блокират почистването. Помислете за използването на statement_timeout за ограничаване на щетите.
  • Дългосрочната транзакция може да блокира почистването. Точното поведение зависи от неща като ниво на изолация или какво се е случило в транзакцията. Наблюдавайте ги и ги прекратете, ако е възможно.
  • Дългосрочни заявки за реплики с hot_standby_feedback=on може също да блокира почистването.
  • autoanalyze също е дроселиран, но за разлика от VACUUM част той запазва единична моментна снимка (и по този начин блокира почистването).
  • n_dead_tup е само оценка, поддържана от ANALYZE , така че очаквайте известни колебания (особено на големи маси).

  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Групиран LIMIT в PostgreSQL:показване на първите N реда за всяка група?

  2. Странно съобщение за грешка в SQLAlchemy:TypeError:обектът 'dict' не поддържа индексиране

  3. Как да създадете единична крайна точка за вашата настройка за репликация на PostgreSQL с помощта на HAProxy

  4. проблем с псевдонима на колоната на postgres

  5. Изчисляване на кумулативна сума в PostgreSQL