Postgres: bloat, pg_repack és késleltetett megszorítások

Postgres: bloat, pg_repack és késleltetett megszorítások

A felfúvódás táblázatokra és indexekre gyakorolt ​​hatása széles körben ismert, és nem csak a Postgresben van jelen. Vannak módok a megoldásra, mint például a VACUUM FULL vagy a CLUSTER, de működés közben lezárják az asztalokat, ezért nem mindig használhatók.

A cikk egy kis elméletet fog tartalmazni arról, hogyan lép fel a duzzanat, hogyan küzdhet le ellene, a késleltetett korlátozásokról és a pg_repack kiterjesztés használatával kapcsolatos problémákról.

Ez a cikk az alapján készült a beszédem a PgConf.Russia 2020-on.

Miért fordul elő puffadás?

A Postgres egy több verziós modellen alapul (MVCC). Lényege, hogy a táblázat minden sorának több verziója is lehet, míg a tranzakciók ezek közül legfeljebb egyet látnak, de nem feltétlenül ugyanazt. Ez lehetővé teszi több tranzakció egyidejű működését, és gyakorlatilag nincs hatással egymásra.

Nyilvánvalóan ezeket a verziókat tárolni kell. A Postgres laponként dolgozik a memóriával, és egy oldal a lemezről olvasható vagy írható minimális adatmennyiség. Nézzünk egy kis példát, hogy megértsük, hogyan történik ez.

Tegyük fel, hogy van egy táblázatunk, amelyhez több rekordot is hozzáadtunk. Új adatok jelentek meg a táblázatot tartalmazó fájl első oldalán. Ezek a sorok élő változatai, amelyek a véglegesítés után elérhetők más tranzakciók számára (az egyszerűség kedvéért feltételezzük, hogy az elkülönítési szint Read Committed).

Postgres: bloat, pg_repack és késleltetett megszorítások

Ezt követően frissítettük az egyik bejegyzést, így a régi verziót már nem relevánsként jelöltük meg.

Postgres: bloat, pg_repack és késleltetett megszorítások

Lépésről lépésre, a soros verziók frissítésével és törlésével egy olyan oldalra jutottunk, amelyen az adatok hozzávetőleg a fele „szemét”. Ezek az adatok egyetlen tranzakciónál sem láthatók.

Postgres: bloat, pg_repack és késleltetett megszorítások

A Postgres-nek van egy mechanizmusa VÁKUUM, amely megtisztítja az elavult verziókat, és helyet ad az új adatoknak. De ha nincs elég agresszíven beállítva, vagy más táblákban dolgozik, akkor maradnak a „szemétadatok”, és további oldalakat kell használnunk az új adatokhoz.

Példánkban tehát a táblázat egy bizonyos időpontban négy oldalból áll majd, de csak a fele tartalmaz majd élő adatokat. Ennek eredményeként a táblázat elérésekor a szükségesnél sokkal több adatot fogunk kiolvasni.

Postgres: bloat, pg_repack és késleltetett megszorítások

Még ha a VACUUM most törli az összes irreleváns sorverziót, a helyzet nem fog drámaian javulni. Lesz szabad helyünk oldalakon vagy akár egész oldalakon az új sorok számára, de továbbra is több adatot fogunk olvasni a szükségesnél.
Egyébként, ha egy teljesen üres oldal (a példánkban a második) lenne a fájl végén, akkor a VACUUM képes lenne levágni. De most középen van, így nem lehet vele mit kezdeni.

Postgres: bloat, pg_repack és késleltetett megszorítások

Amikor az ilyen üres vagy nagyon ritka oldalak száma megnövekszik, amit felfúvódásnak neveznek, az elkezdi befolyásolni a teljesítményt.

Minden, amit fent leírtunk, a puffadás előfordulásának mechanikája a táblázatokban. Az indexeknél ez nagyjából ugyanúgy történik.

puffadásom van?

Számos módja van annak megállapítására, hogy van-e puffadás. Az első ötlete a Postgres belső statisztikáinak felhasználása, amely hozzávetőleges információkat tartalmaz a táblázatok sorainak számáról, az „élő” sorok számáról stb. Az interneten számos kész szkriptet találhat. Mi vettük alapul forgatókönyv a PostgreSQL Expertstől, amely képes kiértékelni a bloat táblázatokat a toast és a bloat btree indexekkel együtt. Tapasztalataink szerint a hibája 10-20%.

Egy másik módszer a kiterjesztés használata pgstattuple, amely lehetővé teszi, hogy betekintsen az oldalak belsejébe, és megkapja a becsült és a pontos felfúvódási értéket. De a második esetben az egész táblázatot át kell vizsgálnia.

Kis felfúvódási értéket, akár 20%-ot is elfogadhatónak tartunk. A fillfactor analógjának tekinthető táblázatos kimutatás и indexek. 50%-nál és afelett teljesítményproblémák kezdődhetnek.

A puffadás leküzdésének módjai

A Postgres többféleképpen is kezelheti a puffadást, de nem mindig mindenki számára megfelelőek.

Állítsa be az AUTOVACUUM funkciót, hogy ne forduljon elő felfúvódás. Pontosabban, hogy egy számodra elfogadható szinten tartsd. Ez „kapitányi” tanácsnak tűnik, de a valóságban ezt nem mindig könnyű elérni. Például aktív fejlesztés folyik az adatséma rendszeres módosításával, vagy valamilyen adatmigráció történik. Ennek eredményeként a terhelési profil gyakran változhat, és általában táblázatonként változik. Ez azt jelenti, hogy folyamatosan egy kicsit előre kell dolgoznia, és az AUTOVACUUM-ot az egyes asztalok változó profiljához kell igazítania. De nyilván ezt nem könnyű megtenni.

Egy másik gyakori ok, amiért az AUTOVACUUM nem tud lépést tartani a táblákkal, az az, hogy vannak olyan régóta futó tranzakciók, amelyek megakadályozzák, hogy kitisztítsa az adott tranzakciók számára elérhető adatokat. Az ajánlás itt is nyilvánvaló - szabaduljon meg a „lógó” tranzakcióktól, és minimalizálja az aktív tranzakciók idejét. De ha az alkalmazás terhelése az OLAP és az OLTP hibridje, akkor egyszerre számos gyakori frissítés és rövid lekérdezés, valamint hosszú távú műveletek végezhetők - például jelentéskészítés. Ilyen helyzetben érdemes elgondolkodni a terhelés különböző alapokon történő elosztásán, ami lehetővé teszi az egyes alapok finomabb beállítását.

Egy másik példa - még ha a profil homogén, de az adatbázis nagyon nagy terhelés alatt áll, akkor még a legagresszívebb AUTOVACUUM sem képes megbirkózni, és felfúvódás következik be. A méretezés (függőleges vagy vízszintes) az egyetlen megoldás.

Mi a teendő olyan helyzetben, amikor beállította az AUTOVACUUM funkciót, de a puffadás tovább nő.

Csapat VÁKUUM MEGTELT újraépíti a táblák és indexek tartalmát, és csak releváns adatokat hagy bennük. A felfúvódás kiküszöbölésére tökéletesen működik, de végrehajtása során egy exkluzív zárolás kerül rögzítésre az asztalon (AccessExclusiveLock), amely nem teszi lehetővé a lekérdezések végrehajtását ezen a táblán, még a kiválasztásokat sem. Ha megengedheti magának, hogy bizonyos időre leállítsa szolgáltatását vagy annak egy részét (az adatbázis méretétől és a hardvertől függően több tíz perctől több óráig), akkor ez a lehetőség a legjobb. Sajnos az ütemezett karbantartás során nincs időnk a VÁKUUM TELJES futtatására, így ez a módszer számunkra nem megfelelő.

Csapat CSOPORT A táblák tartalmát ugyanúgy újraépíti, mint a VACUUM FULL, de lehetővé teszi egy index megadását, amely szerint az adatok fizikailag a lemezen lesznek rendezve (de a jövőben az új soroknál nem garantált a sorrend). Bizonyos helyzetekben ez jó optimalizálás számos lekérdezéshez – több rekord index szerinti beolvasásával. A parancs hátránya ugyanaz, mint a VACUUM FULL-é - működés közben zárolja az asztalt.

Csapat REINDEX hasonló az előző kettőhöz, de egy adott indexet vagy a tábla összes indexét újjáépíti. A zárolások valamivel gyengébbek: a ShareLock az asztalon (megakadályozza a módosításokat, de engedélyezi a kiválasztást) és az AccessExclusiveLock az újraépítendő indexen (blokkolja az indexet használó lekérdezéseket). A Postgres 12. verziójában azonban megjelent egy paraméter EGYSZERŰEN, amely lehetővé teszi az index újraépítését anélkül, hogy blokkolná a rekordok egyidejű hozzáadását, módosítását vagy törlését.

A Postgres korábbi verzióiban a REINDEX EGYESÜLJÜK használatával hasonló eredményt érhet el INDEX EGYESÜLŐ LÉTREHOZÁSA. Lehetővé teszi, hogy szigorú zárolás nélkül készítsünk indexet (ShareUpdateExclusiveLock, ami nem zavarja a párhuzamos lekérdezéseket), majd a régi indexet egy újra cseréljük, és töröljük a régi indexet. Ez lehetővé teszi az index felfúvódásának megszüntetését anélkül, hogy zavarná az alkalmazást. Fontos figyelembe venni, hogy az indexek újraépítésekor a lemez alrendszere további terhelést jelent.

Így, ha az indexeknél van mód a felfúvódás „menet közben” megszüntetésére, akkor a táblázatoknál nincs. Itt lépnek életbe a különféle külső bővítmények: pg_repack (korábban pg_reorg), pgcompact, pgcompacttable és mások. Ebben a cikkben nem hasonlítom össze őket, és csak a pg_repack-ről beszélek, amelyet némi módosítás után magunk is használunk.

Hogyan működik a pg_repack

Postgres: bloat, pg_repack és késleltetett megszorítások
Tegyük fel, hogy van egy teljesen hétköznapi táblázatunk - indexekkel, korlátozásokkal és sajnos felfújással. A pg_repack első lépése egy naplótábla létrehozása, amely az összes változás adatait tárolja futás közben. A trigger megismétli ezeket a változtatásokat minden beszúrásnál, frissítésnél és törlésnél. Ezután létrejön egy tábla, amely az eredetihez hasonló szerkezetű, de indexek és korlátozások nélkül, hogy ne lassítsa az adatok beillesztési folyamatát.

Ezután a pg_repack átviszi az adatokat a régi táblából az új táblába, automatikusan kiszűri az összes irreleváns sort, majd indexeket hoz létre az új táblához. Mindezen műveletek végrehajtása során a változások halmozódnak fel a naplótáblázatban.

A következő lépés a változtatások átvitele az új táblába. Az áttelepítés több iteráción keresztül történik, és ha 20-nál kevesebb bejegyzés maradt a naplótáblában, a pg_repack erős zárolást kap, átállítja a legfrissebb adatokat, és a régi táblát lecseréli az újra a Postgres rendszertáblázataiban. Ez az egyetlen és nagyon rövid idő, amikor nem fog tudni dolgozni az asztallal. Ezt követően a régi tábla és a naplókat tartalmazó tábla törlődik, és hely szabadul fel a fájlrendszerben. A folyamat befejeződött.

Elméletben minden nagyszerűnek tűnik, de mi történik a gyakorlatban? Teszteltük a pg_repack-et terhelés nélkül és terhelés alatt, és ellenőriztük a működését idő előtti leállás esetén (vagyis a Ctrl+C használatával). Minden teszt pozitív volt.

Elmentünk az élelmiszerboltba - aztán nem minden úgy ment, ahogy vártuk.

Eladó az első palacsinta

Az első klaszteren hibaüzenetet kaptunk egy egyedi megszorítás megsértésével kapcsolatban:

$ ./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.

Ennek a korlátozásnak az index_16508 automatikusan generált neve volt – a pg_repack hozta létre. Az összetételében szereplő attribútumok alapján meghatároztuk a neki megfelelő „mi” kényszerünket. A probléma az volt, hogy ez nem egy teljesen hétköznapi korlátozás, hanem egy elhalasztott (halasztott kényszer), azaz ellenőrzése később történik, mint az sql parancsé, ami váratlan következményekkel jár.

Elhalasztott kényszerek: miért van szükség rájuk és hogyan működnek

Egy kis elmélet a halasztott korlátozásokról.
Vegyünk egy egyszerű példát: van egy táblázat-referenciakönyvünk az autókról, két attribútummal - az autó neve és sorrendje a címtárban.
Postgres: bloat, pg_repack és késleltetett megszorítások

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



Tegyük fel, hogy fel kellett cserélnünk az első és a második autót. Az egyszerű megoldás az első érték frissítése a másodikra, a második pedig az elsőre:

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

De amikor ezt a kódot futtatjuk, a megszorítások megsértésére számítunk, mivel a táblázatban szereplő értékek sorrendje egyedi:

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

Hogyan csinálhatnám másképp? Első lehetőség: adjon hozzá egy további értéket egy olyan rendeléshez, amely garantáltan nem szerepel a táblázatban, például „-1”. A programozásban ezt „két változó értékének egy harmadikra ​​cserélésének” nevezik. Ennek a módszernek az egyetlen hátránya a további frissítés.

Második lehetőség: Tervezze át a táblát úgy, hogy egész számok helyett lebegőpontos adattípust használjon a rendelési értékhez. Ezután, amikor például 1-ről 2.5-re frissíti az értéket, az első bejegyzés automatikusan „áll” a második és a harmadik között. Ez a megoldás működik, de két korlátja van. Először is, nem fog működni, ha az értéket valahol a felületen használják. Másodszor, az adattípus pontosságától függően korlátozott számú lehetséges beszúrás áll rendelkezésére az összes rekord értékének újraszámítása előtt.

Harmadik lehetőség: tegye a megszorítást elhalasztottá, hogy csak a véglegesítéskor kerüljön ellenőrzésre:

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

Mivel a kezdeti kérésünk logikája biztosítja, hogy a véglegesítés időpontjában minden érték egyedi legyen, ez sikerülni fog.

A fent tárgyalt példa természetesen nagyon szintetikus, de felfedi az ötletet. Alkalmazásunkban késleltetett kényszereket használunk a logika megvalósítására, amely felelős az ütközések feloldásáért, amikor a felhasználók egyidejűleg a táblán megosztott widget-objektumokkal dolgoznak. Az ilyen korlátozások használata lehetővé teszi, hogy egy kicsit egyszerűbbé tegyük az alkalmazás kódját.

Általánosságban elmondható, hogy a megszorítás típusától függően a Postgres három részletességi szinttel rendelkezik ezek ellenőrzésére: sor, tranzakció és kifejezés szintje.
Postgres: bloat, pg_repack és késleltetett megszorítások
Forrás: begriffek

A CHECK és a NOT NULL mindig sorszinten van bejelölve, a többi korlátozáshoz, amint az a táblázatból is látható, különböző lehetőségek állnak rendelkezésre. Bővebben olvashatsz itt.

Röviden összefoglalva, a halasztott kényszerek számos helyzetben jobban olvasható kódot és kevesebb parancsot biztosítanak. Ezt azonban a hibakeresési folyamat bonyolításával kell fizetni, hiszen a hiba fellépése és a tudomásszerzés pillanata időben elválik egymástól. Egy másik lehetséges probléma az, hogy az ütemező nem mindig tud optimális tervet összeállítani, ha a kérés halasztott kényszert tartalmaz.

A pg_repack továbbfejlesztése

Kitértünk arra, hogy melyek a halasztott korlátozások, de hogyan kapcsolódnak ezek a problémánkhoz? Emlékezzünk a korábban kapott hibára:

$ ./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.

Ez akkor fordul elő, amikor az adatokat egy naplótáblából egy új táblába másolják. Ez furcsán néz ki, mert... a naplótábla adatai a forrástábla adataival együtt kerülnek véglegesítésre. Ha eleget tesznek az eredeti tábla megkötéseinek, hogyan sérthetik meg ugyanazokat a megszorításokat az újban?

Mint kiderült, a probléma gyökere a pg_repack előző lépésében rejlik, amely csak indexeket hoz létre, megszorításokat nem: a régi táblának egyedi megszorítása volt, az új pedig egyedi indexet hozott létre helyette.

Postgres: bloat, pg_repack és késleltetett megszorítások

Itt fontos megjegyezni, hogy ha a megszorítás normál és nem késleltetett, akkor a helyette létrehozott egyedi index ekvivalens ezzel a megszorítással, mert A Postgres egyedi megszorításai egyedi index létrehozásával valósulnak meg. De késleltetett megszorítás esetén a viselkedés nem ugyanaz, mert az index nem halasztható, és mindig az sql parancs végrehajtásakor kerül ellenőrzésre.

A probléma lényege tehát az ellenőrzés „késleltetésében” van: az eredeti táblában a véglegesítéskor, az új táblában pedig az sql parancs végrehajtásakor következik be. Ez azt jelenti, hogy meg kell győződnünk arról, hogy az ellenőrzések mindkét esetben azonosak legyenek: vagy mindig késve, vagy mindig azonnal.

Szóval milyen ötleteink voltak?

Hozzon létre egy, a halasztotthoz hasonló indexet

Az első ötlet az, hogy mindkét ellenőrzést azonnali módban hajtsa végre. Ez több hamis pozitív korlátozást generálhat, de ha kevés van belőlük, akkor ez nem befolyásolhatja a felhasználók munkáját, mivel az ilyen konfliktusok normális helyzetek számukra. Például akkor fordulnak elő, amikor két felhasználó egyszerre kezdi el szerkeszteni ugyanazt a widgetet, és a második felhasználó kliensének nincs ideje arra, hogy információt kapjon arról, hogy a widgetet az első felhasználó már letiltotta szerkesztésében. Ilyen helyzetben a szerver visszautasítja a második felhasználót, a kliens pedig visszavonja a változtatásokat és blokkolja a widgetet. Kicsit később, amikor az első felhasználó befejezi a szerkesztést, a második információt kap arról, hogy a widget már nincs blokkolva, és meg tudja ismételni a műveletet.

Postgres: bloat, pg_repack és késleltetett megszorítások

Annak biztosítására, hogy az ellenőrzések mindig nem halasztott módban legyenek, létrehoztunk egy új indexet, amely hasonló az eredeti halasztott kényszerhez:

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

A tesztkörnyezetben csak néhány várt hibát kaptunk. Siker! Újra futtattuk a pg_repack-et éles környezetben, és 5 hibát kaptunk az első fürtnél egy óra munka alatt. Ez elfogadható eredmény. Azonban már a második klaszteren jelentősen megnőtt a hibák száma, és le kellett állítani a pg_repack-et.

Miért történt ez? A hiba előfordulásának valószínűsége attól függ, hogy hány felhasználó dolgozik egyidejűleg ugyanazokkal a widgetekkel. Nyilvánvalóan abban a pillanatban sokkal kevesebb kompetitív változás történt az első klaszteren tárolt adatokkal, mint a többi klaszteren, pl. csak „szerencsénk” volt.

Az ötlet nem működött. Ekkor két másik megoldást is láttunk: átírjuk az alkalmazás kódját, hogy ne legyenek késleltetett kényszerek, vagy „megtanítsuk” a pg_repack-et, hogy működjön velük. Mi a másodikat választottuk.

Cserélje le az indexeket az új táblában az eredeti tábla késleltetett megszorításaira

A felülvizsgálat célja nyilvánvaló volt - ha az eredeti táblának van halasztott megszorítása, akkor az újhoz ilyen megszorítást kell létrehoznia, nem indexet.

Változásaink tesztelésére egy egyszerű tesztet írtunk:

  • táblázat egy halasztott megszorítással és egy rekorddal;
  • olyan adat beszúrása hurokba, amely ütközik egy meglévő rekorddal;
  • végezzen frissítést – az adatok már nem ütköznek egymással;
  • végrehajtani a változtatásokat.

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;

A pg_repack eredeti verziója mindig az első beillesztéskor összeomlott, a módosított verzió hiba nélkül működött. Nagy.

Átlépünk a termelésbe, és ismét hibaüzenetet kapunk az adatoknak a naplótáblából egy újba másolásakor:

$ ./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.

Klasszikus szituáció: tesztkörnyezetben minden működik, élesben viszont nem?!

APPLY_COUNT és két köteg csomópontja

Elkezdtük szó szerint soronként elemezni a kódot, és felfedeztünk egy fontos pontot: az adatok kötegenként kerülnek át a naplótáblából egy újba, az APPLY_COUNT konstans a köteg méretét jelezte:

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

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

A probléma az, hogy az eredeti tranzakcióból származó adatok, amelyekben több művelet is megsértheti a megkötést, átvitelkor két köteg találkozási pontjára kerülhet – a parancsok fele az első kötegben kerül végrehajtásra, a másik fele a másodikban. És itt, a szerencsédtől függően: ha a csapatok nem sértenek semmit az első adagban, akkor minden rendben van, de ha mégis, akkor hiba történik.

APPLY_COUNT egyenlő 1000 rekorddal, ami megmagyarázza, hogy tesztjeink miért voltak sikeresek – nem terjedtek ki a „batch junction” esetére. Két parancsot használtunk - beszúrás és frissítés, így mindig két parancsból pontosan 500 tranzakció került egy kötegbe, és nem tapasztaltunk problémát. A második frissítés hozzáadása után a szerkesztésünk leállt:

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;

Tehát a következő feladat annak biztosítása, hogy az eredeti tábla adatai, amelyek egy tranzakció során megváltoztak, egy tranzakción belül is az új táblába kerüljenek.

A kötegelés megtagadása

És megint két megoldásunk volt. Először is: hagyjuk teljesen fel a kötegekre particionálást, és vigyük át az adatokat egy tranzakcióban. Ennek a megoldásnak az előnye az egyszerűsége volt - a szükséges kódmódosítások minimálisak voltak (mellesleg a régebbi verziókban a pg_reorg pontosan így működött). De van egy probléma – egy hosszú távú tranzakciót hozunk létre, és ez, amint korábban említettük, egy új felfúvódás kialakulását fenyegeti.

A második megoldás bonyolultabb, de valószínűleg helyesebb: hozzon létre egy oszlopot a naplótáblázatban a táblához adatot hozzáadó tranzakció azonosítójával. Ezután, amikor adatokat másolunk, csoportosíthatjuk ezeket az attribútumokat, és biztosíthatjuk, hogy a kapcsolódó változtatások együtt átvitelre kerüljenek. A köteg több tranzakcióból (vagy egy nagyból) jön létre, és a mérete attól függően változik, hogy mennyi adatot módosítottak ezekben a tranzakciókban. Fontos megjegyezni, hogy mivel a különböző tranzakciókból származó adatok véletlenszerű sorrendben kerülnek a naplótáblázatba, többé nem lesz lehetőség szekvenciális beolvasásra, mint korábban. A seqscan minden kérésre tx_id szerinti szűréssel túl drága, index kell, de a frissítési ráfordítás miatt lelassítja a metódust is. Általában, mint mindig, fel kell áldozni valamit.

Ezért úgy döntöttünk, hogy az első lehetőséggel kezdjük, mivel az egyszerűbb. Először is meg kellett érteni, hogy egy hosszú tranzakció valódi problémát jelent-e. Mivel a fő adatátvitel a régi táblából az újba is egy hosszú tranzakció során történik, a kérdés átalakult „mennyivel növeljük ezt a tranzakciót?”. Az első tranzakció időtartama elsősorban az asztal méretétől függ. Egy új időtartama attól függ, hogy az adatátvitel során hány változás halmozódik fel a táblázatban, pl. a terhelés intenzitásáról. A pg_repack futtatása minimális szolgáltatásterhelés alatt történt, és a változtatások mennyisége aránytalanul kicsi volt a tábla eredeti méretéhez képest. Úgy döntöttünk, hogy az új tranzakció idejét figyelmen kívül hagyhatjuk (összehasonlításképpen átlagosan 1 óra 2-3 perc).

A kísérletek pozitívak voltak. Indítsa el a gyártást is. Az érthetőség kedvéért itt van egy kép az egyik adatbázis méretével a futtatás után:

Postgres: bloat, pg_repack és késleltetett megszorítások

Mivel ezzel a megoldással maximálisan meg voltunk elégedve, a másodikat nem próbáltuk megvalósítani, de mérlegeljük annak lehetőségét, hogy megbeszéljük a bővítmény fejlesztőivel. Jelenlegi revíziónk sajnos még nem áll készen a publikálásra, mivel csak egyedi halasztott korlátozásokkal oldottuk meg a problémát, a teljes értékű patch-hez pedig más típusok támogatása szükséges. Reméljük, hogy ezt a jövőben is megtehetjük.

Talán kérdésed van, hogy miért keveredtünk bele ebbe a történetbe a pg_repack módosításával, és miért nem használtuk például analógjait? Valamikor mi is gondolkodtunk ezen, de a korábbi, halasztott megkötések nélküli asztalokon való használat pozitív tapasztalatai arra ösztönöztek bennünket, hogy megpróbáljuk megérteni a probléma lényegét és orvosolni. Ezen túlmenően más megoldások használata is időt igényel a tesztek elvégzéséhez, ezért úgy döntöttünk, hogy először ebben próbáljuk meg kijavítani a problémát, és ha rájövünk, hogy ezt nem tudjuk ésszerű időn belül megtenni, akkor elkezdjük az analógokat keresni. .

Álláspontja

Amit saját tapasztalataink alapján ajánlhatunk:

  1. Figyelje a puffadást. A megfigyelési adatok alapján megértheti, hogy milyen jól van beállítva az autovákuum.
  2. Állítsa be az AUTOVACUUM funkciót, hogy a puffadás elfogadható szinten maradjon.
  3. Ha a duzzanat még mindig növekszik, és nem tudja leküzdeni a kész eszközökkel, ne féljen külső bővítményektől. A lényeg az, hogy mindent jól tesztelj.
  4. Ne féljen a külső megoldásokat igényeinek megfelelően módosítani – ez néha hatékonyabb és még egyszerűbb is lehet, mint a saját kód megváltoztatása.

Forrás: will.com

Hozzászólás