
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 PgConf.Russia 2020. gadā.

Kāpēc rodas vēdera uzpūšanās?
Postgres pamatā ir vairāku versiju modelis (). Tās būtība ir tāda, ka katrai tabulas rindai var būt vairākas versijas, savukārt transakcijām ir redzama ne vairāk kā viena no šīm versijām, taču ne vienmēr ir viena un tā pati. Tas ļauj vairākiem darījumiem darboties vienlaikus un praktiski neietekmē viens otru.
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 , kas notīra novecojušās versijas un nodrošina vietu jauniem datiem. Bet, ja tas nav pietiekami agresīvi konfigurēts vai ir aizņemts ar darbu citās tabulās, tad paliek “atkritumu dati”, un mums ir jāizmanto papildu lapas jauniem datiem.
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 no PostgreSQL Experts, kas var novērtēt bloat tabulas kopā ar grauzdēšanas un uzpūšanās btree indeksiem. Mūsu pieredze liecina, ka tā kļūda ir 10-20%.
Vēl viens veids ir izmantot paplašinājumu , kas ļauj ieskatīties lapās un iegūt gan aptuveno, gan precīzu uzpūšanās vērtību. Bet otrajā gadījumā jums būs jāskenē visa tabula.
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 и . Pie 50% un vairāk var sākties veiktspējas problēmas.
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 , kas ļauj atjaunot indeksu, nebloķējot vienlaicīgu ierakstu pievienošanu, modificēšanu vai dzēšanu.
Iepriekšējās Postgres versijās jūs varat sasniegt rezultātu, kas līdzīgs REINDEX VIENLAIDĪGI izmantojot . Tas ļauj izveidot indeksu bez stingras bloķēšanas (ShareUpdateExclusiveLock, kas netraucē paralēliem vaicājumiem), pēc tam aizstāt veco indeksu ar jaunu un izdzēst veco indeksu. Tas ļauj novērst indeksa uzpūšanos, netraucējot jūsu lietojumprogrammai. Ir svarīgi ņemt vērā, ka, atjaunojot indeksus, diska apakšsistēmai būs papildu slodze.
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: (iepriekš pg_reorg), , un citi. Šajā rakstā es tos nesalīdzināšu un runāšu tikai par pg_repack, kuru pēc dažām izmaiņām mēs izmantojam paši.
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 (), t.i. tā pārbaude tiek veikta vēlāk nekā sql komanda, kas noved pie neparedzētām sekām.
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
