Postgres: bloat, pg_repack a odložené obmedzenia

Postgres: bloat, pg_repack a odložené obmedzenia

Vplyv nadúvania na tabuľky a indexy je všeobecne známy a je prítomný nielen v Postgrese. Existujú spôsoby, ako sa s tým vysporiadať hneď po vybalení, ako napríklad VACUUM FULL alebo CLUSTER, ktoré však počas prevádzky uzamknú stoly, a preto ich nemožno vždy použiť.

Článok bude obsahovať malú teóriu o tom, ako dochádza k nadúvaniu, ako s ním môžete bojovať, o odložených obmedzeniach a problémoch, ktoré so sebou prinášajú používanie rozšírenia pg_repack.

Tento článok je napísaný na základe moja reč na PgConf.Russia 2020.

Prečo dochádza k nadúvaniu?

Postgres je založený na modeli viacerých verzií (MVCC). Jeho podstatou je, že každý riadok v tabuľke môže mať niekoľko verzií, pričom transakcie nevidia viac ako jednu z týchto verzií, ale nie nevyhnutne tú istú. To umožňuje, aby niekoľko transakcií fungovalo súčasne a nemali na seba prakticky žiadny vplyv.

Je zrejmé, že všetky tieto verzie je potrebné uložiť. Postgres pracuje s pamäťou stránku po stránke a stránka predstavuje minimálne množstvo dát, ktoré je možné čítať z disku alebo zapisovať. Pozrime sa na malý príklad, aby sme pochopili, ako sa to deje.

Povedzme, že máme tabuľku, do ktorej sme pridali niekoľko záznamov. Na prvej strane súboru, kde je uložená tabuľka, sa objavili nové údaje. Toto sú živé verzie riadkov, ktoré sú dostupné pre iné transakcie po potvrdení (pre jednoduchosť budeme predpokladať, že úroveň izolácie je Read Committed).

Postgres: bloat, pg_repack a odložené obmedzenia

Potom sme aktualizovali jeden zo záznamov, čím sme starú verziu označili za nepodstatnú.

Postgres: bloat, pg_repack a odložené obmedzenia

Krok za krokom, aktualizáciou a odstraňovaním verzií riadkov, sme skončili so stránkou, na ktorej je približne polovica údajov „odpad“. Tieto údaje nie sú viditeľné pri žiadnej transakcii.

Postgres: bloat, pg_repack a odložené obmedzenia

Postgres má mechanizmus VACUUM, ktorý čistí zastarané verzie a vytvára priestor pre nové dáta. Ak však nie je nakonfigurovaný dostatočne agresívne alebo je zaneprázdnený prácou v iných tabuľkách, zostávajú „odpadové údaje“ a na nové údaje musíme použiť ďalšie stránky.

Takže v našom príklade bude v určitom čase tabuľka pozostávať zo štyroch strán, ale iba polovica z nich bude obsahovať aktuálne údaje. Výsledkom je, že pri prístupe k tabuľke načítame oveľa viac údajov, ako je potrebné.

Postgres: bloat, pg_repack a odložené obmedzenia

Aj keď teraz VACUUM vymaže všetky nepodstatné verzie riadkov, situácia sa dramaticky nezlepší. Budeme mať voľné miesto na stránkach alebo dokonca celých stránkach pre nové riadky, ale stále budeme čítať viac údajov, ako je potrebné.
Mimochodom, ak by bola na konci súboru úplne prázdna strana (druhá v našom príklade), potom by ju VACUUM vedel orezať. Teraz je však v strede, takže sa s ňou nedá nič robiť.

Postgres: bloat, pg_repack a odložené obmedzenia

Keď sa počet takýchto prázdnych alebo veľmi riedkych strán stane veľkým, čo sa nazýva nadúvanie, začne to ovplyvňovať výkon.

Všetko popísané vyššie je mechanika výskytu nadúvania v tabuľkách. V indexoch sa to deje takmer rovnakým spôsobom.

Mám nadúvanie?

Existuje niekoľko spôsobov, ako zistiť, či máte nadúvanie. Myšlienkou prvého je využiť interné štatistiky Postgres, ktoré obsahujú približné informácie o počte riadkov v tabuľkách, počte „živých“ riadkov atď. Na internete nájdete množstvo variácií hotových skriptov. Brali sme ako základ skript od PostgreSQL Experts, ktorá dokáže vyhodnotiť bloat tabuľky spolu s indexmi toastu a bloat btree. Podľa našich skúseností je jeho chyba 10-20%.

Ďalším spôsobom je použitie rozšírenia pgstattuple, ktorá vám umožňuje nahliadnuť do stránok a získať odhadovanú aj presnú hodnotu nafúknutia. Ale v druhom prípade budete musieť naskenovať celú tabuľku.

Za prijateľnú považujeme malú hodnotu nafúknutia do 20 %. Možno ho považovať za analóg výplňového faktora pre tabuľky и indexy. Pri 50 % a viac môžu začať problémy s výkonom.

Spôsoby boja proti nadúvaniu

Postgres má niekoľko spôsobov, ako sa vysporiadať s nadúvaním, no nie vždy sú vhodné pre každého.

Nakonfigurujte AUTOVACUUM tak, aby nedochádzalo k nadúvaniu. Alebo presnejšie, aby to bolo na pre vás prijateľnej úrovni. Vyzerá to ako „kapitánska“ rada, ale v skutočnosti to nie je vždy ľahké dosiahnuť. Napríklad máte aktívny vývoj s pravidelnými zmenami dátovej schémy alebo prebieha nejaká migrácia dát. V dôsledku toho sa váš profil zaťaženia môže často meniť a zvyčajne sa bude líšiť od tabuľky k tabuľke. To znamená, že musíte neustále pracovať trochu dopredu a prispôsobovať AUTOVACUUM meniacemu sa profilu každého stola. Ale očividne to nie je ľahké.

Ďalším častým dôvodom, prečo AUTOVACUUM nedokáže držať krok s tabuľkami, je skutočnosť, že existujú dlhotrvajúce transakcie, ktoré mu bránia vyčistiť dáta, ktoré sú pre tieto transakcie dostupné. Odporúčanie je tiež zrejmé - zbavte sa „visiacich“ transakcií a minimalizujte čas aktívnych transakcií. Ak je však zaťaženie vašej aplikácie hybridom OLAP a OLTP, môžete mať súčasne veľa častých aktualizácií a krátkych dopytov, ako aj dlhodobé operácie – napríklad zostavovanie zostavy. V takejto situácii sa oplatí popremýšľať o rozložení záťaže na rôzne základne, čo umožní precíznejšie doladenie každej z nich.

Ďalší príklad – aj keď je profil homogénny, ale databáza je veľmi zaťažená, potom ani najagresívnejší AUTOVACUUM nemusí zvládnuť a dôjde k nadúvaniu. Jediným riešením je škálovanie (vertikálne alebo horizontálne).

Čo robiť v situácii, keď ste si nastavili AUTOVAKUUM, ale nadúvanie stále rastie.

Tím VÁKUUM PLNÉ prestavuje obsah tabuliek a indexov a ponecháva v nich len relevantné údaje. Na odstránenie bloatu funguje perfektne, no pri jeho vykonávaní je zachytený exkluzívny zámok na stole (AccessExclusiveLock), ktorý neumožní vykonávať dotazy na tomto stole, dokonca selektuje. Ak si môžete dovoliť zastaviť svoju službu alebo jej časť na nejaký čas (od desiatok minút až po niekoľko hodín v závislosti od veľkosti databázy a vášho hardvéru), potom je táto možnosť najlepšia. Žiaľ, počas plánovanej údržby nestihneme spustiť VACUUM FULL, preto tento spôsob nie je pre nás vhodný.

Tím CLUSTER Prestavuje obsah tabuliek rovnakým spôsobom ako VACUUM FULL, ale umožňuje zadať index, podľa ktorého budú dáta fyzicky usporiadané na disku (v budúcnosti však nie je zaručené poradie pre nové riadky). V určitých situáciách je to dobrá optimalizácia pre množstvo dotazov – s čítaním viacerých záznamov podľa indexu. Nevýhoda príkazu je rovnaká ako pri VACUUM FULL - uzamkne stôl počas prevádzky.

Tím REINDEX podobný predchádzajúcim dvom, ale prebuduje konkrétny index alebo všetky indexy tabuľky. Zámky sú o niečo slabšie: ShareLock na stole (bráni úpravám, ale umožňuje výber) a AccessExclusiveLock na indexe, ktorý sa prestavuje (blokuje dotazy pomocou tohto indexu). V 12. verzii Postgres sa však objavil parameter SÚČASNE, ktorý vám umožňuje prebudovať index bez blokovania súbežného pridávania, úpravy alebo odstraňovania záznamov.

V starších verziách Postgres môžete dosiahnuť podobný výsledok ako REINDEX SÚČASNÝM použitím VYTVORIŤ INDEX SÚČASNE. Umožňuje vám vytvoriť index bez prísneho uzamykania (ShareUpdateExclusiveLock, ktorý nezasahuje do paralelných dotazov), potom nahradiť starý index novým a starý index odstrániť. To vám umožňuje eliminovať nadúvanie indexov bez toho, aby ste zasahovali do vašej aplikácie. Je dôležité vziať do úvahy, že pri prestavbe indexov dôjde k dodatočnému zaťaženiu diskového subsystému.

Ak teda pre indexy existujú spôsoby, ako eliminovať nadúvanie „za behu“, potom neexistujú žiadne pre tabuľky. Tu prichádzajú do úvahy rôzne externé rozšírenia: pg_repack (predtým pg_reorg), pgcompact, pgcompacttable a ďalšie. V tomto článku ich nebudem porovnávať a poviem len o pg_repack, ktorý po určitej úprave sami používame.

Ako funguje pg_repack

Postgres: bloat, pg_repack a odložené obmedzenia
Povedzme, že máme úplne obyčajnú tabuľku – s indexmi, obmedzeniami a bohužiaľ aj s nadúvaním. Prvým krokom pg_repack je vytvorenie protokolovej tabuľky na ukladanie údajov o všetkých zmenách, kým je spustený. Spúšťač zopakuje tieto zmeny pre každé vloženie, aktualizáciu a odstránenie. Potom sa vytvorí tabuľka, štruktúrou podobná tej pôvodnej, no bez indexov a obmedzení, aby sa nespomalil proces vkladania údajov.

Ďalej pg_repack prenesie údaje zo starej tabuľky do novej tabuľky, pričom automaticky odfiltruje všetky irelevantné riadky a potom vytvorí indexy pre novú tabuľku. Počas vykonávania všetkých týchto operácií sa zmeny hromadia v tabuľke protokolu.

Ďalším krokom je prenos zmien do novej tabuľky. Migrácia sa vykonáva v niekoľkých iteráciách, a keď v tabuľke protokolu zostane menej ako 20 záznamov, pg_repack získa silný zámok, migruje najnovšie údaje a nahradí starú tabuľku novou v systémových tabuľkách Postgres. Toto je jediný a veľmi krátky čas, kedy nebudete môcť pracovať so stolom. Potom sa stará tabuľka a tabuľka s protokolmi odstránia a v súborovom systéme sa uvoľní miesto. Proces je dokončený.

Teoreticky všetko vyzerá skvele, ale čo sa stane v praxi? Testovali sme pg_repack bez záťaže a pod záťažou a skontrolovali sme jeho činnosť v prípade predčasného zastavenia (inými slovami, pomocou Ctrl+C). Všetky testy boli pozitívne.

Išli sme do obchodu s potravinami - a potom všetko nešlo tak, ako sme očakávali.

Prvá palacinka v predaji

V prvom klastri sme dostali chybu o porušení jedinečného obmedzenia:

$ ./pg_repack -t tablename -o id
INFO: repacking table "tablename"
ERROR: query failed: 
    ERROR: duplicate key value violates unique constraint "index_16508"
DETAIL:  Key (id, index)=(100500, 42) already exists.

Toto obmedzenie malo automaticky vygenerovaný názov index_16508 – vytvoril ho pg_repack. Na základe atribútov obsiahnutých v jeho zložení sme určili „naše“ obmedzenie, ktoré mu zodpovedá. Problémom sa ukázalo, že nejde o úplne bežné obmedzenie, ale o odložené (odložené obmedzenie), t.j. jeho overenie sa vykonáva neskôr ako príkaz sql, čo vedie k neočakávaným následkom.

Odložené obmedzenia: prečo sú potrebné a ako fungujú

Trochu teórie o odložených obmedzeniach.
Zoberme si jednoduchý príklad: máme tabuľkovú referenčnú knihu automobilov s dvoma atribútmi - názvom a poradím auta v adresári.
Postgres: bloat, pg_repack a odložené obmedzenia

create table cars
(
  name text constraint pk_cars primary key,
  ord integer not null constraint uk_cars unique
);



Povedzme, že sme potrebovali vymeniť prvé a druhé auto. Priamym riešením je aktualizovať prvú hodnotu na druhú a druhú na prvú:

begin;
  update cars set ord = 2 where name = 'audi';
  update cars set ord = 1 where name = 'bmw';
commit;

Ale keď spustíme tento kód, očakávame porušenie obmedzenia, pretože poradie hodnôt v tabuľke je jedinečné:

[23305] ERROR: duplicate key value violates unique constraint “uk_cars”
Detail: Key (ord)=(2) already exists.

Ako to môžem urobiť inak? Možnosť jedna: pridajte dodatočnú náhradu hodnoty k objednávke, ktorá zaručene neexistuje v tabuľke, napríklad „-1“. V programovaní sa to nazýva „výmena hodnôt dvoch premenných za tretiu“. Jedinou nevýhodou tejto metódy je dodatočná aktualizácia.

Druhá možnosť: Prerobte návrh tabuľky tak, aby namiesto celých čísel používala typ údajov s pohyblivou rádovou čiarkou pre hodnotu objednávky. Potom pri aktualizácii hodnoty z 1, napríklad na 2.5, prvý záznam automaticky „stojí“ medzi druhým a tretím. Toto riešenie funguje, má však dve obmedzenia. Po prvé, nebude vám fungovať, ak je hodnota použitá niekde v rozhraní. Po druhé, v závislosti od presnosti typu údajov budete mať pred prepočítaním hodnôt všetkých záznamov obmedzený počet možných vložení.

Možnosť XNUMX: odložte obmedzenie, aby sa kontrolovalo iba v čase potvrdenia:

create table cars
(
  name text constraint pk_cars primary key,
  ord integer not null constraint uk_cars unique deferrable initially deferred
);

Keďže logika našej pôvodnej požiadavky zaisťuje, že všetky hodnoty sú v čase potvrdenia jedinečné, bude úspešná.

Príklad diskutovaný vyššie je, samozrejme, veľmi syntetický, ale odhaľuje myšlienku. V našej aplikácii používame odložené obmedzenia na implementáciu logiky, ktorá je zodpovedná za riešenie konfliktov, keď používatelia súčasne pracujú so zdieľanými objektmi widgetu na doske. Použitie takýchto obmedzení nám umožňuje trochu zjednodušiť kód aplikácie.

Vo všeobecnosti, v závislosti od typu obmedzenia, Postgres má tri úrovne granularity na ich kontrolu: úrovne riadkov, transakcií a výrazov.
Postgres: bloat, pg_repack a odložené obmedzenia
Zdroj: begriffs

CHECK a NOT NULL sa vždy kontrolujú na úrovni riadkov, pre iné obmedzenia, ako je vidieť z tabuľky, existujú rôzne možnosti. Môžete si prečítať viac tu.

Stručne povedané, odložené obmedzenia v mnohých situáciách poskytujú čitateľnejší kód a menej príkazov. Za to však musíte zaplatiť skomplikovaním procesu ladenia, keďže moment výskytu chyby a moment, keď sa o nej dozviete, sú časovo oddelené. Ďalším možným problémom je, že plánovač nemusí byť vždy schopný zostaviť optimálny plán, ak požiadavka zahŕňa odložené obmedzenie.

Vylepšenie pg_repack

Prebrali sme, čo sú odložené obmedzenia, ale ako súvisia s naším problémom? Pripomeňme si chybu, ktorú sme dostali predtým:

$ ./pg_repack -t tablename -o id
INFO: repacking table "tablename"
ERROR: query failed: 
    ERROR: duplicate key value violates unique constraint "index_16508"
DETAIL:  Key (id, index)=(100500, 42) already exists.

Vyskytuje sa pri kopírovaní údajov z protokolovej tabuľky do novej tabuľky. Vyzerá to zvláštne, pretože... údaje v tabuľke protokolu sú potvrdené spolu s údajmi v zdrojovej tabuľke. Ak spĺňajú obmedzenia pôvodnej tabuľky, ako môžu porušiť rovnaké obmedzenia v novej?

Ako sa ukázalo, koreň problému spočíva v predchádzajúcom kroku pg_repack, ktorý vytvára iba indexy, ale nie obmedzenia: stará tabuľka mala jedinečné obmedzenie a nová namiesto toho vytvorila jedinečný index.

Postgres: bloat, pg_repack a odložené obmedzenia

Tu je dôležité poznamenať, že ak je obmedzenie normálne a nie je odložené, potom vytvorený jedinečný index je ekvivalentný tomuto obmedzeniu, pretože Jedinečné obmedzenia v Postgrese sú implementované vytvorením jedinečného indexu. Ale v prípade odloženého obmedzenia nie je správanie rovnaké, pretože index nemožno odložiť a je vždy kontrolovaný v čase vykonania príkazu sql.

Podstata problému teda spočíva v „oneskorení“ kontroly: v pôvodnej tabuľke sa vyskytuje v čase potvrdenia a v novej tabuľke v čase vykonania príkazu sql. To znamená, že musíme zabezpečiť, aby sa kontroly vykonávali rovnako v oboch prípadoch: buď vždy s oneskorením, alebo vždy okamžite.

Aké nápady sme teda mali?

Vytvorte index podobný ako odložený

Prvou myšlienkou je vykonať obe kontroly v okamžitom režime. To môže generovať niekoľko falošne pozitívnych obmedzení, ale ak ich je málo, nemalo by to mať vplyv na prácu používateľov, pretože takéto konflikty sú pre nich normálnou situáciou. Vyskytujú sa napríklad vtedy, keď dvaja používatelia začnú upravovať rovnaký widget súčasne a klient druhého používateľa nestihne dostať informáciu, že widget je už zablokovaný na úpravy prvým používateľom. V takejto situácii server odmietne druhého používateľa a jeho klient vráti späť zmeny a zablokuje widget. O niečo neskôr, keď prvý používateľ dokončí úpravy, druhý dostane informáciu, že miniaplikácia už nie je zablokovaná a bude môcť svoju akciu zopakovať.

Postgres: bloat, pg_repack a odložené obmedzenia

Aby sme zaistili, že kontroly budú vždy v neodloženom režime, vytvorili sme nový index podobný pôvodnému odloženému obmedzeniu:

CREATE UNIQUE INDEX CONCURRENTLY uk_tablename__immediate ON tablename (id, index);
-- run pg_repack
DROP INDEX CONCURRENTLY uk_tablename__immediate;

V testovacom prostredí sme dostali len niekoľko očakávaných chýb. Úspech! Na produkcii sme znova spustili pg_repack a za hodinu práce sme dostali 5 chýb na prvom klastri. Toto je prijateľný výsledok. Avšak už na druhom klastri sa počet chýb výrazne zvýšil a museli sme zastaviť pg_repack.

Prečo sa to stalo? Pravdepodobnosť výskytu chyby závisí od toho, koľko používateľov súčasne pracuje s rovnakými miniaplikáciami. Zrejme v tom momente bolo s dátami uloženými na prvom klastri oveľa menej konkurenčných zmien ako na ostatných, t.j. mali sme len "šťastie".

Nápad nevyšiel. V tom momente sme videli dve ďalšie riešenia: prepíšte náš aplikačný kód, aby ste sa zbavili odložených obmedzení, alebo „naučte“ pg_repack s nimi pracovať. Vybrali sme si to druhé.

Nahraďte indexy v novej tabuľke odloženými obmedzeniami z pôvodnej tabuľky

Účel revízie bol zrejmý – ak má pôvodná tabuľka odložené obmedzenie, potom pre novú musíte vytvoriť takéto obmedzenie a nie index.

Aby sme otestovali naše zmeny, napísali sme jednoduchý test:

  • tabuľka s odloženým obmedzením a jedným záznamom;
  • vložiť údaje do slučky, ktorá je v konflikte s existujúcim záznamom;
  • vykonať aktualizáciu – údaje už nie sú v konflikte;
  • vykonať zmeny.

create table test_table
(
  id serial,
  val int,
  constraint uk_test_table__val unique (val) deferrable initially deferred 
);

INSERT INTO test_table (val) VALUES (0);
FOR i IN 1..10000 LOOP
  BEGIN
    INSERT INTO test_table VALUES (0) RETURNING id INTO v_id;
    UPDATE test_table set val = i where id = v_id;
    COMMIT;
  END;
END LOOP;

Pôvodná verzia pg_repack spadla vždy pri prvom vložení, upravená verzia fungovala bez chýb. Skvelé.

Ideme do výroby a znova dostaneme chybu v rovnakej fáze kopírovania údajov z tabuľky protokolu do novej:

$ ./pg_repack -t tablename -o id
INFO: repacking table "tablename"
ERROR: query failed: 
    ERROR: duplicate key value violates unique constraint "index_16508"
DETAIL:  Key (id, index)=(100500, 42) already exists.

Klasická situácia: v testovacích prostrediach všetko funguje, ale v produkcii nie?!

APPLY_COUNT a spojenie dvoch dávok

Začali sme analyzovať kód doslova riadok po riadku a zistili sme dôležitý bod: údaje sa prenášajú z tabuľky protokolu do novej v dávkach, konštanta APPLY_COUNT indikovala veľkosť dávky:

for (;;)
{
num = apply_log(connection, table, APPLY_COUNT);

if (num > MIN_TUPLES_BEFORE_SWITCH)
     continue;  /* there might be still some tuples, repeat. */
...
}

Problém je v tom, že dáta z pôvodnej transakcie, v ktorej by viacero operácií mohlo potenciálne porušiť obmedzenie, sa pri prenose môžu dostať na spojenie dvoch dávok – polovica príkazov bude vykonaná v prvej dávke a druhá polovica v druhom. A tu, v závislosti od šťastia: ak tímy v prvej dávke nič neporušia, potom je všetko v poriadku, ale ak áno, dôjde k chybe.

APPLY_COUNT sa rovná 1000 500 záznamom, čo vysvetľuje, prečo boli naše testy úspešné – nepokrývali prípad „dávkového spojenia“. Použili sme dva príkazy - insert a update, takže vždy bolo v dávke umiestnených presne XNUMX transakcií dvoch príkazov a nezaznamenali sme žiadne problémy. Po pridaní druhej aktualizácie naša úprava prestala fungovať:

FOR i IN 1..10000 LOOP
  BEGIN
    INSERT INTO test_table VALUES (1) RETURNING id INTO v_id;
    UPDATE test_table set val = i where id = v_id;
    UPDATE test_table set val = i where id = v_id; -- one more update
    COMMIT;
  END;
END LOOP;

Ďalšou úlohou je teda zabezpečiť, aby dáta z pôvodnej tabuľky, ktorá bola zmenená v jednej transakcii, skončili v novej tabuľke aj v rámci jednej transakcie.

Odmietnutie dávkovania

A opäť sme mali dve riešenia. Po prvé: úplne zanechajme rozdelenie na dávky a prenesme dáta v jednej transakcii. Výhodou tohto riešenia bola jeho jednoduchosť – požadované zmeny kódu boli minimálne (mimochodom, v starších verziách pg_reorg fungoval presne takto). Ale je tu problém - vytvárame dlhotrvajúcu transakciu, a to, ako už bolo povedané, je hrozbou pre vznik nového nafúknutia.

Druhé riešenie je zložitejšie, ale pravdepodobne správnejšie: vytvorte stĺpec v tabuľke protokolu s identifikátorom transakcie, ktorá pridala údaje do tabuľky. Potom, keď kopírujeme údaje, môžeme ich zoskupiť podľa tohto atribútu a zabezpečiť, aby sa súvisiace zmeny preniesli spoločne. Dávka sa vytvorí z niekoľkých transakcií (alebo jednej veľkej) a jej veľkosť sa bude líšiť v závislosti od toho, koľko údajov sa v týchto transakciách zmenilo. Je dôležité poznamenať, že keďže údaje z rôznych transakcií vstupujú do tabuľky protokolov v náhodnom poradí, nebude ich už možné čítať postupne, ako to bolo predtým. seqscan pre každú požiadavku s filtrovaním podľa tx_id je príliš drahý, je potrebný index, ale tiež spomalí metódu kvôli réžii jej aktualizácie. Vo všeobecnosti, ako vždy, musíte niečo obetovať.

Preto sme sa rozhodli začať prvou možnosťou, pretože je jednoduchšia. Najprv bolo potrebné pochopiť, či dlhá transakcia bude skutočným problémom. Keďže hlavný prenos údajov zo starej tabuľky do novej prebieha aj v jednej dlhej transakcii, otázka sa pretransformovala na „o koľko túto transakciu navýšime?“ Trvanie prvej transakcie závisí najmä od veľkosti stola. Trvanie nového závisí od toho, koľko zmien sa nahromadí v tabuľke počas prenosu dát, t.j. na intenzite zaťaženia. Spustenie pg_repack prebehlo v čase minimálneho servisného zaťaženia a objem zmien bol neúmerne malý v porovnaní s pôvodnou veľkosťou tabuľky. Rozhodli sme sa, že môžeme zanedbať čas novej transakcie (pre porovnanie, v priemere je to 1 hodina a 2-3 minúty).

Pokusy boli pozitívne. Spustenie aj do výroby. Pre prehľadnosť uvádzame obrázok s veľkosťou jednej z databáz po spustení:

Postgres: bloat, pg_repack a odložené obmedzenia

Keďže sme boli s týmto riešením úplne spokojní, nepokúšali sme sa implementovať druhé, ale zvažujeme možnosť prediskutovať ho s vývojármi rozšírenia. Naša aktuálna revízia, žiaľ, ešte nie je pripravená na zverejnenie, keďže sme problém vyriešili len s unikátnymi odloženými obmedzeniami a pre plnohodnotný patch je potrebné zabezpečiť podporu pre iné typy. Dúfame, že sa nám to v budúcnosti podarí.

Možno máte otázku, prečo sme sa vôbec zapojili do tohto príbehu s úpravou pg_repack a nepoužili sme napríklad jeho analógy? V určitom okamihu sme o tom tiež uvažovali, ale pozitívna skúsenosť s jeho používaním skôr, na stoloch bez odložených obmedzení, nás motivovala k tomu, aby sme sa pokúsili pochopiť podstatu problému a vyriešiť ho. Okrem toho si použitie iných riešení vyžaduje aj čas na vykonanie testov, preto sme sa rozhodli, že sa najskôr pokúsime problém vyriešiť v ňom, a ak si uvedomíme, že to nemôžeme urobiť v primeranom čase, začneme hľadať analógy. .

Závery

Čo môžeme odporučiť na základe vlastných skúseností:

  1. Sledujte svoje nadúvanie. Na základe údajov z monitorovania môžete pochopiť, ako dobre je nakonfigurované automatické vákuum.
  2. Upravte AUTOVACUUM, aby ste udržali nadúvanie na prijateľnej úrovni.
  3. Ak nadúvanie stále rastie a nedokážete ho prekonať pomocou hotových nástrojov, nebojte sa použiť externé rozšírenia. Hlavná vec je všetko dobre otestovať.
  4. Nebojte sa upravovať externé riešenia podľa svojich potrieb – niekedy to môže byť efektívnejšie a dokonca jednoduchšie ako zmena vlastného kódu.

Zdroj: hab.com

Pridať komentár