UzpÅ«Å”anÄs ietekme uz tabulÄm un indeksiem ir plaÅ”i zinÄma un ir sastopama ne tikai Postgres. Ir veidi, kÄ ar to rÄ«koties, piemÄram, VACUUM FULL vai CLUSTER, taÄu tie darbÄ«bas laikÄ bloÄ·Ä galdus, un tÄpÄc tos ne vienmÄr var izmantot.
RakstÄ bÅ«s neliela teorija par to, kÄ rodas uzpÅ«Å”anÄs, kÄ ar to cÄ«nÄ«ties, par atliktajiem ierobežojumiem un problÄmÄm, ko tie rada paplaÅ”inÄjuma pg_repack lietoÅ”anÄ.
Šis raksts ir uzrakstīts, pamatojoties uz
KÄpÄc rodas vÄdera uzpÅ«Å”anÄs?
Postgres pamatÄ ir vairÄku versiju modelis (
AcÄ«mredzot visas Ŕīs versijas ir jÄsaglabÄ. Postgres strÄdÄ ar atmiÅu pa lappusei, un lapa ir minimÄlais datu apjoms, ko var nolasÄ«t no diska vai ierakstÄ«t. ApskatÄ«sim nelielu piemÄru, lai saprastu, kÄ tas notiek.
PieÅemsim, ka mums ir tabula, kurai esam pievienojuÅ”i vairÄkus ierakstus. Faila pirmajÄ lapÄ, kurÄ glabÄjas tabula, ir parÄdÄ«juÅ”ies jauni dati. Å Ä«s ir rindu reÄllaika versijas, kas ir pieejamas citÄm transakcijÄm pÄc saistÄ«bu izpildes (vienkÄrŔības labad mÄs pieÅemsim, ka izolÄcijas lÄ«menis ir Read Committed).
PÄc tam mÄs atjauninÄjÄm vienu no ierakstiem, tÄdÄjÄdi atzÄ«mÄjot veco versiju kÄ vairs neatbilstoÅ”u.
Soli pa solim, atjauninot un dzÄÅ”ot rindu versijas, mÄs nonÄcÄm pie lapas, kurÄ aptuveni puse datu ir āatkritumiā. Å ie dati nav redzami nevienam darÄ«jumam.
Postgres ir mehÄnisms
TÄtad mÅ«su piemÄrÄ tabula kÄdÄ brÄ«dÄ« sastÄvÄs no ÄetrÄm lapÄm, bet tikai puse no tÄs saturÄs reÄllaika datus. RezultÄtÄ, piekļūstot tabulai, mÄs nolasÄ«sim daudz vairÄk datu nekÄ nepiecieÅ”ams.
Pat ja VACUUM tagad izdzÄÅ” visas neatbilstoÅ”Äs rindu versijas, situÄcija dramatiski neuzlabosies. Mums bÅ«s brÄ«va vieta lapÄs vai pat veselas lapas jaunÄm rindÄm, taÄu mÄs joprojÄm nolasÄ«sim vairÄk datu nekÄ nepiecieÅ”ams.
Starp citu, ja faila beigÄs bÅ«tu pilnÄ«gi tukÅ”a lapa (otrÄ mÅ«su piemÄrÄ), tad VACUUM varÄtu to apgriezt. Bet tagad viÅa ir pa vidu, tÄpÄc ar viÅu neko nevar darÄ«t.
Kad Å”Ädu tukÅ”u vai ļoti retu lapu skaits kļūst liels, ko sauc par uzpÅ«Å”anos, tas sÄk ietekmÄt veiktspÄju.
Viss iepriekÅ” aprakstÄ«tais ir uzpÅ«Å”anÄs raÅ”anÄs mehÄnika tabulÄs. Indeksos tas notiek apmÄram tÄpat.
Vai man ir vÄdera uzpÅ«Å”anÄs?
Ir vairÄki veidi, kÄ noteikt, vai jums ir vÄdera uzpÅ«Å”anÄs. PirmÄ ideja ir izmantot iekÅ”Äjo Postgres statistiku, kas satur aptuvenu informÄciju par rindu skaitu tabulÄs, "dzÄ«vo" rindu skaitu utt. InternetÄ varat atrast daudz gatavu skriptu variÄcijas. MÄs ÅÄmÄm par pamatu
VÄl viens veids ir izmantot paplaÅ”inÄjumu
MÄs uzskatÄm, ka neliela uzpÅ«Å”anÄs vÄrtÄ«ba, lÄ«dz 20%, ir pieÅemama. To var uzskatÄ«t par pildfaktora analogu
Veidi, kÄ cÄ«nÄ«ties ar vÄdera uzpÅ«Å”anos
Postgres ir vairÄki veidi, kÄ tikt galÄ ar vÄdera uzpÅ«Å”anos, taÄu tie ne vienmÄr ir piemÄroti ikvienam.
KonfigurÄjiet AUTOVACUUM, lai nenotiktu uzpÅ«Å”anÄs. Vai precÄ«zÄk, noturÄt to sev pieÅemamÄ lÄ«menÄ«. Å Ä·iet, ka tas ir "kapteiÅa" padoms, taÄu patiesÄ«bÄ to ne vienmÄr ir viegli sasniegt. PiemÄram, jums ir aktÄ«va izstrÄde ar regulÄrÄm izmaiÅÄm datu shÄmÄ vai notiek kÄda veida datu migrÄcija. RezultÄtÄ jÅ«su slodzes profils var bieži mainÄ«ties un parasti atŔķiras atkarÄ«bÄ no tabulas. Tas nozÄ«mÄ, ka jums ir pastÄvÄ«gi jÄstrÄdÄ nedaudz uz priekÅ”u un jÄpielÄgo AUTOVACUUM katra galda mainÄ«gajam profilam. Bet acÄ«mredzot tas nav viegli izdarÄms.
VÄl viens izplatÄ«ts iemesls, kÄpÄc AUTOVACUUM nevar sekot lÄ«dzi tabulÄm, ir tas, ka ir ilgstoÅ”i veikti darÄ«jumi, kas neļauj tai tÄ«rÄ«t Å”iem darÄ«jumiem pieejamos datus. ArÄ« Å”eit ieteikums ir acÄ«mredzams - atbrÄ«vojieties no ākarÄjoÅ”iemā darÄ«jumiem un samaziniet aktÄ«vo darÄ«jumu laiku. Bet, ja jÅ«su lietojumprogrammas slodze ir OLAP un OLTP hibrÄ«ds, jums vienlaikus var bÅ«t daudz biežu atjauninÄjumu un Ä«su vaicÄjumu, kÄ arÄ« ilgtermiÅa darbÄ«bas, piemÄram, atskaites veidoÅ”ana. Å ÄdÄ situÄcijÄ ir vÄrts padomÄt par slodzes sadalÄ«Å”anu pa dažÄdÄm bÄzÄm, kas ļaus precÄ«zÄk noregulÄt katru no tÄm.
VÄl viens piemÄrs - pat ja profils ir viendabÄ«gs, bet datu bÄze ir ļoti noslogota, tad pat agresÄ«vÄkais AUTOVACUUM var netikt galÄ, un rodas uzpÅ«Å”anÄs. MÄrogoÅ”ana (vertikÄla vai horizontÄla) ir vienÄ«gais risinÄjums.
Ko darÄ«t situÄcijÄ, kad esi uzstÄdÄ«jis AUTOVAKUUMU, bet uzpÅ«Å”anÄs turpina augt.
Komanda VAKUUMS PILNS pÄrbÅ«vÄ tabulu un indeksu saturu un atstÄj tajos tikai atbilstoÅ”os datus. Lai novÄrstu uzpÅ«Å”anos, tas darbojas lieliski, taÄu tÄ izpildes laikÄ tiek fiksÄts ekskluzÄ«vs bloÄ·ÄtÄjs uz galda (AccessExclusiveLock), kas neļaus izpildÄ«t vaicÄjumus Å”ajÄ tabulÄ, pat atlasa. Ja varat atļauties pÄrtraukt pakalpojumu vai tÄ daļu uz kÄdu laiku (no desmitiem minÅ«Å”u lÄ«dz vairÄkÄm stundÄm atkarÄ«bÄ no datu bÄzes lieluma un aparatÅ«ras), tad Ŕī iespÄja ir vislabÄkÄ. DiemžÄl plÄnotÄs apkopes laikÄ mums nav laika palaist VACUUM FULL, tÄpÄc Ŕī metode mums nav piemÄrota.
Komanda CLUSTER PÄrbÅ«vÄ tabulu saturu tÄpat kÄ VACUUM FULL, bet ļauj norÄdÄ«t indeksu, pÄc kura dati tiks fiziski pasÅ«tÄ«ti diskÄ (bet turpmÄk jaunÄm rindÄm secÄ«ba netiek garantÄta). NoteiktÄs situÄcijÄs tÄ ir laba optimizÄcija vairÄkiem vaicÄjumiem ā nolasot vairÄkus ierakstus pÄc indeksa. Komandas trÅ«kums ir tÄds pats kÄ VACUUM FULL - tÄ darbÄ«bas laikÄ bloÄ·Ä galdu.
Komanda REINDEKSS lÄ«dzÄ«gi kÄ iepriekÅ”Äjie divi, bet pÄrbÅ«vÄ konkrÄtu indeksu vai visus tabulas indeksus. SlÄdzenes ir nedaudz vÄjÄkas: ShareLock tabulÄ (novÄrÅ” modifikÄcijas, bet ļauj atlasÄ«t) un AccessExclusiveLock indeksam, kas tiek pÄrbÅ«vÄts (bloÄ·Ä vaicÄjumus, izmantojot Å”o indeksu). TomÄr Postgres 12. versijÄ parÄdÄ«jÄs parametrs
IepriekÅ”ÄjÄs Postgres versijÄs jÅ«s varat sasniegt rezultÄtu, kas lÄ«dzÄ«gs REINDEX VIENLAIDÄŖGI izmantojot
TÄdÄjÄdi, ja indeksiem ir veidi, kÄ novÄrst uzpÅ«Å”anos ālidojumÄā, tad tabulÄm tÄdu nav. Å eit tiek izmantoti dažÄdi ÄrÄjie paplaÅ”inÄjumi:
KÄ darbojas pg_repack
Teiksim, mums ir pavisam parasta tabula ā ar indeksiem, ierobežojumiem un diemžÄl ar uzpÅ«Å”anos. Pirmais pg_repack solis ir izveidot žurnÄla tabulu, lai saglabÄtu datus par visÄm izmaiÅÄm, kamÄr tÄ darbojas. Trigeris atkÄrtos Ŕīs izmaiÅas katrai ievietoÅ”anai, atjauninÄÅ”anai un dzÄÅ”anai. PÄc tam tiek izveidota tabula, kas pÄc struktÅ«ras ir lÄ«dzÄ«ga oriÄ£inÄlajai, bet bez indeksiem un ierobežojumiem, lai nepalÄninÄtu datu ievietoÅ”anas procesu.
PÄc tam pg_repack pÄrsÅ«ta datus no vecÄs tabulas uz jauno tabulu, automÄtiski filtrÄjot visas neatbilstoÅ”Äs rindas, un pÄc tam izveido indeksus jaunajai tabulai. Visu Å”o darbÄ«bu izpildes laikÄ Å¾urnÄla tabulÄ uzkrÄjas izmaiÅas.
NÄkamais solis ir pÄrsÅ«tÄ«t izmaiÅas uz jauno tabulu. MigrÄÅ”ana tiek veikta vairÄkÄs iterÄcijÄs, un, ja žurnÄla tabulÄ ir atlicis mazÄk par 20 ierakstiem, pg_repack iegÅ«st spÄcÄ«gu bloÄ·ÄÅ”anu, migrÄ jaunÄkos datus un aizstÄj veco tabulu ar jauno Postgres sistÄmas tabulÄs. Å is ir vienÄ«gais un ļoti Ä«sais brÄ«dis, kad nevarÄsi strÄdÄt ar galdu. PÄc tam vecÄ tabula un tabula ar žurnÄliem tiek dzÄsta un failu sistÄmÄ tiek atbrÄ«vota vieta. Process ir pabeigts.
TeorÄtiski viss izskatÄs lieliski, bet kas notiek praksÄ? MÄs pÄrbaudÄ«jÄm pg_repack bez slodzes un zem slodzes, kÄ arÄ« pÄrbaudÄ«jÄm tÄ darbÄ«bu priekÅ”laicÄ«gas apstÄÅ”anÄs gadÄ«jumÄ (citiem vÄrdiem sakot, izmantojot Ctrl+C). Visi testi bija pozitÄ«vi.
MÄs devÄmies uz pÄrtikas veikalu - un tad viss neizdevÄs tÄ, kÄ mÄs gaidÄ«jÄm.
PirmÄ pankÅ«ka pÄrdoÅ”anÄ
PirmajÄ klasterÄ« mÄs saÅÄmÄm kļūdu par unikÄla ierobežojuma pÄrkÄpumu:
$ ./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.
Å im ierobežojumam bija automÄtiski Ä£enerÄts nosaukums index_16508 ā to izveidoja pg_repack. Pamatojoties uz tÄ sastÄvÄ iekļautajiem atribÅ«tiem, mÄs noteicÄm āmÅ«suā ierobežojumu, kas tam atbilst. ProblÄma izrÄdÄ«jÄs tÄda, ka tas nav gluži parasts ierobežojums, bet gan atlikts (
Atliktie ierobežojumi: kÄpÄc tie ir vajadzÄ«gi un kÄ tie darbojas
Nedaudz teorijas par atliktajiem ierobežojumiem.
ApsvÄrsim vienkÄrÅ”u piemÄru: mums ir automaŔīnu tabula-uzziÅu grÄmata ar diviem atribÅ«tiem - automaŔīnas nosaukumu un secÄ«bu direktorijÄ.
create table cars
(
name text constraint pk_cars primary key,
ord integer not null constraint uk_cars unique
);
PieÅemsim, ka mums vajadzÄja apmainÄ«t pirmo un otro automaŔīnu. VienkÄrÅ”s risinÄjums ir atjauninÄt pirmo vÄrtÄ«bu uz otro, bet otro uz pirmo:
begin;
update cars set ord = 2 where name = 'audi';
update cars set ord = 1 where name = 'bmw';
commit;
Bet, palaižot Å”o kodu, mÄs sagaidÄm ierobežojumu pÄrkÄpumu, jo vÄrtÄ«bu secÄ«ba tabulÄ ir unikÄla:
[23305] ERROR: duplicate key value violates unique constraint āuk_carsā
Detail: Key (ord)=(2) already exists.
KÄ es varu darÄ«t savÄdÄk? PirmÄ iespÄja: pievienojiet papildu vÄrtÄ«bas aizstÄÅ”anu pasÅ«tÄ«jumam, kas tiek garantÄts, ka tabulÄ neeksistÄ, piemÄram, ā-1ā. ProgrammÄÅ”anÄ to sauc par "divu mainÄ«go vÄrtÄ«bu apmaiÅu ar treÅ”o". VienÄ«gais Ŕīs metodes trÅ«kums ir papildu atjauninÄjums.
OtrÄ iespÄja: pÄrveidojiet tabulu, lai pasÅ«tÄ«juma vÄrtÄ«bai izmantotu peldoÅ”Ä komata datu tipu, nevis veselus skaitļus. PÄc tam, atjauninot vÄrtÄ«bu no 1, piemÄram, uz 2.5, pirmais ieraksts automÄtiski "stÄvÄs" starp otro un treÅ”o. Å is risinÄjums darbojas, taÄu ir divi ierobežojumi. PirmkÄrt, tas nedarbosies, ja vÄrtÄ«ba tiks izmantota kaut kur saskarnÄ. OtrkÄrt, atkarÄ«bÄ no datu veida precizitÄtes, pirms visu ierakstu vÄrtÄ«bu pÄrrÄÄ·inÄÅ”anas jums bÅ«s ierobežots iespÄjamo ievietojumu skaits.
TreÅ”Ä iespÄja: atlikt ierobežojumu, lai tas tiktu pÄrbaudÄ«ts tikai saistÄ«bu izpildes laikÄ:
create table cars
(
name text constraint pk_cars primary key,
ord integer not null constraint uk_cars unique deferrable initially deferred
);
TÄ kÄ mÅ«su sÄkotnÄjÄ pieprasÄ«juma loÄ£ika nodroÅ”ina, ka visas vÄrtÄ«bas izpildes brÄ«dÄ« ir unikÄlas, tas izdosies.
IepriekÅ” apspriestais piemÄrs, protams, ir ļoti sintÄtisks, taÄu tas atklÄj ideju. MÅ«su lietojumprogrammÄ mÄs izmantojam atliktos ierobežojumus, lai ieviestu loÄ£iku, kas ir atbildÄ«ga par konfliktu atrisinÄÅ”anu, kad lietotÄji vienlaikus strÄdÄ ar koplietotiem logrÄ«ku objektiem uz tÄfeles. Å Ädu ierobežojumu izmantoÅ”ana ļauj mums padarÄ«t lietojumprogrammas kodu nedaudz vienkÄrÅ”Äku.
KopumÄ, atkarÄ«bÄ no ierobežojuma veida, Postgres to pÄrbaudei ir trÄ«s precizitÄtes lÄ«meÅi: rindas, transakcijas un izteiksmes lÄ«meÅi.
Avots:
CHECK un NOT NULL vienmÄr tiek pÄrbaudÄ«ts rindas lÄ«menÄ«; citiem ierobežojumiem, kÄ redzams tabulÄ, ir dažÄdas iespÄjas. JÅ«s varat lasÄ«t vairÄk
ÄŖsumÄ apkopojot, atliktie ierobežojumi vairÄkÄs situÄcijÄs nodroÅ”ina labÄk lasÄmu kodu un mazÄk komandu. TomÄr jums par to ir jÄmaksÄ, sarežģījot atkļūdoÅ”anas procesu, jo kļūdas raÅ”anÄs brÄ«dis un brÄ«dis, kad jÅ«s par to uzzinÄjÄt, tiek savlaicÄ«gi atdalÄ«ti. VÄl viena iespÄjamÄ problÄma ir tÄ, ka plÄnotÄjs ne vienmÄr var izveidot optimÄlu plÄnu, ja pieprasÄ«jums ir saistÄ«ts ar atliktu ierobežojumu.
Pg_repack uzlaboŔana
MÄs esam apskatÄ«juÅ”i, kas ir atliktie ierobežojumi, bet kÄ tie ir saistÄ«ti ar mÅ«su problÄmu? AtcerÄsimies kļūdu, ko saÅÄmÄm iepriekÅ”:
$ ./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.
Tas notiek, kad dati tiek kopÄti no žurnÄla tabulas uz jaunu tabulu. Tas izskatÄs dÄ«vaini, jo... žurnÄla tabulas dati tiek piesaistÄ«ti kopÄ ar datiem avota tabulÄ. Ja tie atbilst sÄkotnÄjÄs tabulas ierobežojumiem, kÄ viÅi var pÄrkÄpt tos paÅ”us ierobežojumus jaunajÄ tabulÄ?
KÄ izrÄdÄs, problÄmas sakne slÄpjas iepriekÅ”ÄjÄ pg_repack solÄ«, kas rada tikai indeksus, bet ne ierobežojumus: vecajai tabulai bija unikÄls ierobežojums, un jaunÄ tÄ vietÄ izveidoja unikÄlu indeksu.
Å eit ir svarÄ«gi atzÄ«mÄt, ka, ja ierobežojums ir normÄls un nav atlikts, tÄ vietÄ izveidotais unikÄlais indekss ir lÄ«dzvÄrtÄ«gs Å”im ierobežojumam, jo UnikÄlie ierobežojumi programmÄ Postgres tiek ieviesti, izveidojot unikÄlu indeksu. Bet atliktÄ ierobežojuma gadÄ«jumÄ uzvedÄ«ba nav tÄda pati, jo indeksu nevar atlikt, un tas vienmÄr tiek pÄrbaudÄ«ts sql komandas izpildes laikÄ.
TÄdÄjÄdi problÄmas bÅ«tÄ«ba slÄpjas pÄrbaudes āaizkavÄÅ”anÄā: sÄkotnÄjÄ tabulÄ tas notiek apÅemÅ”anÄs brÄ«dÄ«, bet jaunajÄ tabulÄ ā sql komandas izpildes laikÄ. Tas nozÄ«mÄ, ka mums ir jÄpÄrliecinÄs, ka abos gadÄ«jumos pÄrbaudes tiek veiktas vienÄdi: vai nu vienmÄr ar kavÄÅ”anos, vai vienmÄr nekavÄjoties.
TÄtad, kÄdas idejas mums bija?
Izveidojiet indeksu, kas ir līdzīgs atliktajam
PirmÄ ideja ir veikt abas pÄrbaudes tÅ«lÄ«tÄjÄ režīmÄ. Tas var radÄ«t vairÄkus kļūdaini pozitÄ«vus ierobežojumus, taÄu, ja to ir maz, tam nevajadzÄtu ietekmÄt lietotÄju darbu, jo Å”Ädi konflikti viÅiem ir normÄla situÄcija. TÄs rodas, piemÄram, ja divi lietotÄji vienlaikus sÄk rediÄ£Ät vienu un to paÅ”u logrÄ«ku, un otrÄ lietotÄja klientam nav laika saÅemt informÄciju, ka pirmais lietotÄjs jau ir bloÄ·Äjis logrÄ«ka rediÄ£ÄÅ”anu. Å ÄdÄ situÄcijÄ serveris atsakÄs no otrÄ lietotÄja, un tÄ klients atsauc izmaiÅas un bloÄ·Ä logrÄ«ku. Nedaudz vÄlÄk, kad pirmais lietotÄjs pabeigs rediÄ£ÄÅ”anu, otrais saÅems informÄciju, ka logrÄ«ks vairs nav bloÄ·Äts un varÄs atkÄrtot savu darbÄ«bu.
Lai nodroÅ”inÄtu, ka pÄrbaudes vienmÄr ir neatliktÄ režīmÄ, mÄs izveidojÄm jaunu indeksu, kas ir lÄ«dzÄ«gs sÄkotnÄjam atliktajam ierobežojumam:
CREATE UNIQUE INDEX CONCURRENTLY uk_tablename__immediate ON tablename (id, index);
-- run pg_repack
DROP INDEX CONCURRENTLY uk_tablename__immediate;
Testa vidÄ mÄs saÅÄmÄm tikai dažas paredzamÄs kļūdas. Veiksmi! MÄs atkal palaidÄm pg_repack ražoÅ”anas režīmÄ un saÅÄmÄm 5 kļūdas pirmajÄ klasterÄ« darba stundas laikÄ. Tas ir pieÅemams rezultÄts. TomÄr jau otrajÄ klasterÄ« kļūdu skaits ievÄrojami palielinÄjÄs, un mums bija jÄpÄrtrauc pg_repack.
KÄpÄc tas notika? Kļūdas raÅ”anÄs iespÄjamÄ«ba ir atkarÄ«ga no tÄ, cik lietotÄju vienlaikus strÄdÄ ar vieniem un tiem paÅ”iem logrÄ«kiem. AcÄ«mredzot tajÄ brÄ«dÄ« bija daudz mazÄk konkurences izmaiÅu ar pirmajÄ klasterÄ« glabÄtajiem datiem nekÄ pÄrÄjos, t.i. mums vienkÄrÅ”i "paveicÄs".
Ideja nedarbojÄs. TajÄ brÄ«dÄ« mÄs redzÄjÄm divus citus risinÄjumus: pÄrrakstÄ«t mÅ«su lietojumprogrammas kodu, lai atbrÄ«votos no atliktajiem ierobežojumiem, vai āiemÄcÄ«tā pg_repack strÄdÄt ar tiem. MÄs izvÄlÄjÄmies otro.
AizstÄt indeksus jaunajÄ tabulÄ ar atliktiem ierobežojumiem no sÄkotnÄjÄs tabulas
PÄrskatÄ«Å”anas mÄrÄ·is bija acÄ«mredzams - ja sÄkotnÄjÄ tabulÄ ir atliktais ierobežojums, tad jaunajai ir jÄizveido Å”Äds ierobežojums, nevis indekss.
Lai pÄrbaudÄ«tu izmaiÅas, mÄs uzrakstÄ«jÄm vienkÄrÅ”u testu:
- tabula ar atlikto ierobežojumu un vienu ierakstu;
- ievietot datus cilpÄ, kas ir pretrunÄ ar esoÅ”u ierakstu;
- veikt atjauninÄjumu ā dati vairs nekonfliktÄ;
- veikt izmaiÅas.
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;
SÄkotnÄjÄ pg_repack versija vienmÄr avarÄja pirmajÄ ievietoÅ”anas reizÄ, pÄrveidotÄ versija strÄdÄja bez kļūdÄm. Lieliski.
MÄs pÄrejam uz ražoÅ”anu un atkal saÅemam kļūdu tajÄ paÅ”Ä fÄzÄ, kopÄjot datus no žurnÄla tabulas uz jaunu:
$ ./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.
KlasiskÄ situÄcija: testa vidÄs viss strÄdÄ, bet ražoÅ”anÄ ne?!
APPLY_COUNT un divu grupu krustojums
MÄs sÄkÄm analizÄt kodu burtiski rindiÅu pa rindiÅai un atklÄjÄm svarÄ«gu punktu: dati tiek pÄrsÅ«tÄ«ti no žurnÄla tabulas uz jaunu partijÄs, konstante APPLY_COUNT norÄdÄ«ja partijas lielumu:
for (;;)
{
num = apply_log(connection, table, APPLY_COUNT);
if (num > MIN_TUPLES_BEFORE_SWITCH)
continue; /* there might be still some tuples, repeat. */
...
}
ProblÄma ir tÄda, ka dati no sÄkotnÄjÄ darÄ«juma, kurÄ vairÄkas operÄcijas, iespÄjams, varÄtu pÄrkÄpt ierobežojumu, pÄrsÅ«tot, var nonÄkt divu pakeÅ”u krustpunktÄ ā puse komandu tiks izpildÄ«tas pirmajÄ partijÄ, bet otra puse otrajÄ. Un Å”eit, atkarÄ«bÄ no jÅ«su veiksmes: ja komandas pirmajÄ partijÄ neko nepÄrkÄpj, tad viss ir kÄrtÄ«bÄ, bet, ja to dara, rodas kļūda.
APPLY_COUNT ir vienÄds ar 1000 ierakstiem, kas izskaidro, kÄpÄc mÅ«su testi bija veiksmÄ«gi ā tie neaptvÄra āpakeÅ”u savienojumaā gadÄ«jumu. MÄs izmantojÄm divas komandas - ievietot un atjauninÄt, tÄpÄc tieÅ”i 500 transakcijas no divÄm komandÄm vienmÄr tika ievietotas partijÄ, un mums nebija nekÄdu problÄmu. PÄc otrÄ atjauninÄjuma pievienoÅ”anas mÅ«su labojums pÄrstÄja darboties:
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;
TÄtad nÄkamais uzdevums ir nodroÅ”inÄt, lai dati no sÄkotnÄjÄs tabulas, kas tika mainÄ«ti vienÄ darÄ«jumÄ, nonÄktu jaunajÄ tabulÄ arÄ« viena darÄ«juma ietvaros.
Atteikums no partiju komplektÄÅ”anas
Un atkal mums bija divi risinÄjumi. PirmkÄrt: pilnÄ«bÄ atteiksimies no sadalÄ«Å”anas pa partijÄm un pÄrsÅ«tÄ«sim datus vienÄ darÄ«jumÄ. Å Ä« risinÄjuma priekÅ”rocÄ«ba bija tÄ vienkÄrŔība ā nepiecieÅ”amÄs koda izmaiÅas bija minimÄlas (starp citu, vecÄkÄs versijÄs pg_reorg darbojÄs tieÅ”i tÄ). Bet ir problÄma - mÄs veidojam ilgstoÅ”u darÄ«jumu, un tas, kÄ jau iepriekÅ” teikts, ir drauds jauna uzpÅ«Å”anÄs raÅ”anÄs brÄ«dim.
Otrs risinÄjums ir sarežģītÄks, bet, iespÄjams, pareizÄks: žurnÄla tabulÄ izveidojiet kolonnu ar tÄs darÄ«juma identifikatoru, kas pievienoja tabulai datus. PÄc tam, kad mÄs kopÄjam datus, mÄs varam tos grupÄt pÄc Ŕī atribÅ«ta un nodroÅ”inÄt, ka saistÄ«tÄs izmaiÅas tiek pÄrsÅ«tÄ«tas kopÄ. Partija tiks veidota no vairÄkiem darÄ«jumiem (vai viena liela), un tÄs lielums mainÄ«sies atkarÄ«bÄ no tÄ, cik daudz datu Å”ajos darÄ«jumos tika mainÄ«ts. Ir svarÄ«gi atzÄ«mÄt, ka, tÄ kÄ dažÄdu darÄ«jumu dati žurnÄla tabulÄ nonÄk nejauÅ”Ä secÄ«bÄ, tos vairs nebÅ«s iespÄjams nolasÄ«t secÄ«gi, kÄ tas bija iepriekÅ”. seqscan katram pieprasÄ«jumam ar filtrÄÅ”anu pÄc tx_id ir pÄrÄk dÄrgs, ir nepiecieÅ”ams indekss, taÄu tas arÄ« palÄninÄs metodi, jo tÄ atjauninÄÅ”ana ir saistÄ«ta ar papildu izmaksÄm. KopumÄ, kÄ vienmÄr, jums ir nepiecieÅ”ams kaut ko upurÄt.
TÄpÄc mÄs nolÄmÄm sÄkt ar pirmo iespÄju, jo tÄ ir vienkÄrÅ”Äka. PirmkÄrt, bija jÄsaprot, vai ilgstoÅ”s darÄ«jums bÅ«s reÄla problÄma. TÄ kÄ galvenÄ datu pÄrsÅ«tÄ«Å”ana no vecÄs tabulas uz jauno notiek arÄ« vienÄ garÄ darÄ«jumÄ, jautÄjums pÄrvÄrtÄs par ācik mÄs palielinÄsim Å”o darÄ«jumu?ā PirmÄ darÄ«juma ilgums galvenokÄrt ir atkarÄ«gs no tabulas lieluma. Jaunas ilgums ir atkarÄ«gs no tÄ, cik izmaiÅu uzkrÄjas tabulÄ datu pÄrsÅ«tÄ«Å”anas laikÄ, t.i. uz slodzes intensitÄti. Pg_repack izpilde notika minimÄlas pakalpojuma slodzes laikÄ, un izmaiÅu apjoms bija nesamÄrÄ«gi mazs salÄ«dzinÄjumÄ ar sÄkotnÄjo tabulas izmÄru. NolÄmÄm, ka varam atstÄt novÄrtÄ jauna darÄ«juma laiku (salÄ«dzinÄjumam, vidÄji tÄ ir 1 stunda un 2-3 minÅ«tes).
Eksperimenti bija pozitÄ«vi. UzsÄkt arÄ« ražoÅ”anu. SkaidrÄ«bas labad Å”eit ir attÄls ar vienas datu bÄzes lielumu pÄc palaiÅ”anas:
TÄ kÄ Å”is risinÄjums mÅ«s pilnÄ«bÄ apmierinÄja, tad otro nemÄÄ£inÄjÄm ieviest, taÄu apsveram iespÄju to apspriest ar paplaÅ”inÄjuma izstrÄdÄtÄjiem. MÅ«su paÅ”reizÄjÄ redakcija diemžÄl vÄl nav gatava publicÄÅ”anai, jo problÄmu atrisinÄjÄm tikai ar unikÄliem atliktajiem ierobežojumiem, un pilnvÄrtÄ«gam ielÄpam ir nepiecieÅ”ams nodroÅ”inÄt atbalstu citiem veidiem. MÄs ceram, ka varÄsim to izdarÄ«t arÄ« turpmÄk.
VarbÅ«t jums ir jautÄjums, kÄpÄc mÄs pat iesaistÄ«jÄmies Å”ajÄ stÄstÄ ar pg_repack modifikÄciju un neizmantojÄm, piemÄram, tÄ analogus? KÄdÄ brÄ«dÄ« arÄ« mÄs par to domÄjÄm, taÄu pozitÄ«vÄ pieredze, izmantojot to agrÄk, uz tabulÄm bez atliktiem ierobežojumiem, motivÄja mÅ«s mÄÄ£inÄt izprast problÄmas bÅ«tÄ«bu un to novÄrst. TurklÄt arÄ« citu risinÄjumu izmantoÅ”ana prasa laiku testu veikÅ”anai, tÄpÄc nolÄmÄm, ka vispirms mÄÄ£inÄsim tajÄ novÄrst problÄmu un, ja sapratÄ«sim, ka nevaram to izdarÄ«t saprÄtÄ«gÄ termiÅÄ, tad sÄksim meklÄt analogus. .
Atzinumi
Ko mÄs varam ieteikt, pamatojoties uz savu pieredzi:
- Uzraugiet savu vÄdera uzpÅ«Å”anos. Pamatojoties uz uzraudzÄ«bas datiem, varat saprast, cik labi ir konfigurÄts autovakuums.
- NoregulÄjiet AUTOVACUUM, lai uzpÅ«Å”anÄs bÅ«tu pieÅemamÄ lÄ«menÄ«.
- Ja uzpÅ«Å”anÄs joprojÄm aug un jÅ«s nevarat to pÄrvarÄt, izmantojot gatavus rÄ«kus, nebaidieties izmantot ÄrÄjos paplaÅ”inÄjumus. Galvenais ir visu labi pÄrbaudÄ«t.
- Nebaidieties modificÄt ÄrÄjos risinÄjumus atbilstoÅ”i savÄm vajadzÄ«bÄm ā dažreiz tas var bÅ«t efektÄ«vÄk un pat vienkÄrÅ”Äk nekÄ mainÄ«t savu kodu.
Avots: www.habr.com