Postgres: bloat, pg_repack eta atzeratutako mugak

Postgres: bloat, pg_repack eta atzeratutako mugak

Puzketak tauletan eta indizeetan duen eragina oso ezaguna da eta Postgres-en ez ezik. Kutxatik kanpo aurre egiteko moduak daude, VACUUM FULL edo CLUSTER bezalakoak, baina funtzionatzen ari diren bitartean mahaiak blokeatzen dituzte eta, beraz, ezin dira beti erabili.

Artikuluak bloat nola gertatzen den, nola borrokatu dezakezun, atzeratutako murrizketei eta pg_repack luzapenaren erabilerari dakarten arazoei buruzko teoria txiki bat izango du.

Artikulu hau oinarrian idatzita dago nire hizkera PgConf.Errusia 2020n.

Zergatik gertatzen da hantura?

Postgres bertsio anitzeko eredu batean oinarritzen da (MVCC). Bere funtsa da taulako errenkada bakoitzak hainbat bertsio izan ditzakeela, transakzioek bertsio horietako bat baino gehiago ikusten ez duten bitartean, baina ez du zertan bera. Horri esker, hainbat transakzio aldi berean funtzionatzen dute eta ia ez dute eraginik bata bestearengan.

Jakina, bertsio horiek guztiak gorde behar dira. Postgres-ek memoriarekin funtzionatzen du orrialdez orrialde eta orri bat diskotik irakurri edo idatzi daitekeen datu kopuru minimoa da. Ikus dezagun adibide txiki bat nola gertatzen den ulertzeko.

Demagun taula bat dugula eta horri hainbat erregistro gehitu dizkiogun. Datu berriak agertu dira taula gordetzen den fitxategiaren lehen orrian. Konpromiso baten ondoren beste transakzio batzuetarako erabilgarri dauden errenkaden zuzeneko bertsioak dira (sinpletasunerako, isolamendu maila Read Committed dela suposatuko dugu).

Postgres: bloat, pg_repack eta atzeratutako mugak

Ondoren, sarreretako bat eguneratu dugu, eta, horrela, bertsio zaharra jada garrantzitsua ez dela markatu dugu.

Postgres: bloat, pg_repack eta atzeratutako mugak

Pausoz pauso, errenkada bertsioak eguneratuz eta ezabatuz, datuen erdia gutxi gorabehera "zaborra" den orrialde batekin amaitu genuen. Datu hauek ez dira ikusgai edozein transakziok.

Postgres: bloat, pg_repack eta atzeratutako mugak

Postgres-ek mekanismo bat du hutsean, zaharkitutako bertsioak garbitu eta datu berriei lekua egiten diena. Baina nahikoa oldarkor konfiguratuta ez badago edo beste tauletan lanean lanpetuta badago, "zabor datuak" geratzen dira, eta orri gehigarriak erabili behar ditugu datu berrietarako.

Beraz, gure adibidean, uneren batean taulak lau orrialde izango ditu, baina erdiak bakarrik izango ditu zuzeneko datuak. Ondorioz, taulara sartzean, behar baino askoz datu gehiago irakurriko ditugu.

Postgres: bloat, pg_repack eta atzeratutako mugak

VACUUMek orain garrantzirik gabeko errenkada-bertsio guztiak ezabatzen baditu ere, egoera ez da nabarmen hobetuko. Leku librea izango dugu orrialdeetan edo baita orrialde osoetan ere errenkada berrietarako, baina hala ere behar baino datu gehiago irakurriko ditugu.
Bide batez, orri guztiz huts bat (gure adibideko bigarrena) fitxategiaren amaieran egongo balitz, VACUUMek moztu ahal izango luke. Baina orain erdian dago, beraz, ezin da berarekin ezer egin.

Postgres: bloat, pg_repack eta atzeratutako mugak

Orrialde huts edo oso urri horien kopurua handitzen denean, hau da, puztuta dagoenean, errendimenduan eragiten hasten da.

Goian deskribatutako guztia tauletan puzketak agertzearen mekanika da. Indizeetan hori modu berean gertatzen da.

Puztuta daukat?

Hainbat modu daude puztuta dagoen ala ez zehazteko. Lehenengoaren ideia Postgresen barneko estatistikak erabiltzea da, zeinak tauletako errenkada kopuruari, "zuzeneko" errenkaden kopuruari eta abarri buruzko gutxi gorabeherako informazioa biltzen duena. Interneten prest dauden scripten aldaera asko aurki ditzakezu. Oinarritzat hartu genuen gidoia PostgreSQL Adituen eskutik, bloat taulak ebaluatu ditzaketen toast eta bloat btree indizeekin batera. Gure esperientziaren arabera, bere errorea % 10-20 da.

Beste modu bat luzapena erabiltzea da pgstattuple, orrialdeen barrura begiratu eta bloat balio estimatua eta zehatza lortzeko aukera ematen duena. Baina bigarren kasuan, taula osoa eskaneatu beharko duzu.

Puztu-balio txiki bat, %20raino, onargarritzat jotzen dugu. Bete-faktorearen analogotzat har daiteke mahaiak и indizeak. % 50ean eta gehiagotan, errendimendu-arazoak ager daitezke.

Puzketak aurre egiteko moduak

Postgres-ek hainbat modu ditu puzketei aurre egiteko kutxatik kanpo, baina ez dira beti denentzat egokiak.

Konfiguratu AUTOVACUUM puzketak gerta ez daitezen. Edo zehatzago esanda, zuretzat onargarria den mailan mantentzea. "Kapitainaren" aholkua dirudi, baina errealitatean hori ez da beti erraza izaten. Esate baterako, garapen aktiboa duzu datu-eskeman aldaketa erregularrekin, edo datu-migrazio motaren bat gertatzen ari da. Ondorioz, zure karga-profila maiz alda daiteke eta normalean taula batetik bestera aldatuko da. Horrek esan nahi du etengabe lan egin behar duzula pixka bat aurreratu eta AUTOVACUUM mahai bakoitzaren profil aldakorrera egokitu behar duzula. Baina, jakina, hori ez da erraza egiten.

AUTOVACUUMek taulekin jarraitu ezin duen beste arrazoi arrunt bat da transakzio horietarako erabilgarri dauden datuak garbitzea eragozten duten transakzio luzeak daudelako. Hemen gomendioa ere begi-bistakoa da: ken ezazu "zintzilik" transakzioak eta murriztu transakzio aktiboen denbora. Baina zure aplikazioaren karga OLAP eta OLTP hibridoa bada, orduan maiz eguneratze eta kontsulta labur asko izan ditzakezu aldi berean, baita epe luzerako eragiketak ere, adibidez, txosten bat eraikitzea. Egoera horretan, pentsatzea komeni da karga oinarri ezberdinetan zabaltzea, eta horrek horietako bakoitza hobeto doitzeko aukera emango du.

Beste adibide bat - profila homogeneoa bada ere, baina datu-basea oso karga handian dagoen arren, baliteke AUTOVACUUM oldarkorrenak ere ez egitea eta puzketak gertatuko dira. Eskalatzea (bertikala edo horizontala) da irtenbide bakarra.

Zer egin AUTOVACUUM ezarri duzun egoera batean, baina puzketak hazten jarraitzen du.

Team HUTSEAN BETEA taulen eta indizeen edukia berreraikitzen du eta datu garrantzitsuak soilik uzten ditu horietan. Puzketak ezabatzeko, primeran funtzionatzen du, baina exekutatzen ari den bitartean mahai gainean blokeo esklusibo bat atzematen da (AccessExclusiveLock), eta horrek ez du baimenduko mahai honetan kontsultak, baita hautaketak ere. Zure zerbitzua edo zati bat denbora pixka batean gelditzea ahalbidetzen baduzu (hamarka minututik hainbat ordutara datu-basearen eta zure hardwarearen tamainaren arabera), aukera hau da onena. Zoritxarrez, ez dugu astirik VACUUM FULL exekutatzeko programatutako mantentze-lanetan, beraz, metodo hau ez da egokia guretzat.

Team KLUSTERRA VACUUM FULL bezalako taulen edukia berreraikitzen du, baina datuak fisikoki diskoan ordenatuko diren indize bat zehazteko aukera ematen du (baina etorkizunean ez dago errenkada berrietarako ordena bermatuta). Zenbait egoeratan, optimizazio ona da hainbat kontsultatarako - indizearen araberako erregistro anitz irakurtzean. Komandoaren desabantaila VACUUM FULL-ren berdina da: mahaia blokeatzen du funtzionatzen ari den bitartean.

Team BERRINDEXEA aurreko bien antzekoa, baina indize zehatz bat edo taularen indize guztiak berreraikitzen ditu. Blokeoak zertxobait ahulagoak dira: ShareLock mahai gainean (aldaketak eragozten ditu, baina aukeratzen uzten du) eta AccessExclusiveLock berreraikitzen ari den indizean (indize hau erabiliz kontsultak blokeatzen ditu). Hala ere, Postgres-en 12. bertsioan parametro bat agertu zen aldi berean, indizea berreraikitzeko aukera ematen duena erregistroak aldi berean gehitzea, aldatzea edo ezabatzea blokeatu gabe.

Postgres-en aurreko bertsioetan, REINDEX CONCURRENTLY erabiliz emaitza antzeko bat lor dezakezu SORTU AURKIBIDEA aldi berean. Blokeo zorrotzik gabe indize bat sortzeko aukera ematen du (ShareUpdateExclusiveLock, kontsulta paraleloak oztopatzen ez dituena), ondoren indize zaharra berri batekin ordezkatu eta indize zaharra ezabatu. Horri esker, indizearen bloat ezaba dezakezu zure aplikazioa oztopatu gabe. Garrantzitsua da kontuan izan indizeak berreraikitzean karga gehigarri bat egongo dela diskoaren azpisisteman.

Horrela, indizeetarako puzketak "heganean" kentzeko modurik badago, orduan ez dago tauletarako. Hor sartzen dira kanpoko hainbat luzapen: pg_repack (lehen pg_reorg), pgcompact, pgcompactable eta besteak. Artikulu honetan, ez ditut alderatuko eta pg_repack-i buruz bakarrik hitz egingo dut, zeina, aldaketaren bat egin ondoren, guk geuk erabiltzen dugun.

pg_repack-ek nola funtzionatzen duen

Postgres: bloat, pg_repack eta atzeratutako mugak
Demagun taula guztiz arrunta dugula, indizeekin, murrizketekin eta, zoritxarrez, puzturekin. pg_repack-en lehen urratsa erregistro-taula bat sortzea da exekutatzen ari den bitartean aldaketa guztiei buruzko datuak gordetzeko. Abiarazleak aldaketa hauek errepikatuko ditu txertatze, eguneratze eta ezabatze bakoitzean. Ondoren, taula bat sortzen da, egituraz jatorrizkoaren antzekoa, baina indize eta murrizketarik gabe, datuak txertatzeko prozesua ez moteltzeko.

Ondoren, pg_repack-ek datuak taula zaharretik taula berrira transferitzen ditu, garrantzirik gabeko errenkada guztiak automatikoki iragazten ditu eta, ondoren, taula berrirako indizeak sortzen ditu. Eragiketa horiek guztiak exekutatzen diren bitartean, aldaketak erregistro-taulan pilatzen dira.

Hurrengo urratsa aldaketak taula berrira transferitzea da. Migrazioa hainbat iteraziotan egiten da, eta erregistro-taulan 20 sarrera baino gutxiago geratzen direnean, pg_repack-ek blokeo sendoa eskuratzen du, azken datuak migratzen ditu eta taula zaharra Postgres sistemako tauletako berriarekin ordezkatzen du. Hau da mahaiarekin lan egin ezin izango duzun denbora bakarra eta oso laburra. Honen ondoren, taula zaharra eta erregistroak dituen taula ezabatzen dira eta tokia askatzen da fitxategi-sisteman. Prozesua amaitu da.

Teorian dena itxura bikaina da, baina zer gertatzen da praktikan? pg_repack kargarik gabe eta kargapean probatu genuen, eta bere funtzionamendua egiaztatu genuen goiztiarra gelditzen bazen (hots, Ctrl+C erabiliz). Proba guztiak positiboak izan ziren.

Janari dendara joan ginen, eta gero dena ez zen espero genuen bezala atera.

Lehenengo krepea salgai

Lehenengo klusterean muga bakarra urratzeari buruzko errore bat jaso genuen:

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

Muga honek index_16508 automatikoki sortutako izena zuen - pg_repack-ek sortu zuen. Bere osaeran sartutako atributuetan oinarrituta, hari dagokion "gure" muga zehaztu dugu. Arazoa izan zen hau ez dela muga guztiz arrunta, atzeratua baizik (murrizketa geroratua), hau da. bere egiaztapena sql komandoa baino beranduago egiten da, eta horrek ustekabeko ondorioak dakartza.

Muga atzeratuak: zergatik behar diren eta nola funtzionatzen duten

Murrizketa geroratuei buruzko teoria txiki bat.
Har dezagun adibide sinple bat: autoen taula-erreferentzia-liburu bat dugu bi atributurekin - direktorioko autoaren izena eta ordena.
Postgres: bloat, pg_repack eta atzeratutako mugak

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



Demagun lehenengo eta bigarren autoak aldatu behar genituela. Irtenbide zuzena lehenengo balioa bigarrenera eguneratzea da, eta bigarrena lehenengora:

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

Baina kode hau exekutatzen dugunean, muga-urraketa espero dugu, taulako balioen ordena bakarra delako:

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

Nola egin dezaket ezberdin? Lehenengo aukera: gehitu balio-ordezkapen gehigarri bat taulan ez egotea bermatuta dagoen eskaera bati, adibidez, "-1". Programazioan, "bi aldagairen balioak hirugarren baten bidez trukatzea" deitzen zaio. Metodo honen eragozpen bakarra eguneraketa gehigarria da.

Bigarren aukera: birdiseina ezazu taula ordena-baliorako koma mugikorreko datu-mota erabiltzeko zenbaki osoen ordez. Ondoren, 1etik 2.5era, adibidez, balioa eguneratzean, lehenengo sarrera automatikoki bigarrenaren eta hirugarrenaren artean "egokituko da". Irtenbide honek funtzionatzen du, baina bi muga daude. Lehenik eta behin, ez du balio izango interfazean nonbait erabiltzen bada. Bigarrenik, datu-motaren zehaztasunaren arabera, txertatze posible kopuru mugatua izango duzu erregistro guztien balioak berriro kalkulatu aurretik.

Hiru aukera: muga atzeratu egin ezazu konpromezuaren unean soilik egiaztatzea:

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

Gure hasierako eskaeraren logikak konpromisoaren unean balio guztiak bakarrak direla ziurtatzen duenez, arrakasta izango du.

Goian aipatu dugun adibidea, noski, oso sintetikoa da, baina ideia agerian uzten du. Gure aplikazioan, mugak atzeratuak erabiltzen ditugu, erabiltzaileek arbelean partekatutako widget-objektuekin lan egiten dutenean, gatazkak konpontzeko ardura duen logika ezartzeko. Horrelako murrizketak erabiltzeari esker, aplikazioaren kodea apur bat errazagoa da.

Oro har, murrizketa motaren arabera, Postgres-ek hiru granularitate-maila ditu horiek egiaztatzeko: errenkada, transakzio eta adierazpen mailak.
Postgres: bloat, pg_repack eta atzeratutako mugak
Iturria: eskeak

CHECK eta NOT NULL errenkada mailan egiaztatzen dira beti; beste murrizketetarako, taulan ikus daitekeenez, aukera desberdinak daude. Gehiago irakur dezakezu Hemen.

Laburbilduz, hainbat egoeratan atzeratutako mugak kode irakurgarriagoa eta komando gutxiago eskaintzen dituzte. Hala ere, hori ordaindu behar duzu arazketa-prozesua zailduz, errorea gertatzen den momentua eta horren berri ematen duzun momentua denboran bereizten baitira. Beste arazo posible bat da planifikatzaileak ez duela beti plan optimo bat eraikitzeko gai, eskaerak muga atzeratua badakar.

pg_repack-en hobekuntza

Murrizketa atzeratuak zer diren azaldu dugu, baina nola erlazionatzen dira gure arazoarekin? Gogora dezagun lehenago jaso genuen errorea:

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

Datuak erregistro-taula batetik taula berri batera kopiatzen direnean gertatzen da. Horrek arraroa dirudi, zeren... erregistro-taularen datuak iturburu-taularen datuekin batera konprometitzen dira. Jatorrizko taularen mugak betetzen badituzte, nola urratu ditzakete muga berberak berrian?

Ikusten denez, arazoaren erroa pg_repack-en aurreko urratsean dago, indizeak soilik sortzen dituena, baina ez mugak: taula zaharrak muga bakarra zuen, eta berriak indize bakarra sortu zuen horren ordez.

Postgres: bloat, pg_repack eta atzeratutako mugak

Garrantzitsua da hemen murrizketa normala bada eta geroratua ez bada, orduan sortutako indize bakarra muga horren baliokidea dela, izan ere. Postgres-en muga bakarrak indize bakarra sortuz ezartzen dira. Baina atzeratutako murrizketa baten kasuan, portaera ez da berdina, indizea ezin baita atzeratu eta beti egiaztatzen baita sql komandoa exekutatzen den unean.

Beraz, arazoaren funtsa egiaztapenaren “atzerapenean” datza: jatorrizko taulan konprometitzeko unean gertatzen da, eta taula berrian sql komandoa exekutatzen den unean. Horrek esan nahi du bi kasuetan egiaztapenak berdin egiten direla ziurtatu behar dugula: beti atzeratuta, edo beti berehala.

Orduan, zer ideia geneukan?

Sortu deferred-en antzeko indize bat

Lehenengo ideia bi egiaztapenak berehalako moduan egitea da. Horrek hainbat murrizketa positibo faltsu sor ditzake, baina horietako gutxi baldin badira, horrek ez luke erabiltzaileen lanari eragin behar, gatazka horiek egoera normala baitira haientzat. Gertatzen dira, adibidez, bi erabiltzaile widget bera editatzen hasten direnean aldi berean, eta bigarren erabiltzailearen bezeroak denborarik ez duen informazioa jasotzeko widgeta dagoeneko blokeatuta dagoela lehen erabiltzaileak editatzeko. Egoera horretan, zerbitzariak bigarren erabiltzaileari uko egiten dio eta bere bezeroak aldaketak atzera egiten ditu eta widgeta blokeatzen du. Pixka bat geroago, lehen erabiltzaileak editatzen amaitzen duenean, bigarrenak widget-a jada blokeatuta ez dagoela dioen informazioa jasoko du eta bere ekintza errepikatu ahal izango du.

Postgres: bloat, pg_repack eta atzeratutako mugak

Egiaztapenak beti atzeratu gabeko moduan daudela ziurtatzeko, jatorrizko atzeratutako mugaren antzeko indize berri bat sortu dugu:

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

Proba-ingurunean, espero ziren akats batzuk baino ez ditugu jaso. Arrakasta! pg_repack berriro exekutatu dugu ekoizpenean eta 5 errore lortu ditugu lehen klusterrean lanean ordubetean. Hau emaitza onargarria da. Hala ere, jada bigarren klusterean errore kopurua nabarmen handitu zen eta pg_repack gelditu behar izan genuen.

Zergatik gertatu zen? Errore bat gertatzeko probabilitatea aldi berean widget berdinekin lan egiten duten zenbat erabiltzaileen araberakoa da. Dirudienez, momentu horretan askoz lehiakortasun-aldaketa gutxiago egon ziren lehen klusterrean gordetako datuekin besteetan baino, hau da. “zortea” besterik ez genuen izan.

Ideiak ez zuen funtzionatu. Une horretan, beste bi irtenbide ikusi genituen: gure aplikazio-kodea berridatzi atzeratutako murriztapenak alde batera uzteko, edo "irakatsi" pg_repack haiekin lan egiten. Bigarrena aukeratu dugu.

Ordeztu taula berriko indizeak jatorrizko taulatik atzeratutako murriztapenekin

Berrikuspenaren helburua begi-bistakoa zen: jatorrizko taulak muga atzeratu bat badu, orduan berrirako muga hori sortu behar duzu, eta ez indize bat.

Gure aldaketak probatzeko, proba sinple bat idatzi dugu:

  • Muga atzeratu batekin eta erregistro batekin taula;
  • txertatu datuak lehendik dagoen erregistro batekin gatazkan dauden begizta batean;
  • eguneraketa bat egin - datuak jada ez dira gatazkan;
  • aldaketak konprometitu.

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;

pg_repack-en jatorrizko bertsioak lehen txertatzean huts egiten zuen beti, aldatutako bertsioak akatsik gabe funtzionatu zuen. Bikaina.

Produkziora joaten gara eta berriro errore bat jasoko dugu erregistro-taulatik datuak kopiatzeko fase berean:

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

Egoera klasikoa: dena proba-inguruneetan funtzionatzen du, baina ez ekoizpenean?!

APPLY_COUNT eta bi loteren elkargunea

Kodea literalki lerroz lerro aztertzen hasi ginen eta puntu garrantzitsu bat aurkitu genuen: datuak erregistro-taulatik berri batera transferitzen dira loteka, APPLY_COUNT konstanteak lotearen tamaina adierazten zuen:

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

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

Arazoa da jatorrizko transakzioko datuak, zeinetan hainbat eragik potentzialki murrizketa urratu dezaketen, transferitzen direnean, bi loteren elkargunean buka dezaketela - komandoen erdia lehen lotean konprometituko da, eta beste erdia. bigarrenean. Eta hemen, zure zortearen arabera: taldeek ez badute ezer urratzen lehen multzoan, dena ondo dago, baina hala egiten badute, akats bat gertatzen da.

APPLY_COUNT 1000 erregistroren berdina da, eta horrek azaltzen du zergatik izan diren gure probak arrakastatsuak: ez dute "batch junction" kasua estaltzen. Bi komando erabili genituen: txertatu eta eguneratu, beraz, zehazki, bi komandoetako 500 transakzio beti lote batean jartzen ziren eta ez genuen arazorik izan. Bigarren eguneratzea gehitu ondoren, gure edizioak funtzionatzeari utzi zion:

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;

Beraz, hurrengo zeregina transakzio batean aldatu zen jatorrizko taulako datuak transakzio batean ere taula berrian amaitzen direla ziurtatzea da.

Loteari uko egitea

Eta berriro bi irtenbide izan genituen. Lehenik eta behin: alde batera utz ditzagun loteetan zatitzea eta transferi ditzagun datuak transakzio bakarrean. Irtenbide honen abantaila sinpletasuna zen: beharrezko kode aldaketak gutxienekoak ziren (bide batez, bertsio zaharretan pg_reorg-ek horrela funtzionatzen zuen). Baina arazo bat dago: iraupen luzeko transakzio bat sortzen ari gara, eta hau, lehen esan bezala, mehatxu bat da puzkera berri bat agertzeko.

Bigarren irtenbidea konplexuagoa da, baina ziurrenik zuzenagoa: sortu zutabe bat erregistro-taulan, taulari datuak gehitu dizkion transakzioaren identifikatzailearekin. Ondoren, datuak kopiatzen ditugunean, atributu honen arabera taldeka ditzakegu eta erlazionatutako aldaketak batera transferitzen direla ziurtatu. Sortea hainbat transakziorekin (edo handi batetik) osatuko da eta bere tamaina aldatu egingo da transakzio horietan zenbat datu aldatu diren arabera. Garrantzitsua da kontutan izan transakzio ezberdinetako datuak erregistro-taulan ausazko ordenan sartzen direnez, ezin izango dela sekuentzialki irakurri, lehen bezala. tx_id-en iragazkia duen eskaera bakoitzeko seqscan garestiegia da, indize bat behar da, baina metodoa motelduko du eguneratzeak duen gastuagatik. Orokorrean, beti bezala, zerbait sakrifikatu behar duzu.

Beraz, lehenengo aukeratik hastea erabaki dugu, sinpleagoa baita. Lehenik eta behin, transakzio luze bat benetako arazoa izango zen ala ez ulertu behar zen. Taula zaharretik berrira datuen transferentzia nagusia transakzio luze batean ere gertatzen denez, galdera "zenbat handituko dugu transakzio hau?" Lehenengo transakzioaren iraupena taularen tamainaren araberakoa da nagusiki. Berri baten iraupena datuak transferitzean taulan zenbat aldaketa pilatzen diren araberakoa da, hau da. kargaren intentsitatean. pg_repack exekuzioa zerbitzu-karga gutxieneko garaian gertatu zen, eta aldaketen bolumena neurrigabea izan zen taularen jatorrizko tamainarekin alderatuta. Erabaki genuen transakzio berri baten denbora alde batera utzi genezakeela (konparazio baterako, batez beste ordu 1 eta 2-3 minutu da).

Esperimentuak positiboak izan ziren. Ekoizpenean ere abiarazi. Argitasuna lortzeko, hona hemen exekutatu ondoren datu-baseetako baten tamaina duen irudi bat:

Postgres: bloat, pg_repack eta atzeratutako mugak

Irtenbide honekin guztiz pozik geundenez, ez ginen bigarrena inplementatzen saiatu, baina luzapenen garatzaileekin eztabaidatzeko aukera aztertzen ari gara. Gure egungo berrikuspena, zoritxarrez, oraindik ez dago argitaratzeko prest, arazoa atzeratutako murrizketa bereziekin bakarrik konpondu baikenuen, eta erabateko adabaki baterako beharrezkoa da beste mota batzuetarako laguntza ematea. Etorkizunean hori egin ahal izatea espero dugu.

Agian galdera bat duzu, zergatik sartu ginen istorio honetan pg_repack-en aldaketarekin, eta ez genituen, adibidez, bere analogoak erabili? Noizbait hau ere pentsatu genuen, baina lehenago erabiltzearen esperientzia positiboak, muga atzeratu gabeko mahaietan, arazoaren funtsa ulertzen eta konpontzen saiatzera bultzatu gintuen. Horrez gain, beste irtenbide batzuk erabiltzeak probak egiteko denbora ere eskatzen du, beraz, lehenik bertan arazoa konpontzen saiatuko ginela erabaki genuen, eta konturatu bagara ezin genuela hori arrazoizko denbora batean egin, orduan analogoak aztertzen hasiko ginateke. .

Findings

Gure esperientzian oinarrituta gomenda dezakeguna:

  1. Kontrolatu zure bloat. Jarraipen datuetan oinarrituta, hutsune automatikoa zein ondo konfiguratuta dagoen uler dezakezu.
  2. Egokitu AUTOVACUUM puzketak maila onargarrian mantentzeko.
  3. Puztua oraindik hazten ari bada eta ezin baduzu kutxaz kanpoko tresnak erabiliz gainditu, ez izan beldurrik kanpoko luzapenak erabiltzeko. Gauza nagusia dena ondo probatzea da.
  4. Ez izan beldurrik kanpoko irtenbideak aldatzeko zure beharretara egokitzeko - batzuetan eraginkorragoa eta are errazagoa izan daiteke zure kodea aldatzea baino.

Iturria: www.habr.com

Gehitu iruzkin berria