Postgres: bloat, pg_repack en útstelde beheiningen

Postgres: bloat, pg_repack en útstelde beheiningen

It effekt fan bloat op tabellen en yndeksen is wiid bekend en is net allinich oanwêzich yn Postgres. D'r binne manieren om it út 'e doaze te behanneljen, lykas VACUUM FULL of CLUSTER, mar se beskoattelje tafels by operaasje en kinne dêrom net altyd brûkt wurde.

It artikel sil in lytse teory befetsje oer hoe't bloat foarkomt, hoe't jo it kinne bestride, oer útstelde beheiningen en de problemen dy't se bringe foar it gebrûk fan 'e pg_repack-útwreiding.

Dit artikel is skreaun basearre op myn spraak op PgConf.Russia 2020.

Wêrom komt bloat foar?

Postgres is basearre op in multi-ferzje model (MVCC). Syn essinsje is dat elke rige yn 'e tabel kin hawwe ferskate ferzjes, wylst transaksjes sjogge net mear as ien fan dizze ferzjes, mar net needsaaklikerwize deselde. Hjirmei kinne ferskate transaksjes tagelyk wurkje en hawwe praktysk gjin ynfloed op elkoar.

Fansels moatte al dizze ferzjes wurde opslein. Postgres wurket mei ûnthâld side foar side en in side is de minimale hoemannichte gegevens dat kin wurde lêzen fan skiif of skreaun. Litte wy nei in lyts foarbyld sjen om te begripen hoe't dit bart.

Litte wy sizze dat wy in tabel hawwe dêr't wy ferskate records oan tafoege hawwe. Nije gegevens binne ferskynd op 'e earste side fan it bestân wêr't de tabel wurdt opslein. Dit binne live ferzjes fan rigen dy't beskikber binne foar oare transaksjes nei in commit (foar ienfâld, wy sille oannimme dat it isolemint nivo is Read Committed).

Postgres: bloat, pg_repack en útstelde beheiningen

Wy hawwe doe ien fan de ynstjoerings bywurke, en markearje dêrmei de âlde ferzje as net mear relevant.

Postgres: bloat, pg_repack en útstelde beheiningen

Stap foar stap, bywurkjen en wiskjen rige ferzjes, wy einige mei in side wêryn likernôch de helte fan de gegevens is "garbage". Dizze gegevens binne net sichtber foar elke transaksje.

Postgres: bloat, pg_repack en útstelde beheiningen

Postgres hat in meganisme VACUUM, dy't ferâldere ferzjes skjinmakket en romte makket foar nije gegevens. Mar as it net agressyf genôch ynsteld is of dwaande is mei it wurkjen yn oare tabellen, dan bliuwt "garbage data" oer, en wy moatte ekstra siden brûke foar nije gegevens.

Dus yn ús foarbyld sil de tabel op in stuit yn 'e tiid bestean út fjouwer siden, mar allinich de helte dêrfan sil live gegevens befetsje. As gefolch, by tagong ta de tabel, sille wy folle mear gegevens lêze dan nedich.

Postgres: bloat, pg_repack en útstelde beheiningen

Sels as VACUUM no alle irrelevante rige ferzjes wisket, sil de situaasje net dramatysk ferbetterje. Wy sille frije romte hawwe yn siden of sels hiele siden foar nije rigen, mar wy sille noch hieltyd mear gegevens lêze dan nedich.
Trouwens, as in folslein lege side (de twadde yn ús foarbyld) oan 'e ein fan' e triem wie, dan soe VACUUM it kinne trimme. Mar no sit se yn 'e midden, dat der kin neat mei har dien wurde.

Postgres: bloat, pg_repack en útstelde beheiningen

As it oantal fan sokke lege of tige sparse siden grut wurdt, wat bloat neamd wurdt, begjint it de prestaasjes te beynfloedzjen.

Alles beskreaun hjirboppe is de meganika fan it foarkommen fan bloat yn tabellen. Yn yndeksen bart dit op in protte deselde wize.

Haw ik in blaas?

D'r binne ferskate manieren om te bepalen as jo bloat hawwe. It idee fan 'e earste is om ynterne Postgres-statistiken te brûken, dy't sawat ynformaasje befettet oer it oantal rigen yn tabellen, it oantal "live" rigen, ensfh. Jo kinne in protte farianten fan klearmakke skripts fine op it ynternet. Wy namen as basis skrift út PostgreSQL Experts, dat kin evaluearje bloat tabellen tegearre mei toast en bloat btree yndeksen. Yn ús ûnderfining is har flater 10-20%.

In oare manier is om de útwreiding te brûken pgstattuple, wêrtroch jo binnen de siden kinne sjen en sawol in skatte as in krekte bloatwearde krije. Mar yn it twadde gefal moatte jo de hiele tabel scannen.

Wy beskôgje in lytse bloat wearde, oant 20%, akseptabel. It kin beskôge wurde as in analoog fan fillfactor foar tabellen и yndeksen. By 50% en boppe kinne prestaasjesproblemen begjinne.

Manieren om bloat te bestriden

Postgres hat ferskate manieren om te gean mei bloat út 'e doaze, mar se binne net altyd geskikt foar elkenien.

Konfigurearje AUTOVACUUM sadat bloat net foarkomt. Of krekter, om it op in foar jo akseptabel nivo te hâlden. Dit liket as advys fan "kaptein", mar yn werklikheid is dit net altyd maklik te berikken. Jo hawwe bygelyks aktive ûntwikkeling mei reguliere feroarings oan it gegevensskema, of in soarte fan gegevensmigraasje fynt plak. As gefolch kin jo loadprofyl faak feroarje en sil typysk ferskille fan tabel ta tabel. Dit betsjut dat jo konstant in bytsje foarút moatte wurkje en AUTOVACUUM oanpasse oan it feroarjende profyl fan elke tafel. Mar fansels is dit net maklik te dwaan.

In oare mienskiplike reden wêrom't AUTOVACUUM net mei tabellen byhâlde kin, is om't d'r langrinnende transaksjes binne dy't foarkomme dat it de gegevens opromje dy't beskikber binne foar dy transaksjes. De oanbefelling hjir is ek fanselssprekkend - kwyt "dangling" transaksjes en minimalisearje de tiid fan aktive transaksjes. Mar as de lading op jo applikaasje in hybride is fan OLAP en OLTP, dan kinne jo tagelyk in protte faak updates en koarte fragen hawwe, lykas lange-termyn operaasjes - bygelyks it bouwen fan in rapport. Yn sa'n situaasje is it wurdich om te tinken oer it fersprieden fan 'e lading oer ferskate basisen, wêrtroch't mear fine-tuning fan elk fan har mooglik is.

In oar foarbyld - sels as it profyl homogeen is, mar de databank is ûnder in heul hege lading, dan kin sels de meast agressive AUTOVACUUM net omgean, en blaas sil foarkomme. Skaalfergrutting (fertikaal of horizontaal) is de ienige oplossing.

Wat te dwaan yn in situaasje dêr't jo hawwe ynsteld AUTOVACUUM, mar de bloat bliuwt te groeien.

team VAKUUM FULL werbout de ynhâld fan tabellen en yndeksen en lit allinnich relevante gegevens yn harren. Om elimineren bloat, it wurket perfekt, mar yn syn útfiering in eksklusyf slot op 'e tafel wurdt finzen nommen (AccessExclusiveLock), dat sil net tastean it útfieren fan queries op dizze tafel, sels selektearret. As jo ​​​​kinne betelje om jo tsjinst of in diel dêrfan foar in skoft te stopjen (fan tsientallen minuten oant ferskate oeren ôfhinklik fan de grutte fan 'e databank en jo hardware), dan is dizze opsje de bêste. Spitigernôch hawwe wy gjin tiid om VACUUM FULL te rinnen tidens it plande ûnderhâld, dus dizze metoade is net geskikt foar ús.

team KLUSTER Herbout de ynhâld fan tabellen op deselde wize as VACUUM FULL, mar kinne jo opjaan in yndeks neffens dêr't de gegevens sille wurde fysyk oardere op skiif (mar yn 'e takomst de oarder is net garandearre foar nije rigen). Yn bepaalde situaasjes is dit in goede optimalisaasje foar in oantal fragen - mei it lêzen fan meardere records per yndeks. It neidiel fan it kommando is itselde as dat fan VACUUM FULL - it slút de tafel by operaasje.

team REINDEX fergelykber mei de foarige twa, mar werbout in spesifike yndeks of alle yndeksen fan 'e tabel. Sloten binne wat swakker: ShareLock op 'e tafel (foarkomt oanpassings, mar kinne selektearje) en AccessExclusiveLock op' e yndeks wurdt ferboud (blokkearret queries mei help fan dizze yndeks). Yn 'e 12e ferzje fan Postgres ferskynde lykwols in parameter TIJDIGEND, wêrmei jo de yndeks opnij opbouwe kinne sûnder tagelyk tafoegjen, wizigjen of wiskjen fan records te blokkearjen.

Yn eardere ferzjes fan Postgres kinne jo in resultaat berikke fergelykber mei REINDEX CONCURRENTLY brûkend CREATE INDEX TIJDLIGEN. It lit jo in yndeks meitsje sûnder strikte beskoatteljen (ShareUpdateExclusiveLock, dy't net bemuoit mei parallelle queries), ferfange dan de âlde yndeks troch in nije en wiskje de âlde yndeks. Hjirmei kinne jo yndeksbloat eliminearje sûnder jo applikaasje te bemuoien. It is wichtich om te beskôgje dat by it werbouwen fan yndeksen d'r in ekstra lading sil wêze op it skiifsubsysteem.

Dus, as d'r manieren binne foar yndeksen om bloat "op 'e flecht" te eliminearjen, dan binne d'r gjin foar tabellen. Dit is wêr't ferskate eksterne útwreidingen yn spiel komme: pg_repack (earder pg_reorg), pgcompact, pgcompacttable en oaren. Yn dit artikel sil ik se net fergelykje en sil allinich prate oer pg_repack, dy't, nei wat modifikaasje, wy sels brûke.

Hoe pg_repack wurket

Postgres: bloat, pg_repack en útstelde beheiningen
Litte wy sizze dat wy in folslein gewoane tafel hawwe - mei yndeksen, beheiningen en, spitigernôch, mei bloat. De earste stap fan pg_repack is om in logtabel te meitsjen om gegevens oer alle wizigingen op te slaan wylst it rint. De trigger sil dizze wizigingen replikearje foar elke ynfoegje, bywurkje en wiskje. Dan wurdt in tabel makke, fergelykber mei de oarspronklike yn struktuer, mar sûnder yndeksen en beheiningen, om it proses fan it ynfoegjen fan gegevens net te fertragen.

Folgjende, pg_repack ferpleatst de gegevens fan de âlde tabel nei de nije tabel, automatysk filterjen út alle irrelevante rigen, en dan makket yndeksen foar de nije tabel. Tidens de útfiering fan al dizze operaasjes sammelje feroarings yn 'e logtabel.

De folgjende stap is om de wizigingen oer te bringen nei de nije tabel. De migraasje wurdt útfierd oer ferskate iteraasjes, en as der minder as 20 yngongen oerbleaun binne yn 'e logtabel, krijt pg_repack in sterke slûs, migreart de lêste gegevens en ferfangt de âlde tabel mei de nije yn' e Postgres-systeemtabellen. Dit is de ienige en heul koarte tiid dat jo net mei de tafel kinne wurkje. Hjirnei wurde de âlde tabel en de tabel mei logs wiske en wurdt romte frijmakke yn it bestânsysteem. It proses is foltôge.

Alles sjocht der geweldich yn teory, mar wat bart der yn 'e praktyk? Wy hifke pg_repack sûnder lading en ûnder lading, en kontrolearre syn wurking yn gefal fan te betiid stop (mei oare wurden, mei help fan Ctrl + C). Alle testen wiene posityf.

Wy gongen nei de itenwinkel - en doe gie alles net sa't wy ferwachte.

Earste pankoek te keap

Op it earste kluster krigen wy in flater oer in oertrêding fan in unike beheining:

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

Dizze beheining hie in auto-generearre namme index_16508 - it waard makke troch pg_repack. Op grûn fan de attributen opnommen yn syn gearstalling, hawwe wy bepaald "ús" beheining dy't oerienkomt mei it. It probleem die bliken te wêzen dat dit net in folslein gewoane beheining is, mar in útstelde (útstelde beheining), d.w.s. syn ferifikaasje wurdt letter útfierd as it kommando sql, wat liedt ta unferwachte gefolgen.

Útstelde beheiningen: wêrom se nedich binne en hoe't se wurkje

In lytse teory oer útstelde beheiningen.
Litte wy in ienfâldich foarbyld beskôgje: wy hawwe in tabelferwizingsboek fan auto's mei twa attributen - de namme en folchoarder fan 'e auto yn' e map.
Postgres: bloat, pg_repack en útstelde beheiningen

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



Litte wy sizze dat wy de earste en twadde auto's moatte ruilje. De ienfâldige oplossing is om de earste wearde te aktualisearjen nei de twadde, en de twadde nei de earste:

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

Mar as wy dizze koade útfiere, ferwachtsje wy in beheining oertreding, om't de folchoarder fan 'e wearden yn' e tabel unyk is:

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

Hoe kin ik it oars dwaan? Opsje ien: foegje in ekstra weardeferfanging ta oan in bestelling dy't garandearre net bestiet yn 'e tabel, bygelyks "-1". Yn programmearring wurdt dit neamd "de wearden fan twa fariabelen útwikselje troch in tredde." It ienige nadeel fan dizze metoade is de ekstra fernijing.

Opsje twa: Untwerp de tabel opnij om in driuwend puntgegevenstype te brûken foar de folchoarderwearde ynstee fan heule getallen. Dan, by it bywurkjen fan de wearde fan 1, bygelyks, nei 2.5, sil de earste yngong automatysk "stean" tusken de twadde en tredde. Dizze oplossing wurket, mar d'r binne twa beheiningen. Earst sil it net foar jo wurkje as de wearde earne yn 'e ynterface wurdt brûkt. Twad, ôfhinklik fan de krektens fan it gegevenstype, sille jo in beheind oantal mooglike ynfoegjes hawwe foardat jo de wearden fan alle records opnij berekkenje.

Opsje trije: meitsje de beheining útsteld sadat it allinich wurdt kontrolearre op it momint fan commit:

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

Sûnt de logika fan ús earste fersyk soarget derfoar dat alle wearden unyk binne op it momint fan commit, sil it slagje.

It hjirboppe besprutsen foarbyld is fansels tige syntetysk, mar it lit it idee sjen. Yn ús applikaasje brûke wy útstelde beheiningen om logika te ymplementearjen dy't ferantwurdlik is foar it oplossen fan konflikten as brûkers tagelyk wurkje mei dielde widgetobjekten op it boerd. Troch sokke beheiningen te brûken kinne wy ​​de applikaasjekoade in bytsje ienfâldiger meitsje.

Yn 't algemien, ôfhinklik fan it type beheining, hat Postgres trije nivo's fan granulariteit om se te kontrolearjen: rige, transaksje en ekspresjenivo's.
Postgres: bloat, pg_repack en útstelde beheiningen
Boarne: begriffs

CHECK en NOT NULL wurde altyd kontrolearre op it rigelnivo; foar oare beheiningen, lykas kin wurde sjoen út 'e tabel, binne d'r ferskate opsjes. Jo kinne mear lêze hjir.

Om koart te gearfetten, jouwe útstelde beheiningen yn in oantal situaasjes mear lêsbere koade en minder kommando's. Jo moatte lykwols betelje foar dit troch it komplisearjen fan it debuggenproses, om't it momint dat de flater optreedt en it momint dat jo it witte, wurde yn 'e tiid skieden. In oar mooglik probleem is dat de planner miskien net altyd in optimaal plan kin konstruearje as it fersyk in útstelde beheining omfettet.

Ferbettering fan pg_repack

Wy hawwe behannele wat útstelde beheiningen binne, mar hoe ferhâlde se har mei ús probleem? Lit ús de flater ûnthâlde dy't wy earder krigen hawwe:

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

It bart as gegevens wurde kopieare fan in logtabel nei in nije tabel. Dit sjocht der nuver út omdat... de gegevens yn 'e logtabel wurdt ynset tegearre mei de gegevens yn' e boarnetabel. As se foldwaan oan de beheinings fan de oarspronklike tabel, hoe kinne se skeine deselde beheinings yn de nije?

As it docht bliken, leit de woartel fan it probleem yn 'e foarige stap fan pg_repack, dy't allinich yndeksen makket, mar gjin beheiningen: de âlde tabel hie in unike beheining, en de nije makke ynstee in unike yndeks.

Postgres: bloat, pg_repack en útstelde beheiningen

It is wichtich om hjir op te merken dat as de beheining normaal is en net útsteld, dan is de unike yndeks dy't ynstee makke is lykweardich oan dizze beheining, om't Unike beheiningen yn Postgres wurde ymplementearre troch it meitsjen fan in unike yndeks. Mar yn it gefal fan in útstelde beheining is it gedrach net itselde, om't de yndeks net útsteld wurde kin en wurdt altyd kontrolearre op it momint dat it kommando sql wurdt útfierd.

Sa leit de essinsje fan it probleem yn 'e "fertraging" fan 'e kontrôle: yn' e orizjinele tabel komt it foar op 'e tiid fan commit, en yn' e nije tabel op it momint dat it kommando sql wurdt útfierd. Dit betsjut dat wy derfoar soargje moatte dat de kontrôles yn beide gefallen itselde wurde útfierd: of altyd fertrage, of altyd fuortendaliks.

Dus hokker ideeën hiene wy?

Meitsje in yndeks fergelykber mei deferred

It earste idee is om beide kontrôles yn direkte modus út te fieren. Dit kin ferskate falske positive beheiningen generearje, mar as d'r in pear binne, soe dit gjin ynfloed hawwe op it wurk fan brûkers, om't sokke konflikten in normale situaasje foar har binne. Se foarkomme bygelyks as twa brûkers tagelyk begjinne mei it bewurkjen fan deselde widget, en de kliïnt fan 'e twadde brûker hat gjin tiid om ynformaasje te ûntfangen dat de widget al blokkearre is foar bewurkjen troch de earste brûker. Yn sa'n situaasje wegeret de tsjinner de twadde brûker, en syn kliïnt rôlet de wizigingen werom en blokkearret de widget. Efkes letter, as de earste brûker it bewurkjen foltôget, sil de twadde ynformaasje krije dat de widget net langer blokkearre is en har aksje kin werhelje.

Postgres: bloat, pg_repack en útstelde beheiningen

Om derfoar te soargjen dat kontrôles altyd yn net-útstelde modus binne, hawwe wy in nije yndeks makke lykas de oarspronklike útstelde beheining:

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

Yn 'e testomjouwing krigen wy mar in pear ferwachte flaters. Sukses! Wy rûnen pg_repack wer op produksje en krigen 5 flaters op it earste kluster yn in oere wurk. Dit is in akseptabel resultaat. Lykwols, al op it twadde kluster it oantal flaters tanommen gâns en wy moasten stopje pg_repack.

Wêrom is it bard? De kâns dat in flater foarkomt hinget ôf fan hoefolle brûkers tagelyk mei deselde widgets wurkje. Blykber wiene der op dat stuit folle minder kompetitive feroarings mei de gegevens opslein op it earste kluster as op 'e oaren, d.w.s. wy wiene gewoan "lokkich".

It idee wurke net. Op dat stuit seagen wy twa oare oplossingen: skriuw ús applikaasjekoade opnij om útstelde beheiningen ôf te jaan, of "lear" pg_repack om mei har te wurkjen. Wy hawwe de twadde keazen.

Ferfange yndeksen yn 'e nije tabel mei útstelde beheiningen fan' e oarspronklike tabel

It doel fan 'e revyzje wie dúdlik - as de oarspronklike tabel in útstelde beheining hat, dan moatte jo foar de nije sa'n beheining oanmeitsje, en net in yndeks.

Om ús wizigingen te testen, hawwe wy in ienfâldige test skreaun:

  • tabel mei in útstelde beheining en ien rekord;
  • ynfoegje gegevens yn in lus dy't konflikt mei in besteande record;
  • doch in update - de gegevens konflikten net mear;
  • de feroarings ynsette.

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;

De orizjinele ferzje fan pg_repack ferûngelokke altyd by de earste ynfoegje, de wizige ferzje wurke sûnder flaters. Grut.

Wy geane nei produksje en krije wer in flater yn deselde faze fan it kopiearjen fan gegevens fan 'e logtabel nei in nije:

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

Klassike situaasje: alles wurket yn testomjouwings, mar net yn produksje?!

APPLY_COUNT en it krúspunt fan twa batches

Wy begûnen de koade letterlik line foar rigel te analysearjen en ûntdutsen in wichtich punt: gegevens wurde oerbrocht fan 'e logtabel nei in nije yn batches, de APPLY_COUNT konstante joech de grutte fan' e batch oan:

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

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

It probleem is dat de gegevens fan 'e orizjinele transaksje, wêryn ferskate operaasjes mooglik de beheining kinne skeine, by oerdracht, kinne einigje op it krúspunt fan twa batches - de helte fan' e kommando's sil yn 'e earste batch wurde ynset, en de oare helte yn de twadde. En hjir, ôfhinklik fan jo gelok: as de ploegen yn 'e earste batch neat oerbrekke, dan is alles goed, mar as se dogge, bart der in flater.

APPLY_COUNT is gelyk oan 1000 records, wat ferklearret wêrom't ús tests suksesfol wiene - se hawwe it gefal fan "batchjunction" net behannele. Wy brûkten twa kommando's - ynfoegje en bywurkje, dus krekt 500 transaksjes fan twa kommando's waarden altyd yn in batch pleatst en wy hawwe gjin problemen ûnderfûn. Nei it tafoegjen fan de twadde fernijing stoppe ús bewurking mei wurkjen:

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;

Dat, de folgjende taak is om te soargjen dat gegevens fan 'e orizjinele tabel, dy't yn ien transaksje feroare binne, ek binnen ien transaksje yn' e nije tabel einigje.

Wegering fan batching

En wer hiene wy ​​twa oplossingen. Earst: litte wy it partitionearjen yn batches folslein ferlitte en gegevens oerdrage yn ien transaksje. It foardiel fan dizze oplossing wie har ienfâld - de fereaske koadewizigingen wiene minimaal (troch de manier, yn âldere ferzjes wurke pg_reorg krekt sa). Mar d'r is in probleem - wy meitsje in langrinnende transaksje, en dit, lykas earder sein, is in bedriging foar it ûntstean fan in nije bloat.

De twadde oplossing is komplekser, mar wierskynlik mear korrekt: meitsje in kolom yn 'e logtabel mei de identifier fan' e transaksje dy't gegevens oan 'e tabel tafoege. Dan, as wy gegevens kopiearje, kinne wy ​​it groepearje troch dit attribút en soargje dat relatearre feroarings tegearre wurde oerdroegen. De batch sil wurde foarme út ferskate transaksjes (as ien grutte) en syn grutte sil fariearje ôfhinklik fan hoefolle gegevens waarden feroare yn dizze transaksjes. It is wichtich om te notearjen dat, om't gegevens fan ferskate transaksjes yn willekeurige folchoarder yn 'e logtabel komme, it net langer mooglik is om se opfolgjend te lêzen, lykas it earder wie. seqscan foar elk fersyk mei filterjen troch tx_id is te djoer, in yndeks is nedich, mar it sil ek de metoade fertrage fanwege de overhead fan it bywurkjen. Yn 't algemien, lykas altyd, moatte jo wat opofferje.

Dat, wy besletten om te begjinnen mei de earste opsje, sa't it is ienfâldiger. Earst wie it nedich om te begripen oft in lange transaksje in echte probleem wêze soe. Sûnt de wichtichste oerdracht fan gegevens fan 'e âlde tafel nei de nije komt ek foar yn ien lange transaksje, feroare de fraach yn "hoefolle sille wy dizze transaksje ferheegje?" De doer fan 'e earste transaksje is benammen ôfhinklik fan' e grutte fan 'e tafel. De doer fan in nije hinget ôf fan hoefolle feroarings sammelje yn 'e tabel tidens de gegevensoerdracht, d.w.s. oer de yntinsiteit fan 'e lading. De pg_repack run barde yn in tiid fan minimale tsjinst load, en it folume fan feroarings wie ûnevenredich lyts yn ferliking mei de oarspronklike grutte fan 'e tafel. Wy besletten dat wy de tiid fan in nije transaksje kinne negearje (foar ferliking is it gemiddeld 1 oere en 2-3 minuten).

De eksperiminten wiene posityf. Launch op produksje ek. Foar dúdlikens is hjir in foto mei de grutte fan ien fan 'e databases nei it útfieren:

Postgres: bloat, pg_repack en útstelde beheiningen

Om't wy folslein tefreden wiene mei dizze oplossing, hawwe wy net besocht de twadde te ymplementearjen, mar wy beskôgje de mooglikheid om it te besprekken mei de útwreidingsûntwikkelders. Us hjoeddeistige revyzje is spitigernôch noch net klear foar publikaasje, om't wy it probleem allinich hawwe oplost mei unike útstelde beheiningen, en foar in folsleine patch is it nedich om stipe te jaan foar oare soarten. Wy hoopje dit yn de takomst dwaan te kinnen.

Miskien hawwe jo in fraach, wêrom hawwe wy sels belutsen by dit ferhaal mei de wiziging fan pg_repack, en hawwe, bygelyks, syn analogen net brûkt? Op in stuit hawwe wy ek neitocht oer dit, mar de positive ûnderfining fan it earder brûke, op tabellen sûnder útstelde beheiningen, motivearre ús om te besykjen de essinsje fan it probleem te begripen en it te reparearjen. Derneist fereasket it brûken fan oare oplossingen ek tiid om tests út te fieren, dus wy besletten dat wy earst besykje it probleem dêryn te reparearjen, en as wy realisearje dat wy dit net yn in ridlike tiid koene dwaan, dan soene wy ​​​​begjinne te sjen nei analogen .

befinings

Wat wy kinne oanbefelje op basis fan ús eigen ûnderfining:

  1. Kontrolearje jo bloat. Op grûn fan tafersjochgegevens kinne jo begripe hoe goed autovacuum is konfigureare.
  2. Pas AUTOVACUUM oan om bloat op in akseptabel nivo te hâlden.
  3. As de bloat noch groeit en jo it net kinne oerwinne mei help fan out-of-the-box ark, wês dan net bang om eksterne útwreidingen te brûken. It wichtichste is om alles goed te testen.
  4. Wês net bang om eksterne oplossingen te feroarjen om oan jo behoeften te passen - soms kin dit effektiver en noch makliker wêze dan jo eigen koade te feroarjen.

Boarne: www.habr.com

Add a comment