PostgreSQL vakuumavimas ir analyze

Kas nutinka, kai duomenų bazė tampa chaotiška

Dirbant su PostgreSQL, anksčiau ar vėliau susidursite su keista situacija – jūsų duomenų bazė veikia lėčiau, nors duomenų kiekis iš esmės nepasikeitė. Užklausos, kurios anksčiau veikė akimirksniu, dabar užtrunka sekundes. Disko vieta nyksta greičiau nei tikėjotės. Tai klasikiniai požymiai, kad jūsų PostgreSQL reikia šiek tiek priežiūros, o tiksliau – vakuumavimo.

PostgreSQL turi unikalų būdą tvarkyti duomenų pakeitimus. Kai ištrinate ar atnaujinat eilutę, ji iš tikrųjų neišnyksta iš karto. Vietoj to, PostgreSQL pažymi ją kaip „negyvą” ir palieka fiziškai diske. Kodėl taip? Dėl MVCC (Multi-Version Concurrency Control) mechanizmo, kuris leidžia keliems procesams vienu metu dirbti su duomenimis be konfliktų. Tai geniali sistema, bet ji turi šalutinį efektą – duomenų bazėje kaupiasi šiukšlės.

Čia ir ateina į pagalbą VACUUM komanda. Ji suranda tas negyvąsias eilutes ir išvalo jas, grąžindama vietą sistemai. Kartu su ja dirba ir ANALYZE, kuri renka statistiką apie duomenis, padedančią užklausų planuotojui priimti geresnius sprendimus.

Kaip veikia vakuumavimas po gaubtu

Vakuumavimo procesas nėra toks paprastas, kaip gali pasirodyti. Kai paleidžiate VACUUM, PostgreSQL pradeda skenuoti lentelę ir ieško eilučių, kurios nebereikalingos jokiai aktyviai transakcijai. Tai reiškia, kad sistema turi sekti visas vykstančias transakcijas ir žinoti, kurios eilutės dar gali būti reikalingos.

Kai VACUUM randa tokias „mirusias” eilutes, ji neišmeta jų iš disko iš karto. Vietoj to, ji pažymi tą vietą kaip laisvą ir prieinamą naujiems įrašams. Tai vadinama „space reuse” principu. Tik naudojant VACUUM FULL (apie kurį kalbėsime vėliau), duomenys iš tikrųjų fiziškai persirašomi ir vieta grąžinama operacinei sistemai.

Svarbu suprasti, kad vakuumavimas taip pat atnaujina visibility map – specialų failą, kuris saugo informaciją apie tai, kurie lentelės puslapiai (pages) yra visiškai užpildyti tik „gyvomis” eilutėmis. Tai labai pagreitina būsimus vakuumavimus ir index-only scans.

Autovacuum – jūsų tylus pagalbininkas

Geros naujienos – jums nebūtina rankiniu būdu vakuumuoti duomenų bazės. PostgreSQL turi įmontuotą autovacuum daemon procesą, kuris automatiškai stebi lenteles ir vakuumuoja jas, kai reikia. Tai viena iš tų funkcijų, kurios veikia fone ir dažnai lieka nepastebėtos, kol kažkas nepradeda veikti ne taip.

Autovacuum sprendžia, kada vakuumuoti, remdamasis keliais parametrais. Pagrindinis yra autovacuum_vacuum_threshold ir autovacuum_vacuum_scale_factor. Pagal nutylėjimą, lentelė vakuumuojama, kai pasenusių eilučių skaičius viršija 50 + 20% visų lentelės eilučių. Tai reiškia, kad didelėms lentelėms gali prireikti labai daug pakeitimų, kol autovacuum suveiks.

Štai kodėl kartais reikia koreguoti šiuos parametrus konkrečioms lentelėms:

ALTER TABLE jusu_lentele SET (autovacuum_vacuum_scale_factor = 0.05);

Tai sumažins slenkstį iki 5%, o tai reiškia dažnesnį, bet lengvesnį vakuumavimą. Ypač naudinga didelėms, intensyviai naudojamoms lentelėms.

ANALYZE ir kodėl jūsų užklausos gali būti lėtos

Dabar pereikime prie ANALYZE – komandos, kuri dažnai eina koja kojon su VACUUM, bet atlieka visiškai kitą darbą. ANALYZE renka statistiką apie duomenų pasiskirstymą lentelėse. Ši statistika saugoma pg_statistics lentelėje ir naudojama užklausų planuotojo.

Kodėl tai svarbu? Įsivaizduokite, kad turite lentelę su milijonu įrašų ir norite surasti visus klientus iš Lietuvos. Jei tik 100 klientų yra iš Lietuvos, efektyviausia būtų naudoti indeksą. Bet jei 900,000 klientų yra iš Lietuvos, efektyviau būtų tiesiog nuskaityti visą lentelę. ANALYZE padeda planuotojui priimti šį sprendimą.

Be aktualios statistikos, PostgreSQL gali priimti katastrofiškus sprendimus. Pavyzdžiui, naudoti sequential scan, kai reikėtų index scan, arba atvirkščiai. Rezultatas – užklausos, kurios turėtų užtrukti milisekundes, trunka sekundes ar net minutes.

Gera praktika – paleisti ANALYZE po didelių duomenų pakeitimų:


INSERT INTO orders SELECT * FROM legacy_orders;
ANALYZE orders;

Taip pat galite paleisti ANALYZE VERBOSE, kad pamatytumėte, ką tiksliai sistema daro:

ANALYZE VERBOSE jusu_lentele;

Kada reikia rankinio įsikišimo

Nors autovacuum daro puikų darbą daugeliu atvejų, yra situacijų, kai reikia rankiniu būdu įsikišti. Viena tokių situacijų – po didelių duomenų migracijų ar masinio ištrynimo. Jei ką tik ištrynėte milijoną eilučių, nenorite laukti, kol autovacuum pagaliau pasieks jūsų lentelę.

Kitas atvejis – transaction ID wraparound prevencija. PostgreSQL naudoja 32-bitų transaction ID, o tai reiškia, kad po maždaug 2 milijardų transakcijų sistema turi „apsisukti” atgal. Kad išvengtų duomenų praradimo, PostgreSQL priverstinai sustabdys naujų transakcijų priėmimą, jei nepavyksta laiku pavakuumuoti. Tai katastrofa produkcinei sistemai.

Galite patikrinti savo lentelių būklę:


SELECT relname, age(relfrozenxid),
pg_size_pretty(pg_total_relation_size(oid))
FROM pg_class
WHERE relkind = 'r'
ORDER BY age(relfrozenxid) DESC
LIMIT 10;

Jei matote age reikšmes, artėjančias prie 200 milijonų, laikas susirūpinti ir paleisti rankinį vakuumavimą.

VACUUM FULL ir kada jo vengti

VACUUM FULL yra visiškai kitoks žvėris nei paprastas VACUUM. Jis fiziškai perrašo visą lentelę, pašalindamas visas negyvąsias eilutes ir grąžindamas vietą operacinei sistemai. Skamba puikiai, bet yra didžiulis trūkumas – VACUUM FULL užrakina lentelę visiems rašymo ir skaitymo veiksmams.

Produkcinėje sistemoje tai dažniausiai nepriimtina. Įsivaizduokite, kad jūsų pagrindine lentelė tampa nepasiekiama valandai ar net ilgiau. Vartotojai negali prisijungti, užsakymai nepriimami, sistema iš esmės sustoja.

Yra alternatyvų:

pg_repack – išplėtimas, kuris atlieka tą patį darbą kaip VACUUM FULL, bet be lentelės užrakinimo. Jis sukuria naują lentelės kopiją, perkeliama duomenis ir tada atomiškai pakeičia seną lentelę nauja.

Partition switching – jei naudojate particionavimą, galite vakuumuoti atskiras particijas po vieną, minimizuodami poveikį.

Replica rebuild – jei turite replikaciją, galite perkurti repliką nuo nulio ir tada paversti ją master’iu.

Tačiau yra situacijų, kai VACUUM FULL yra tinkamas pasirinkimas – pavyzdžiui, kai turite priežiūros langą ir lentelė yra labai išpūsta (bloat). Jei lentelė turėtų užimti 1GB, bet užima 10GB, VACUUM FULL gali būti vienintelis praktiškas sprendimas.

Stebėjimas ir optimizavimas

Kaip žinoti, ar jūsų vakuumavimo strategija veikia? Reikia stebėti. PostgreSQL teikia daug įrankių tam.

Pirmiausia, patikrinkite pg_stat_user_tables vaizdą:


SELECT schemaname, relname,
n_live_tup, n_dead_tup,
last_vacuum, last_autovacuum,
last_analyze, last_autoanalyze
FROM pg_stat_user_tables
WHERE n_dead_tup > 1000
ORDER BY n_dead_tup DESC;

Tai parodys, kuriose lentelėse kaupiasi negyvos eilutės ir kada jos buvo paskutinį kartą vakuumuotos. Jei matote lenteles su dideliu n_dead_tup skaičiumi ir senomis last_autovacuum datomis, turite problemą.

Bloat (išpūtimas) yra kita svarbi metrika. Yra įvairių užklausų bloat’ui apskaičiuoti, bet paprasčiausia – naudoti pgstattuple išplėtimą:


CREATE EXTENSION pgstattuple;
SELECT * FROM pgstattuple('jusu_lentele');

Tai parodys tikslų dead tuples procentą ir kiek vietos švaistoma.

Jei autovacuum neveikia pakankamai agresyviai, galite koreguoti parametrus postgresql.conf faile:


autovacuum_max_workers = 4 # daugiau lygiagrečių procesų
autovacuum_naptime = 30s # dažnesnis tikrinimas
autovacuum_vacuum_cost_limit = 2000 # greičiau dirba

Bet atsargiai – per agresyvus vakuumavimas gali sukurti I/O spūstis ir sulėtinti visą sistemą.

Praktiniai patarimai ir dažniausios klaidos

Iš patirties galiu pasakyti, kad dauguma problemų su vakuumavimu kyla ne dėl PostgreSQL trūkumų, o dėl netinkamos konfigūracijos ar nesupratimo, kaip sistema veikia.

Ilgos transakcijos – tai vakuumavimo priešas numeris vienas. Jei turite transakciją, kuri trunka valandas (pavyzdžiui, pamiršote commit), VACUUM negali išvalyti jokių eilučių, kurios buvo pakeistos po tos transakcijos pradžios. Rezultatas – greitai augantis bloat. Visada stebėkite ilgas transakcijas:


SELECT pid, now() - xact_start AS duration, query
FROM pg_stat_activity
WHERE state != 'idle'
AND xact_start IS NOT NULL
ORDER BY duration DESC;

Replication slots – nepanaudojami replication slots gali užkirsti kelią vakuumavimui. Jei turėjote repliką, kuri nebeprijungta, bet slot’as liko, jūsų WAL failai augs be galo ir vakuumavimas neveiks tinkamai.

Prepared transactions – panašiai kaip ilgos transakcijos, užmirštos prepared transakcijos gali blokuoti vakuumavimą. Patikrinkite:

SELECT * FROM pg_prepared_xacts;

Netinkami autovacuum parametrai didelėms lentelėms – jau minėjau, bet verta pakartoti. Jei turite 100 milijonų eilučių lentelę su default 20% scale_factor, reikės 20 milijonų pakeitimų, kol autovacuum suveiks. Tai per daug.

Dar vienas patarimas – naudokite VACUUM (VERBOSE, ANALYZE) testuojant. Tai parodys daug naudingos informacijos apie tai, ką VACUUM daro:


VACUUM (VERBOSE, ANALYZE) jusu_lentele;

Išvestyje matysite, kiek puslapių buvo nuskaityta, kiek eilučių pašalinta, kiek laiko užtruko kiekviena operacija. Tai neįkainojama informacija optimizuojant.

Kai viskas veikia sklandžiai

Tinkamas vakuumavimas ir analyze – tai ne vienkartinė užduotis, o nuolatinis procesas. Gerai sukonfigūruota sistema turėtų veikti beveik autonomiškai, su autovacuum, kuris dirba fone ir palaiko viską tvarkingą.

Bet net su geriausiu autovacuum nustatymu, vis tiek reikia reguliaraus stebėjimo. Įtraukite vakuumavimo metrikos į savo monitoring sistemą. Nustatykite alertus, kai bloat viršija tam tikrą slenkstį arba kai autovacuum nespėja laiku.

Ir nepamirškite, kad vakuumavimas yra tik viena duomenų bazės priežiūros dalis. Indeksų priežiūra, statistikos atnaujinimas, WAL archyvavimas – visa tai dirba kartu, kad jūsų PostgreSQL būtų greitas ir patikimas.

Galiausiai, jei susidūrėte su rimtomis vakuumavimo problemomis, nebijokite eksperimentuoti testuose. Sukurkite panašią aplinką, pabandykite skirtingus parametrus, išmatuokite rezultatus. PostgreSQL yra labai lankstus, ir dažnai sprendimas yra paprasčiau surasti, nei atrodo iš pirmo žvilgsnio. Svarbiausia – suprasti, kas vyksta po gaubtu, ir tada galėsite priimti informuotus sprendimus, kurie tikrai veiks jūsų konkrečioje situacijoje.

Daugiau

DaisyUI: Tailwind CSS komponentai