
Tabelite ja indeksite paisumise mĂ”ju (bloat) on laialdaselt tuntud ja esineb mitte ainult Postgreses. On viisid selle vastu vĂ”itlemiseks âkastist vĂ€ljaâ nagu VACUUM FULL vĂ”i CLUSTER, kuid need blokeerivad tabelid töötamise ajal ja seetĂ”ttu ei saa neid alati kasutada.
Selles artiklis on veidi teooriat selle kohta, kuidas paisumine tekib, kuidas sellega saab vĂ”idelda, edasilĂŒkatud constraints ja probleemid, mida nad toovad kaasa pg_repacki laienduse kasutamisel.
See artikkel on kirjutatud minu esitluse pÔhjal PgConf.Russia 2020-l.

Kuidas paisumine tekib
Postgres pĂ”hineb mitmeversioonilisel mudelil (). Selle olemus seisneb selles, et igal tabelis oleval real vĂ”ib olla mitu versiooni, samal ajal kui tehingud nĂ€evad mitte rohkem kui ĂŒhte neist versioonidest, kuid see ei pea olema tingimata sama. See vĂ”imaldab mitme tehingu samal ajal töötada ja peaaegu mitte ĂŒksteist mĂ”jutada.
Selge on see, et kĂ”ik need versioonid tuleb salvestada. Postgres töötab mĂ€lu lehekĂŒlgede kaupa ja leht on minimaalne andmemaht, mida saab lugeda kettalt vĂ”i kirjutada. Vaatame vĂ€ikest nĂ€idet, et mĂ”ista, kuidas see juhtub.
Oletame, et meil on tabel, kuhu oleme lisanud mitu rekordit. Faili esimesel lehekĂŒljel, kuhu tabelit salvestatakse, on ilmunud uued andmed. Need on aktiivsed rida versioonid, mis on teistele tehingutele kĂ€ttesaadavad pĂ€rast kinnitamist (kerguse huvides oletame, et isolatsioonitase on Read Committed).

SeejĂ€rel uuendasime ĂŒhte rekordit, mille tulemusel tĂ€histasime vana versiooni aegunuks.

Samm-sammult, uuendades ja kustutades rida versioone, saime lehe, kus umbes pool andmetest on âprĂŒgiâ. Need andmed ei ole ĂŒhelegi tehingule nĂ€htavad.

Postgreses on mehhanism , mis puhastab aegunud versioonid ja vabastab ruumi uutele andmetele. Kuid kui see pole piisavalt agressiivselt seadistatud vĂ”i on hĂ”ivatud teiste tabelitega töötamisega, jÀÀvad âprĂŒgised andmedâ alles ja me peame kasutama lisalehti uute andmete jaoks.
Nii et meie nÀites koosneb tabel mÔnel ajahetkel neljast lehelt, kuid selles on ainult pool aktiivsetest andmetest. Tulemuseks on see, et tabelile juurdepÀÀsuks loeme palju rohkem andmeid, kui on vajalik.

Isegi kui VACUUM eemaldab kÔik aegunud ridade versioonid, ei parane olukord dramaatiliselt. Meil on olemas vaba ruum lehtedel vÔi isegi terveid lehti uute ridade jaoks, kuid me loeme endiselt rohkem andmeid, kui vajalik.
Muide, kui tĂ€iesti tĂŒhi leht (meie nĂ€ites teine) oleks faili lĂ”pus, saaks VACUUM selle kĂ€rpida. Kuid praegu asub see keskel, seega ei saa me sellega midagi teha.

Kui selliste tĂŒhi vĂ”i tugevasti tĂŒhjendatud lehtede arv suureneb, mida tuntakse kui bloat, hakkab see mĂ”jutama jĂ”udlust.
KÔik eelnevalt kirjeldatud on bloati tekkimise mehhanism tabelites. Indeksites toimub see ligikaudu samamoodi.
Kas mul on bloat?
On mitmeid viise, kuidas teada saada, kas teil on bloat. Esimese idee on kasutada Postgrase sisestatistikat, kus on ligikaudne teave tabelites ridade arvu, 'elus' ridade arvu jne kohta. Internetist on vÔimalik leida palju variatsioone juba valmis skripte. Meie alus on PostgreSQL Expertsilt, mis suudab hinnata tabelite bloati koos toast ja bloat btree-indeksitega. Meie kogemuse kohaselt on selle tÀpsus 10-20%.
Teine viis on kasutada laiendust , mis vÔimaldab vaadata lehtede sisu ja saada nii hindelisi kui ka tÀpseid bloati vÀÀrtuseid. Kuid teisel juhul tuleb kogu tabel skaneerida.
VĂ€ike bloati vÀÀrtus, kuni 20%, on meie arvates vastuvĂ”etav. Seda vĂ”ib pidada sarnaseks fillfactorâiga ja . Kui see ĂŒletab 50%, vĂ”ivad tekkida jĂ”udlusprobleemid.
Viise bloati vastu vÔitlemiseks
Postgresis on mitmeid viise bloatiga vÔitlemiseks 'kastist vÀlja', kuid need ei sobi alati ega kÔigile.
Konfigureerida AUTOVACUUM, et bloat ei tekiks. Ja kui olla tĂ€psem, et see pĂŒsiks teie jaoks vastuvĂ”etaval tasemel. Tundub, et see on âkapteniâ nĂ”uanne, kuid tegelikult ei ole seda alati kerge saavutada. NĂ€iteks, kui teete aktiivset arendust, kus andmemudelit pidevalt muudate, vĂ”i kui toimub mingi andmete migreerimine. Selle tulemusena vĂ”ib teie koormuse profiil sageli muutuda ja ĂŒldiselt on see erinev erinevate tabelite jaoks. Seega peate pidevalt natuke ette tegutsema ja kohandama AUTOVACUUMi vastavalt iga tabeli muutuvatele profiilidele. Kuid on ilmne, et seda ei ole lihtne teha.
Teine levinud pĂ”hjus, miks AUTOVACUUM ei suuda tabeleid töödelda, on pikaajalised tehingud, mis ei luba tal andmeid puhastada, kuna need on nendele tehingutele kergesti ligipÀÀsetavad. Soovitus siin on samuti ilmne â vabaneda âriputatudâ tehingutest ja minimeerida aktiivsete tehingute aega. Kuid kui teie rakenduse koormus on OLAP-i ja OLTP-i hĂŒbriid, vĂ”ib teil samaaegselt olla nii palju sagedasi vĂ€rskendusi ja lĂŒhikesi pĂ€ringuid kui ka pikka aega kestev tegevus â nĂ€iteks mĂ”ne aruande koostamine. Sellises olukorras tuleks mĂ”elda koormuse jaotamisele erinevatesse andmebaasidesse, mis vĂ”imaldab neid paremini seadistada.
Veel ĂŒks nĂ€ide â isegi kui profiil on ĂŒhtlane, kuid andmebaas on vĂ€ga kĂ”rge koormuse all, vĂ”ib isegi kĂ”ige agressiivsem AUTOVACUUM mitte hakkama saada ja bloat vĂ”ib tekkida. Skaleerimine (vertikaalne vĂ”i horisontaalne) on ainus lahendus.
Mida aga teha olukorras, kus olete AUTOVACUUMi seadnud, kuid bloat jÀtkab kasvu.
Meeskond VACUUM FULL koostab tabelite ja indeksite sisu ĂŒmber ning jĂ€tab neisse ainult asjakohased andmed. Bloat'i kĂ”rvaldamiseks töötab see ideaalselt, kuid selle tĂ€itmise ajal saadakse tabelile eriline lukustus (AccessExclusiveLock), mis ei luba selle tabeli pĂ€ringute tĂ€itmist, isegi select'e. Kui saate endale lubada teenuse vĂ”i selle osa peatamist mĂ”neks ajaks (kĂŒmnetest minutitest mitme tunnini, olenevalt andmebaasi suurusest ja teie seadmetest), siis on see variant parim. Kahjuks ei jĂ”ua me VACUUM FULL-i teha kavandatud hooldusaegade jooksul, seega see meetod ei sobi meile.
Meeskond CLUSTER taaskordab tabeli sisu nagu VACUUM FULL, vĂ”imaldades samas nĂ€idata indeksit, mille alusel andmed fĂŒĂŒsiliselt kettale jĂ€rjestatakse (kuid tulevikus ei tagata uute ridade jĂ€rjekorda). Teatud olukordades on see hea optimeerimine mitmete pĂ€ringute jaoks â indeksi kaudu mitme salvestuse lugemisega. Selle kĂ€su puudus on sama, mis VACUUM FULL-l â see lukustab tabeli töö ajal.
Meeskond REINDEX on sarnane eelnevatele, kuid see teostab konkreetse indeksi vĂ”i kĂ”ikide tabeli indeksite ĂŒmberkorraldamise. Lukustused on veidi nĂ”rgemad: ShareLock tabelil (takistab modifikatsiooni, kuid vĂ”imaldab select teadet) ja AccessExclusiveLock ĂŒmberkorraldataval indeksil (blokeerib selle indeksi kasutavaid pĂ€ringuid). Kuid PostgreSQL versioonis 12 ilmus parameeter , mis vĂ”imaldab indeksi ĂŒmberkorraldamist, blokeerimata samal ajal paralleelset rekordite lisamist, muutmist vĂ”i kustutamist.
Varem PostgreSQL versioonides vĂ”is saavutada tulemuse, mis on sarnane REINDEX CONCURRENTLY-le, kasutades . See vĂ”imaldab luua indeksi ilma range blokeerimiseta (ShareUpdateExclusiveLock, mis ei sega paralleelseid pĂ€ringuid), seejĂ€rel asendada vana indeks uuega ja eemaldada vana indeks. See aitab kĂ”rvaldada indeksite bloat'i, segamata teie rakenduse tööd. Oluline on mĂ€rkida, et indeksite ĂŒmberkorraldamise ajal on kettasĂŒsteemile tĂ€iendav koormus.
Seega, kui indeksite jaoks on olemas viise bloat'i âkuumaltâ kĂ”rvaldamiseks, siis tabelite jaoks neid ei ole. Siia tulevad erinevad vĂ€lised laiendused: (varasemalt pg_reorg), , ja teised. KĂ€esolevas artiklis ei hakka ma neid vĂ”rreldama ja rÀÀgin ainult pg_repack'ist, mida pĂ€rast teatud töötlemist kasutame.
Kuidas pg_repack töötab

Kujutage ette, et meil on tĂ€iesti tavaline tabel â indeksite, piirangute ja, kahjuks, bloat'iga. Esimeseks sammuks loob pg_repack logitabeli, et salvestada teavet kĂ”igi muudatuste kohta töö ajal. Trigger replikeerib need muudatused igal lisamisel, uuendamisel ja kustutamisel. SeejĂ€rel luuakse tabel, mis on struktuurilt algsega sarnane, kuid ilma indeksite ja piiranguteta, et mitte aeglustada andmete sisestamise protsessi.
Seejuhul pg_repack kannab vana tabeli andmed uude tabelisse, filtreerides automaatselt vÀlja kÔik ebatÀiendavad read, seejÀrel loob uue tabeli jaoks indeksid. KÔigi nende toimingute kÀigus koguneb logitabelisse muudatusi.
JĂ€rgmine samm on viia muudatused uude tabelisse. Ăleviimine toimub mitmes iteratsioonis ja kui logitabelis on alle 20 kirjet, haarab pg_repack ranged lukustuse, viib viimased andmed ĂŒle ja asendab vana tabeli uuega PostgreSQLi sĂŒsteemitabelites. See on ainus ja vĂ€ga lĂŒhike aeg, mil te ei saa tabeliga töötada. PĂ€rast seda eemaldatakse vana tabel ja logitabel ning failisĂŒsteemis vabastatakse ruum. Protsess on lĂ”petatud.
Teoorias nÀeb kÔik suurepÀrane vÀlja, aga kuidas on praktikas? Testisime pg_repacki ilma koormata ja koormuse all, kontrollisime selle töö tÔhusust varajase katkestamise korral (lihtsalt öeldes, Ctrl+C abil). KÔik testid olid positiivsed.
KĂ€isime produtseerimisprotsessis â ja siin ei lĂ€inud kĂ”ik nii, nagu ootasime.
Esimene ebaÔnnestumine tootmises
Esimesel klastril saime unikaalsuse piirangu rikkumise vea:
$ ./pg_repack -t tablename -o id
INFO: tabeli "tablename" repakkimine
ERROR: pÀring ebaÔnnestus:
ERROR: dubleeritud vÔti rikub unikaalset piirangut "index_16508"
DETAIL: VÔti (id, index)=(100500, 42) juba eksisteerib.
See piirang oli automaatselt genereeritud nimega index_16508 â selle lĂ”i pg_repack. Tema koostisosade jĂ€rgi mÀÀrasime, et see on "meie" piirang, mis vastab sellele. Probleem oligi selles, et see ei olnud tavaline piirang, vaid edasi lĂŒkatud (), st selle kontrollimine toimub hiljem kui SQL kĂ€sk, mis toob kaasa ootamatud tagajĂ€rjed.
Edasi lĂŒkatud piirangud: miks need vajalikud on ja kuidas need töötavad
Veidi teooriat edasi lĂŒkatud piirangutest.
Vaatame lihtsat nĂ€idet: meil on autode viidatud tabel, millel on kaks attribuuti â nimi ja auto jĂ€rjestus viidatud tabelis.

create table cars
(
name text constraint pk_cars primary key,
ord integer not null constraint uk_cars unique
);
Kujutame ette, et peame esimesed ja teised autod omavahel vahetama. Raskete lahendus â uuendada esimene vÀÀrtus teiseks ja teine esimesele:
begin;
update cars set ord = 2 where name = 'audi';
update cars set ord = 1 where name = 'bmw';
commit;
Kuid selle koodi teostamisel saame ootuspÀraselt piirangu rikkumise, sest tabelis vÀÀrtuste jÀrjekord on unikaalne:
[23305] VIGA: duplikaadi vĂ”tme vÀÀrtus rikub unikaalse piirangu âuk_carsâ\nDetail: Key (ord)=(2) juba eksisteerib.
Kuidas teha teisiti? Esimene variant: lisada tĂ€iendav vÀÀrtuse vahetus, mille jĂ€rjekord kindlasti ei eksisteeri tabelis, nĂ€iteks â-1â. Programmeerimises nimetatakse seda âkaks muutuja vÀÀrtuse vahetamiseks kolmanda kauduâ. Ainus puudus selle meetodi juures on tĂ€iendav vĂ€rskendus.
Teine variant: projekti ĂŒmberkujundamine, et kasutada jĂ€rjekorra vÀÀrtuse jaoks ujuva komaga andmetĂŒĂŒpi terve arvu asemel. Siis uuendades vÀÀrtust 1-st nĂ€iteks 2.5 ni, asetub esimene kirje automaatselt teise ja kolmanda vahele. See lahendus töötab, kuid on kaks piirangut. Esiteks, see ei sobi teile, kui vÀÀrtust kasutatakse kuskil liideses. Teiseks, sĂ”ltuvalt andmetĂŒĂŒbi tĂ€psusest, on teil piiratud arv vĂ”imalikke lisamisi enne, kui peate kĂ”ikide kirjete vÀÀrtused uuesti arvutama.
Kolmas variant: muuta piirang tingimuseks, et seda kontrollitakse ainult pĂŒhendamise hetkel:
create table cars\n(\n name text constraint pk_cars primary key,\n ord integer not null constraint uk_cars unique deferrable initially deferred\n);Kuna meie algse kĂŒsimuse loogika garanteerib, et pĂŒhendamise hetkeks on kĂ”ik vÀÀrtused unikaalsed, siis see Ă”nnestub.
Ălaltoodud nĂ€ide on kindlasti vĂ€ga sĂŒnteetiline, kuid idee vĂ€ljendatakse. Meie rakenduses kasutame paindlikke piiranguid loogika rakendamiseks, mis haldab konflikte, kui kasutajad töötavad samaaegselt ĂŒhiste objektide-widgetitega tahvlil. Selliste piirangute kasutamine vĂ”imaldab meil rakenduskoodi veidi lihtsamaks teha.
Ăldiselt, sĂ”ltuvalt Postgresi piirangu tĂŒĂŒbist, on nende kontrollimiseks kolm tasemete granulariteeti: rea, tehingu ja vĂ€ljendi tase.

Allikas:
CHECK ja NOT NULL kontrollitakse alati rea tasemel, teiste piirangute jaoks, nagu tabelist nĂ€ha, on erinevad variandid. Ăksikasjalikumalt saab lugeda .
KokkuvĂ”ttes annavad edasilĂŒkatud piirangud teatavates olukordades parema loetavuse ja vĂ€hem kĂ€ske. Kuid selle eest tuleb maksta debugeerimisprotsessi keerukuse eest, kuna vea esinemise hetk ja hetk, mil te sellest teada saate, on ajas eraldatud. Veel ĂŒks vĂ”imalik probleem on seotud sellega, et planeerija ei suuda alati optimeeritud plaani koostada, kui pĂ€ringus on edasilĂŒkatud piirang.
pg_repacki tÀiendamine
Oleme arutanud, mis on edasilĂŒkatud piirangud, kuid kuidas need on seotud meie probleemiga? Tuletame meelde viga, mille me varem saime:
$ ./pg_repack -t tablename -o id
INFO: tabeli "tablename" repakkimine
ERROR: pÀring ebaÔnnestus:
ERROR: dubleeritud vÔti rikub unikaalset piirangut "index_16508"
DETAIL: VÔti (id, index)=(100500, 42) juba eksisteerib.See tekib andmete kopeerimisel logitabelist uude tabelisse. See nÀeb vÀlja kummaline, kuna logitabeli andmed kinnitatakse koos algse tabeli andmetega. Kui nad vastavad algse tabeli piirangutele, siis kuidas nad saavad rikkuda samu piiranguid uues tabelis?
Nagu selgus, peitub probleemi juur pg_repacki varasemas etapis, kus luuakse ainult indeksid, mitte piirangud: vanas tabelis oli unikaalne piirang, aga uues loodi selle asemel unikaalne indeks.

Siin on oluline mĂ€rkida, et kui piirang on tavaline, mitte edasilĂŒkatud, siis selle asemel loodud unikaalne indeks on samavÀÀrne selle piiranguga, kuna Postgresis rakendatakse unikaalseid piiranguid unikaalse indeksi loomise kaudu. Kuid edasilĂŒkatud piirangu puhul ei ole kĂ€itumine sama, kuna indeks ei saa olla edasilĂŒkatud ja seda kontrollitakse alati SQL-kĂ€su tĂ€itmise hetkel.
Nii et probleemi olemus seisneb âedasilĂŒkatudâ kontrollis: algses tabelis toimub see kinnitamisel, aga uues - SQL-kĂ€su tĂ€itmisel. Seega peame tegema nii, et kontrollid toimuksid mĂ”lemas juhul ĂŒhtemoodi: kas alati edasilĂŒkatult vĂ”i alati kohe.
Nii et, millised ideed meil olid.
Luua indeks, mis on sarnane edasilĂŒkatule
Esimene idee on teha mĂ”lemad kontrollid kohe. See vĂ”ib tekitada mitmeid valepositiivseid piiranguid, kuid kui neid on vĂ€he, ei tohiks see kasutajate tööd mĂ”jutada, kuna sellised konfliktid on neile normaalsed. Need esinevad nĂ€iteks siis, kui kaks kasutajat hakkavad samal ajal ĂŒhte ja sama vidinat redigeerima ning teise kasutaja klient ei saa infot, et vidin on juba esimesest kasutajast redigeerimiseks lukustatud. Sellises olukorras reageerib server teisele kasutajale keeldumisega ning tema klient tĂŒhistab muudatused ja lukustab vidina. Hiljem, kui esimene kasutaja lĂ”petab redigeerimise, saab teine teate, et vidin ei ole enam lukus, ja saab oma toimingut korrata.

Kuna tegemist oli alati prioriteetse reĆŸiimiga, lĂ”ime uue indeksi, mis on sarnane originaalsele viivitatud piirangule:
CREATE UNIQUE INDEX CONCURRENTLY uk_tablename__immediate ON tablename (id, index);
-- kÀivita pg_repack
DROP INDEX CONCURRENTLY uk_tablename__immediate;Testkeskkonnas saime ainult mĂ”ned oodatud vead. Edu! TaaskĂ€ivitasime pg_repack tootmises ja saime ĂŒhe tunni töö jooksul esimeses klastris 5 viga. See on vastuvĂ”etav tulemus. Kuid juba teises klastris kasvas vigade arv kordades ja pidime pg_repacki peatama.
Miks see juhtus? Veaoht sĂ”ltub sellest, kui paljud kasutajad töötavad samade vidinatega samal ajal. Tundub, et esimeses klastris oli andmete seas palju vĂ€hem konkurentsivĂ”imelisi muudatusi, kui teistes, st lihtsalt âĂ”nnestusâ.
Idee ei töötanud. Sel ajal nĂ€gime kahte muud lahenduse varianti: meie rakenduskoodi ĂŒmber kirjutamine, et loobuda viivitatud piirangutest, vĂ”i pg_repack'i âĂ”petamineâ nendega töötama. Valisime teise variandi.
Asenda uue tabeli indeksid algse tabeli viivitatud piirangutega.
Töötamise eesmĂ€rk oli ilmne â kui algsel tabelil on viivitatud piirang, siis tuleks uue jaoks luua selline piirang, mitte indeks.
Meie muudatuste kontrollimiseks kirjutasime lihtsa testi:
- tabel viivitatud piiranguga ja ĂŒhe kirje;
- sisestame tsĂŒklis andmed, mis konkureerivad olemasoleva kirje vastu;
- teeme uuenduse â andmed ei ole enam vastuolus;
- komitee muudatused.
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;Esialgne versioon pg_repack kukkus alati esimesel insert'il, parendatud versioon töötas ilma vigadeta. SuurepÀrane.
Liigume tootmisse ja saame jÀlle vea samas faasis, kui kopeerime andmeid logitabelist uude:
$ ./pg_repack -t tablename -o id
INFO: tabeli "tablename" repakkimine
ERROR: pÀring ebaÔnnestus:
ERROR: dubleeritud vÔti rikub unikaalset piirangut "index_16508"
DETAIL: VÔti (id, index)=(100500, 42) juba eksisteerib.Klassikaline olukord: testkeskkondades töötab kÔik, aga tootmises ei?!
APPLY_COUNT ja kahe partii kohtumine
Alustasime koodi analĂŒĂŒsimist sĂ”na-sĂ”nalt ja avastasime olulise hetke: andmete ĂŒlekandmine logitabelist uude toimub partiitena, konstants APPLY_COUNT nĂ€itas partii suurust:
for (;;)
{
num = apply_log(connection, table, APPLY_COUNT);
if (num > MIN_TUPLES_BEFORE_SWITCH)
continue;
/* there might be still some tuples, repeat. */
...
}Probleem on selles, et algse tehingu andmed, kus mitu toimingut vĂ”ivad potentsiaalselt piirangut rikkuda, vĂ”ivad ĂŒlekandmisel sattuda kahe partii vahele â pool kĂ€ske kinnitatakse esimeses parteis ja teine pool teises. Siin vĂ”ib minna nii, et kui esimeses parteis ei rikkuda midagi, siis on kĂ”ik korras, aga kui rikutakse â toimub viga.
APPLY_COUNT on 1000 kirjet, mis selgitab, miks meie testid lĂ€ksid edukalt â nad ei katnud juhtumit âpartii kohtumisestâ. Kasutasime kahte kĂ€sku â insert ja update, seega tĂ€pselt 500 tehingut kahe kĂ€skuga mahtusid alati partii, ega kogenud probleeme. PĂ€rast teise uuenduse lisamist lĂ”petas meie muudatus töötamise:
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; -- ĂŒks uuendus veel
COMMIT;
END;
END LOOP;Nii et jĂ€rgmine ĂŒlesanne â teha nii, et andmed algsest tabelist, mis muutuvad ĂŒhes tehingus, jaguks ka uude tabelisse samas tehingus.
Partii kasutamisest loobumine
Ja meil oli taas kaks lahendust. Esimene: loobume ĂŒldse partiide jagamisest ja teeme andmete ĂŒlekande ĂŒhe tehinguga. Selle lahenduse kasuks rÀÀkis selle lihtsus â vajalikud koodimuudatused on minimaalsed (muide, vanemates versioonides töötas pg_reorg tookord just nii). Kuid probleem on see, et loome pikaajalise tehingu, ja see, nagu varem öeldud, on oht uue bloat'i tekkeks.
Teine lahendus â keerulisem, kuid ilmselt Ă”igem: luua logitabelis veerg, mis sisaldab selle tehingu identifikaatorit, mis lisas andmed tabelisse. Sel juhul saame andmete kopeerimise ajal neid selle atribuudi jĂ€rgi grupeerida ja tagada, et seotud muudatused kantakse ĂŒle koos. Partii koosneb mitmest tehingust (vĂ”i ĂŒhest suurest) ja selle suurus varieerub sĂ”ltuvalt sellest, kui palju andmeid nende tehingute kĂ€igus muudeti. Oluline on mĂ€rkida, et kuna erinevate tehingute andmed satuvad logitabelisse juhuslikus jĂ€rjekorras, ei saa seda enam lugeda jĂ€rjestikku nagu varem. seqscan iga pĂ€ringu puhul, kus filtrimine toimub tx_id jĂ€rgi â on liiga kallis, vajalik on indeks, kuid see aeglustab meetodi tööd kulude tĂ”ttu selle uuendamisel. ĂhesĂ”naga, nagu alati, tuleb millegagi ofreerida.
Nii et otsustasime alustada esimesest variandist, kuna see oli lihtsam. Esiteks oli vaja mĂ”ista, kas pikk tehing on tĂ”sine probleem. Kuna andmete pĂ”hiasi ĂŒlekande vanast tabelist uude toimub samuti ĂŒhe pikaajalise tehingu kĂ€igus, siis kĂŒsimus muutus kĂŒsimuseks: âkui palju me suurendame seda tehingut?â Esimese tehingu kestus sĂ”ltub peamiselt tabeli suurusest. Uue kestus sĂ”ltub sellest, kui palju muudatusi koguneb tabelisse andmete ĂŒlekande ajal, st koormuse intensiivsusest. pg_repack kĂ€ivitamine toimus teenuse minimaalsete koormuste ajal ning muudatuste hulk oli vĂ”rreldes algse tabeli suurusega vĂ”rreldamatult vĂ€ike. Otsustasime, et saame uue tehingu kestuse ignoreerida (keskmiselt on see 1h ja 2-3 minutit).
Eksperimendid olid positiivsed. KĂ€ivitamine tootmises samuti. Selguse huvides â pilt ĂŒhe andmebaasi suurusest pĂ€rast kĂ€itamist:

Kuna see lahendus rahuldas meid tĂ€ielikult, ei proovinud me teist rakendust, kuid kaalume selle arutamist laienduse arendajatega. Kahjuks ei ole meie praegune tĂ€iendus veel avaldamiseks valmis, kuna lahendasime probleemi ainult ainulaadsete edasi lĂŒkatud piirangutega, kuid tĂ€ieliku plaani jaoks on vaja toetada ka muid tĂŒĂŒpe. Loodame, et suudame seda tulevikus teha.
VĂ”ib-olla tekkis teil kĂŒsimus, miks me ĂŒldse sellesse pg_repacki tĂ€iustamisse sukeldusime, mitte ei kasutanud nĂ€iteks selle analooge? MĂ”nes etapis mĂ”tlesime ka sellele, kuid varasem positiivne kogemus selle kasutamisest edasi lĂŒkatud piiranguteta tabelitel motiveeris meid probleemi olemust mĂ”istma ja selle parandamiseks pingutama. Lisaks sellele nĂ”uab teiste lahenduste kasutamine samuti aega testimiseks, seetĂ”ttu otsustasime, et proovime esmalt probleemi selles lahenduses lahendada, ja kui mĂ”istame, et ei suuda seda mĂ”istliku aja jooksul teha, siis hakkame kaaluma analooge.
JĂ€reldused
Mida me saame soovitada oma kogemuste pÔhjal:
- JÀlgige oma bloat'i. JÀlgimise andmete pÔhjal saate aru, kui hÀsti on autovacuum seadistatud.
- Seadistage AUTOVACUUM, et hoida bloat lubatud tasemel.
- Kui bloat siiski kasvab ja te ei saa seda âkarbist vĂ€ljaâ vahenditega vĂ”ita, Ă€rge kartke kasutada vĂ€list laiendust. Peamine on kĂ”ik pĂ”hjalikult testida.
- Ărge kartke tĂ€iustada vĂ€liseid lahendusi vastavalt oma vajadustele â mĂ”nikord vĂ”ib see olla efektiivsem ja isegi lihtsam kui teie enda koodi muutmine.
Allikas: habr.com
