Před pár týdny jsem vysvětlil základy autovakuového ladění. Na konci tohoto příspěvku jsem slíbil, že se brzy podívám na problémy s vysáváním. No, trvalo to trochu déle, než jsem plánoval, ale je to tady.
Pro rychlou rekapitulaci autovacuum
je proces na pozadí, který čistí mrtvé řádky, např. staré smazané verze řádků. Čištění můžete také provést ručně spuštěním VACUUM
, ale autovacuum
dělá to automaticky v závislosti na množství mrtvých řádků v tabulce ve správný okamžik – ne příliš často, ale dostatečně často, aby udrželo množství „odpadu“ pod kontrolou.
Obecně řečeno, autovacuum
nemůže běžet příliš často – čištění se provádí až po dosažení určitého počtu nahromaděných mrtvých řádků v tabulce. Může se však z různých důvodů zpozdit, což má za následek, že tabulky a indexy budou větší, než je žádoucí. A to je přesně téma tohoto příspěvku. Jací jsou tedy běžní viníci a jak je identifikovat?
Omezování
Jak je vysvětleno v Základech ladění, autovacuum
pracovníci jsou omezeni, aby vykonali pouze určité množství práce za časový interval. Výchozí limity jsou poměrně nízké – asi 4 MB/s pro zápis, 8 MB/s pro čtení. To je vhodné pro malé stroje jako Raspberry Pi nebo malé servery z doby před 10 lety, ale současné stroje jsou mnohem výkonnější (jak z hlediska CPU, tak I/O) a zpracovávají mnohem více dat.
Představte si, že máte několik velkých stolů a několik malých. Pokud všechny tři autovacuum
pracovníci začnou uklízet velké stoly, žádný z malých stolů nebude vysát bez ohledu na množství mrtvých řad, které se nahromadí. Identifikace není nijak zvlášť obtížná, za předpokladu, že máte dostatečné sledování. Hledejte období, kdy jsou všechna autovacuum
pracovníci jsou zaneprázdněni, zatímco stoly nejsou vysávány, přestože hromadí mnoho mrtvých řad.
Všechny potřebné informace jsou v pg_stat_activity
(počet autovacuum
pracovní procesy) a pg_stat_all_tables
(last_autovacuum
a n_dead_tup
).
Zvýšení počtu autovacuum
pracovníků není řešením, protože celkové množství práce zůstává stejné. Můžete zadat limity omezení pro jednotlivé tabulky, přičemž daného pracovníka z celkového limitu vyloučíte, ale stále to nezaručuje, že v případě potřeby budou k dispozici pracovníci.
Správným řešením je vyladění omezení pomocí limitů přiměřených s ohledem na konfiguraci hardwaru a vzorce pracovní zátěže. Některá základní doporučení ohledně omezení jsou zmíněna v předchozím příspěvku. (Je zřejmé, že pokud můžete snížit množství mrtvých řádků generovaných v databázi, bylo by to ideální řešení.)
Od tohoto bodu budeme předpokládat, že problém není omezování, tj. že autovacuum
pracovníci nejsou nasyceni po dlouhou dobu a že úklid je spuštěn na všech stolech bez nepřiměřených prodlev.
Dlouhé transakce
Takže pokud je stůl pravidelně vysáván, určitě se na něm nemůže nashromáždit mnoho mrtvých řádků, že? Bohužel ne. Řádky nejsou ve skutečnosti „odstranitelné“ ihned po smazání, ale pouze v případě, že neexistují žádné transakce, které by je mohly vidět. Přesné chování závisí na tom, co (dělají) ostatní transakce a na úrovni serializace, ale obecně:
ČTĚTE ODPOVĚDĚNO
- vyčištění bloku spuštěných dotazů
- nečinné transakce blokují vyčištění pouze v případě, že provedly zápis
- nečinné transakce (bez jakýchkoli zápisů) nezablokují čištění (ale není dobrým zvykem je udržovat)
SERIALIZOVATELNÉ
- vyčištění bloku spuštěných dotazů
- Čištění bloku nečinných transakcí (i když prováděly pouze čtení)
V praxi je to samozřejmě jemnější, ale vysvětlení všech různých bitů by vyžadovalo nejprve vysvětlit, jak XID a snímky fungují, a to není cílem tohoto příspěvku. Co byste si z toho měli skutečně odnést je, že dlouhé transakce jsou špatný nápad, zvláště pokud by tyto transakce mohly způsobit zápisy.
Samozřejmě existují naprosto oprávněné důvody, proč možná budete muset uchovávat transakce po dlouhou dobu (např. pokud potřebujete zajistit ACID pro všechny změny). Dejte si ale pozor, aby se to zbytečně nestalo, kupř. kvůli špatnému návrhu aplikace.
Poněkud neočekávaným důsledkem toho je vysoké využití CPU a I/O kvůli autovacuum
běhat znovu a znovu, aniž by se vyčistily mrtvé řady (nebo jen několik z nich). Kvůli tomu jsou stoly stále způsobilé k vyčištění v příštím kole, což způsobí více škody než užitku.
Jak to zjistit? Nejprve musíte sledovat dlouhotrvající transakce, zejména ty nečinné. Vše, co musíte udělat, je načíst data z pg_stat_activity
. Definice pohledu se s verzí PostgreSQL trochu mění, takže to možná budete muset trochu upravit:
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'
Můžete také jednoduše použít některý stávající monitorovací plugin, např. check_postgres.pl. Ty již zahrnují tento typ kontroly zdravého rozumu. Budete se muset rozhodnout, jaká je přiměřená doba trvání transakce/dotazu, která je specifická pro aplikaci.
Od PostgreSQL 9.6 můžete také použít idle_in_transaction_session_timeout
takže transakce nečinné příliš dlouho jsou automaticky ukončeny. Podobně pro dlouhé dotazy existuje statement_timeout
.
Další užitečnou věcí je VACUUM VERBOSE
což vám ve skutečnosti řekne, kolik mrtvých řad se ještě nepodařilo odstranit:
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. ...
Neřekne vám, který backend brání vyčištění, ale je to docela jasné znamení toho, co se děje.
Poznámka: . Tyto informace nemůžete snadno získat z autovacuum
protože je přihlášen pouze pomocí DEBUG2
ve výchozím nastavení (a určitě nechcete s touto úrovní protokolu běžet v produkci).
Dlouhé dotazy v pohotovostních režimech
Předpokládejme, že tabulky jsou vysávány včas, ale neodstraňují se mrtvé n-tice, což vede k nadýmání tabulek a indexů. Sledujete pg_stat_activity
a neexistují žádné dlouhodobé transakce. V čem by mohl být problém?
Pokud máte streamovací repliku, je pravděpodobné, že problém může být tam. Pokud replika používá hot_standby_feedback=on
, dotazy na replice fungují v podstatě jako transakce na primární, včetně čištění blokování. Samozřejmě hot_standby_feedback=on
se používá přesně při spouštění dlouhých dotazů (např. analytické a BI úlohy) na repliky, aby se zabránilo zrušení kvůli konfliktům replikace.
Bohužel si budete muset vybrat – buď ponechat hot_standby_feedback=on
a přijmout zpoždění při čištění nebo se vypořádat se zrušenými dotazy. Můžete také použít max_standby_streaming_delay
abyste omezili dopad, i když to zcela nezabrání zrušením (takže stále musíte opakovat dotazy).
Ve skutečnosti je nyní třetí možnost – logická replikace. Namísto použití replikace fyzického streamování pro repliku BI můžete změny zkopírovat pomocí nové logické replikace, která je k dispozici v PostgreSQL 10. Logická replikace uvolňuje spojení mezi primární a replikou a činí clustery většinou nezávislými (jsou čištěny nezávisle, atd.).
To řeší dva problémy spojené s replikací fyzického streamování – zpožděné čištění primárních nebo zrušených dotazů na repliku BI. Pro repliky sloužící účelům DR však zůstává správnou volbou streamingová replikace. Ale tyto repliky nespouštějí (nebo by neměly být) dlouhé dotazy.
Poznámka: I když jsem zmínil, že logická replikace bude dostupná v PostgreSQL 10, významná část infrastruktury byla dostupná v předchozích verzích (zejména PostgreSQL 9.6). Takže to možná budete moci udělat i na starších verzích (to jsme udělali pro některé naše zákazníky), ale PostgreSQL 10 to udělá mnohem pohodlnější a pohodlnější.
Potíže s autoanalyze
Detail, který vám může uniknout, je autovacuum
pracovníci ve skutečnosti plní dva různé úkoly. Nejprve čištění (jako byste spouštěli VACUUM
), ale také shromažďování statistik (jako byste spouštěli ANALYZE
). A obě díly jsou omezeny pomocí autovacuum_cost_limit
.
Velký rozdíl je ale ve zpracování transakcí. Kdykoli VACUUM
část dosáhne autovacuum_cost_limit
, pracovník uvolní snímek a na chvíli usne. ANALYZE
musí však běžet v jediném snímku/transakci, což dělá čištění bloku.
Toto je elegantní způsob, jak se střelit do nohy, zvláště pokud také děláte něco z tohoto:
- zvýšit
default_statistics_target
k vytvoření přesnějších statistik z větších vzorků - nižší
autovacuum_analyze_scale_factor
shromažďovat statistiky častěji
Nezamýšleným důsledkem je samozřejmě to, že ANALYZE
bude probíhat častěji, bude trvat mnohem déle a bude (na rozdíl od VACUUM
část) zabránit čištění. Řešení je obvykle poměrně jednoduché – nesnižujte autovacuum_analyze_scale_factor
příliš mnoho. Spuštění ANALYZE
pokaždé, když 10 % změn v tabulce bude ve většině případů více než dost.
n_dead_tup
Poslední věc, kterou bych rád zmínil, je o změnách v pg_stat_all_tables.n_dead_tup
hodnoty. Můžete si myslet, že hodnota je jednoduchý čítač, který se zvýší vždy, když se vytvoří nová mrtvá n-tice, a sníží se, když se vyčistí. Ale ve skutečnosti je to pouze odhad počtu mrtvých n-tic, který aktualizuje ANALYZE
. U malých tabulek (méně než 240 MB) to opravdu není velký rozdíl, protože ANALYZE
čte celou tabulku a tak je docela přesná. U velkých tabulek se však může dost změnit v závislosti na tom, jaká podmnožina tabulky je vzorkována. A snížení autovacuum_vacuum_scale_factor
dělá to více náhodné.
Buďte tedy opatrní při prohlížení n_dead_tup
v monitorovacím systému. Náhlé poklesy nebo zvýšení hodnoty mohou být jednoduše způsobeny ANALYZE
přepočítávání jiného odhadu, a ne kvůli skutečnému vyčištění a/nebo novým mrtvým n-ticím, které se objevují v tabulce.
Shrnutí
Abych to shrnul do několika jednoduchých bodů:
autovacuum
může fungovat pouze v případě, že neexistují žádné transakce, které by mohly potřebovat mrtvé n-tice.- Dlouhotrvající dotazy blokují čištění. Zvažte použití
statement_timeout
k omezení škod. - Dlouho trvající transakce může blokovat čištění. Přesné chování závisí na věcech, jako je úroveň izolace nebo co se stalo v transakci. Sledujte je a pokud je to možné, ukončete je.
- Dlouhotrvající dotazy na repliky pomocí
hot_standby_feedback=on
může také blokovat čištění. autoanalyze
je také přiškrcen, ale na rozdíl odVACUUM
část uchovává jeden snímek (a tím blokuje čištění).n_dead_tup
je pouze odhad spravovanýANALYZE
, takže počítejte s určitou fluktuací (zejména na velkých stolech).