Postgres: bloat, pg_repack și constrângeri amânate

Postgres: bloat, pg_repack și constrângeri amânate

Efectul balonării asupra tabelelor și indicilor este larg cunoscut și este prezent nu numai în Postgres. Există modalități de a face față acestei probleme, cum ar fi VACUUM FULL sau CLUSTER, dar blochează mesele în timpul funcționării și, prin urmare, nu pot fi folosite întotdeauna.

Articolul va conține o mică teorie despre cum apare balonarea, cum o poți combate, despre constrângerile amânate și despre problemele pe care acestea le aduc la utilizarea extensiei pg_repack.

Acest articol este scris pe baza discursul meu la PgConf.Russia 2020.

De ce apare balonarea?

Postgres se bazează pe un model cu versiuni multiple (MVCC). Esența sa este că fiecare rând din tabel poate avea mai multe versiuni, în timp ce tranzacțiile nu văd mai mult de una dintre aceste versiuni, dar nu neapărat aceeași. Acest lucru permite mai multor tranzacții să funcționeze simultan și să nu aibă practic niciun impact unul asupra celuilalt.

Evident, toate aceste versiuni trebuie stocate. Postgres funcționează cu memorie pagină cu pagină și o pagină este cantitatea minimă de date care poate fi citită de pe disc sau scrisă. Să ne uităm la un mic exemplu pentru a înțelege cum se întâmplă acest lucru.

Să presupunem că avem un tabel la care am adăugat mai multe înregistrări. Date noi au apărut pe prima pagină a fișierului în care este stocat tabelul. Acestea sunt versiuni live ale rândurilor care sunt disponibile pentru alte tranzacții după un commit (pentru simplitate, vom presupune că nivelul de izolare este Read Committed).

Postgres: bloat, pg_repack și constrângeri amânate

Am actualizat apoi una dintre intrări, marcând astfel versiunea veche ca nu mai relevantă.

Postgres: bloat, pg_repack și constrângeri amânate

Pas cu pas, actualizând și ștergând versiunile de rând, am ajuns la o pagină în care aproximativ jumătate din date sunt „gunoi”. Aceste date nu sunt vizibile pentru nicio tranzacție.

Postgres: bloat, pg_repack și constrângeri amânate

Postgres are un mecanism VID, care curăță versiunile învechite și face loc pentru date noi. Dar dacă nu este configurat suficient de agresiv sau este ocupat să lucreze în alte tabele, atunci „date deșeurilor” rămân și trebuie să folosim pagini suplimentare pentru date noi.

Deci, în exemplul nostru, la un moment dat, tabelul va fi format din patru pagini, dar numai jumătate din el va conține date live. Drept urmare, la accesarea tabelului, vom citi mult mai multe date decât este necesar.

Postgres: bloat, pg_repack și constrângeri amânate

Chiar dacă acum VACUUM șterge toate versiunile de rând irelevante, situația nu se va îmbunătăți dramatic. Vom avea spațiu liber în pagini sau chiar pagini întregi pentru rândurile noi, dar vom citi în continuare mai multe date decât este necesar.
Apropo, dacă o pagină complet goală (a doua din exemplul nostru) ar fi la sfârșitul fișierului, atunci VACUUM ar putea să o decupeze. Dar acum e la mijloc, așa că nu se poate face nimic cu ea.

Postgres: bloat, pg_repack și constrângeri amânate

Când numărul de astfel de pagini goale sau foarte rare devine mare, ceea ce se numește balonare, începe să afecteze performanța.

Tot ceea ce este descris mai sus este mecanica apariției balonării în tabele. În indici, acest lucru se întâmplă aproape în același mod.

Am balonare?

Există mai multe moduri de a determina dacă aveți balonare. Ideea primului este de a folosi statistici interne Postgres, care conțin informații aproximative despre numărul de rânduri din tabele, numărul de rânduri „în direct” etc. Puteți găsi multe variante de scripturi gata făcute pe Internet. Am luat ca bază scenariu de la PostgreSQL Experts, care poate evalua tabelele bloat împreună cu indicii btree toast și bloat. Din experiența noastră, eroarea sa este de 10-20%.

O altă modalitate este să utilizați extensia pgstattuple, care vă permite să vă uitați în interiorul paginilor și să obțineți atât o valoare estimată, cât și o valoare exactă. Dar în al doilea caz, va trebui să scanați întregul tabel.

Considerăm o valoare mică de balonare, de până la 20%, acceptabilă. Poate fi considerat ca un analog al factorului de umplere pentru Mese и indici. La 50% și peste, pot apărea probleme de performanță.

Modalități de a combate balonarea

Postgres are mai multe moduri de a face față balonării din cutie, dar nu sunt întotdeauna potrivite pentru toată lumea.

Configurați AUTOVACUUM astfel încât să nu apară balonare. Sau mai precis, pentru a-l menține la un nivel acceptabil pentru tine. Acesta pare a fi sfatul „căpitanului”, dar, în realitate, acesta nu este întotdeauna ușor de realizat. De exemplu, aveți o dezvoltare activă cu modificări regulate ale schemei de date sau are loc un fel de migrare a datelor. Ca rezultat, profilul dvs. de încărcare se poate schimba frecvent și va varia de obicei de la un tabel la altul. Aceasta înseamnă că trebuie să lucrați în mod constant puțin înainte și să ajustați AUTOVACUUM la profilul schimbător al fiecărei mese. Dar, evident, acest lucru nu este ușor de făcut.

Un alt motiv comun pentru care AUTOVACUUM nu poate ține pasul cu tabelele este că există tranzacții de lungă durată care îl împiedică să curețe datele disponibile pentru tranzacțiile respective. Recomandarea aici este, de asemenea, evidentă - scăpați de tranzacțiile „atârnate” și minimizați timpul tranzacțiilor active. Dar dacă încărcarea aplicației dvs. este un hibrid de OLAP și OLTP, atunci puteți avea simultan multe actualizări frecvente și interogări scurte, precum și operațiuni pe termen lung - de exemplu, construirea unui raport. Într-o astfel de situație, merită să ne gândim la răspândirea sarcinii pe diferite baze, ceea ce va permite o mai bună reglare a fiecăreia dintre ele.

Un alt exemplu - chiar dacă profilul este omogen, dar baza de date este sub o sarcină foarte mare, atunci chiar și cel mai agresiv AUTOVACUUM poate să nu facă față și va apărea balonare. Scalare (verticală sau orizontală) este singura soluție.

Ce să faci într-o situație în care ai setat AUTOVACUUM, dar balonarea continuă să crească.

Echipă VACUUM PLIN reconstruiește conținutul tabelelor și indexurilor și lasă în ele doar date relevante. Pentru a elimina balonarea, funcționează perfect, dar în timpul execuției sale este capturată o blocare exclusivă pe tabel (AccessExclusiveLock), care nu va permite executarea de interogări pe acest tabel, chiar și selectări. Dacă vă puteți permite să opriți serviciul sau o parte a acestuia pentru o perioadă de timp (de la zeci de minute la câteva ore, în funcție de dimensiunea bazei de date și a hardware-ului dvs.), atunci această opțiune este cea mai bună. Din păcate, nu avem timp să rulăm VACUUM FULL în timpul întreținerii programate, așa că această metodă nu este potrivită pentru noi.

Echipă CURSE Reconstruiește conținutul tabelelor în același mod ca VACUUM FULL, dar vă permite să specificați un index în funcție de care datele vor fi ordonate fizic pe disc (dar în viitor ordinea nu este garantată pentru rândurile noi). În anumite situații, aceasta este o optimizare bună pentru un număr de interogări - cu citirea mai multor înregistrări după index. Dezavantajul comenzii este același cu cel al VACUUM FULL - blochează masa în timpul funcționării.

Echipă REINDEXARE similar cu cele două anterioare, dar reconstruiește un index specific sau toți indecșii tabelului. Blocările sunt puțin mai slabe: ShareLock pe masă (previne modificări, dar permite selectarea) și AccessExclusiveLock pe index care este reconstruit (blochează interogările folosind acest index). Cu toate acestea, în cea de-a 12-a versiune a Postgres a apărut un parametru CONCURSENT, care vă permite să reconstruiți indexul fără a bloca adăugarea, modificarea sau ștergerea simultană a înregistrărilor.

În versiunile anterioare ale Postgres, puteți obține un rezultat similar utilizării REINDEX CONCURRENTLY CREAȚI INDEX ÎN CONCURS. Vă permite să creați un index fără blocare strictă (ShareUpdateExclusiveLock, care nu interferează cu interogările paralele), apoi înlocuiți vechiul index cu unul nou și ștergeți vechiul index. Acest lucru vă permite să eliminați balonarea indexului fără a interfera cu aplicația dvs. Este important să luați în considerare faptul că atunci când reconstruiți indecșii va exista o încărcare suplimentară pe subsistemul disc.

Astfel, dacă pentru indici există modalități de a elimina balonarea „din mers”, atunci nu există niciuna pentru tabele. Aici intră în joc diverse extensii externe: pg_repack (fost pg_reorg), pgcompact, pgcompacttable si altii. În acest articol, nu le voi compara și voi vorbi doar despre pg_repack, pe care, după unele modificări, îl folosim singuri.

Cum funcționează pg_repack

Postgres: bloat, pg_repack și constrângeri amânate
Să presupunem că avem un tabel complet obișnuit - cu indici, restricții și, din păcate, cu balonare. Primul pas al pg_repack este de a crea un tabel de jurnal pentru a stoca date despre toate modificările în timp ce acesta rulează. Declanșatorul va replica aceste modificări pentru fiecare inserare, actualizare și ștergere. Apoi se creează un tabel, asemănător celui original ca structură, dar fără indici și restricții, pentru a nu încetini procesul de inserare a datelor.

Apoi, pg_repack transferă datele din tabelul vechi în tabelul nou, eliminând automat toate rândurile irelevante și apoi creează indecși pentru noul tabel. În timpul executării tuturor acestor operațiuni, modificările se acumulează în tabelul de jurnal.

Următorul pas este transferul modificărilor în noul tabel. Migrarea se efectuează pe mai multe iterații, iar când mai sunt mai puțin de 20 de intrări în tabelul de jurnal, pg_repack dobândește o blocare puternică, migrează cele mai recente date și înlocuiește tabelul vechi cu cel nou din tabelele de sistem Postgres. Acesta este singurul și foarte scurt timp în care nu veți putea lucra cu masa. După aceasta, tabelul vechi și tabelul cu jurnalele sunt șterse și spațiul este eliberat în sistemul de fișiere. Procesul este finalizat.

Totul arată grozav în teorie, dar ce se întâmplă în practică? Am testat pg_repack fără sarcină și sub sarcină și am verificat funcționarea acestuia în caz de oprire prematură (cu alte cuvinte, folosind Ctrl+C). Toate testele au fost pozitive.

Am mers la magazinul de produse alimentare - și apoi totul nu a decurs așa cum ne așteptam.

Prima clătită la vânzare

Pe primul cluster am primit o eroare despre o încălcare a unei constrângeri unice:

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

Această limitare a avut un nume generat automat index_16508 - a fost creat de pg_repack. Pe baza atributelor incluse în compoziția sa, am determinat constrângerea „noastre” care îi corespunde. Problema s-a dovedit a fi că aceasta nu este o limitare complet obișnuită, ci una amânată (constrângere amânată), adică verificarea acestuia este efectuată mai târziu decât comanda sql, ceea ce duce la consecințe neașteptate.

Constrângeri amânate: de ce sunt necesare și cum funcționează

O mică teorie despre restricțiile amânate.
Să luăm în considerare un exemplu simplu: avem un tabel-carte de referință de mașini cu două atribute - numele și ordinea mașinii în director.
Postgres: bloat, pg_repack și constrângeri amânate

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



Să presupunem că trebuie să schimbăm prima și a doua mașină. Soluția simplă este să actualizați prima valoare la a doua și a doua la prima:

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

Dar când rulăm acest cod, ne așteptăm la o încălcare a constrângerii, deoarece ordinea valorilor din tabel este unică:

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

Cum pot să o fac diferit? Opțiunea 1: adăugați o valoare suplimentară de înlocuire la o comandă care este garantată să nu existe în tabel, de exemplu „-XNUMX”. În programare, acest lucru se numește „schimbul valorilor a două variabile printr-o a treia”. Singurul dezavantaj al acestei metode este actualizarea suplimentară.

Opțiunea a doua: Reproiectați tabelul pentru a utiliza un tip de date în virgulă mobilă pentru valoarea comenzii în loc de numere întregi. Apoi, când actualizați valoarea de la 1, de exemplu, la 2.5, prima intrare va „sta” automat între a doua și a treia. Această soluție funcționează, dar există două limitări. În primul rând, nu va funcționa pentru tine dacă valoarea este folosită undeva în interfață. În al doilea rând, în funcție de precizia tipului de date, veți avea un număr limitat de inserări posibile înainte de a recalcula valorile tuturor înregistrărilor.

Opțiunea trei: amânați constrângerea astfel încât să fie verificată numai în momentul comiterii:

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

Deoarece logica solicitării noastre inițiale asigură că toate valorile sunt unice în momentul comiterii, aceasta va reuși.

Exemplul discutat mai sus este, desigur, foarte sintetic, dar dezvăluie ideea. În aplicația noastră, folosim constrângeri amânate pentru a implementa logica care este responsabilă pentru rezolvarea conflictelor atunci când utilizatorii lucrează simultan cu obiecte widget partajate de pe tablă. Utilizarea unor astfel de restricții ne permite să simplificăm puțin codul aplicației.

În general, în funcție de tipul de constrângere, Postgres are trei niveluri de granularitate pentru verificarea acestora: niveluri de rând, tranzacție și expresie.
Postgres: bloat, pg_repack și constrângeri amânate
Sursa: rugăciuni

CHECK și NOT NULL sunt întotdeauna bifate la nivel de rând; pentru alte restricții, după cum se poate vedea din tabel, există diferite opțiuni. Puteți citi mai multe aici.

Pentru a rezuma pe scurt, constrângerile amânate într-un număr de situații oferă un cod mai lizibil și mai puține comenzi. Cu toate acestea, trebuie să plătiți pentru acest lucru complicând procesul de depanare, deoarece momentul în care apare eroarea și momentul în care aflați despre aceasta sunt separate în timp. O altă problemă posibilă este că planificatorul poate să nu fie întotdeauna capabil să construiască un plan optim dacă cererea implică o constrângere amânată.

Îmbunătățirea pg_repack

Am acoperit ce sunt constrângerile amânate, dar cum se leagă ele cu problema noastră? Să ne amintim eroarea primită mai devreme:

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

Apare atunci când datele sunt copiate dintr-un tabel de jurnal într-un tabel nou. Asta pare ciudat pentru că... datele din tabelul de jurnal sunt comite împreună cu datele din tabelul sursă. Dacă îndeplinesc constrângerile din tabelul original, cum pot încălca aceleași constrângeri în cel nou?

După cum se dovedește, rădăcina problemei se află în pasul anterior al pg_repack, care creează numai indici, dar nu constrângeri: tabelul vechi avea o constrângere unică, iar cel nou a creat un index unic.

Postgres: bloat, pg_repack și constrângeri amânate

Este important de reținut aici că, dacă constrângerea este normală și nu este amânată, atunci indexul unic creat este echivalent cu această constrângere, deoarece Constrângerile unice în Postgres sunt implementate prin crearea unui index unic. Dar în cazul unei constrângeri amânate, comportamentul nu este același, deoarece indexul nu poate fi amânat și este întotdeauna verificat în momentul executării comenzii sql.

Astfel, esența problemei constă în „întârzierea” verificării: în tabelul original apare în momentul comiterii, iar în tabelul nou în momentul executării comenzii sql. Aceasta înseamnă că trebuie să ne asigurăm că verificările sunt efectuate la fel în ambele cazuri: fie întotdeauna întârziate, fie întotdeauna imediat.

Deci ce idei aveam?

Creați un index similar cu deferred

Prima idee este de a efectua ambele verificări în modul imediat. Acest lucru poate genera mai multe restricții fals pozitive, dar dacă sunt puține, acest lucru nu ar trebui să afecteze munca utilizatorilor, deoarece astfel de conflicte reprezintă o situație normală pentru ei. Acestea apar, de exemplu, atunci când doi utilizatori încep să editeze același widget în același timp, iar clientul celui de-al doilea utilizator nu are timp să primească informații că widget-ul este deja blocat pentru editare de către primul utilizator. Într-o astfel de situație, serverul îl refuză pe cel de-al doilea utilizator, iar clientul său derulează înapoi modificările și blochează widget-ul. Puțin mai târziu, când primul utilizator finalizează editarea, al doilea va primi informații că widget-ul nu mai este blocat și își va putea repeta acțiunea.

Postgres: bloat, pg_repack și constrângeri amânate

Pentru a ne asigura că verificările sunt întotdeauna în modul neamânat, am creat un nou index similar cu constrângerea amânată inițială:

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

În mediul de testare, am primit doar câteva erori așteptate. Succes! Am rulat pg_repack din nou în producție și am primit 5 erori pe primul cluster într-o oră de lucru. Acesta este un rezultat acceptabil. Cu toate acestea, deja pe al doilea cluster numărul de erori a crescut semnificativ și a trebuit să oprim pg_repack.

De ce s-a întâmplat? Probabilitatea apariției unei erori depinde de câți utilizatori lucrează cu aceleași widget-uri în același timp. Aparent, în acel moment au existat mult mai puține schimbări competitive cu datele stocate pe primul cluster decât pe celelalte, i.e. am fost doar „norocoși”.

Ideea nu a mers. În acel moment, am văzut alte două soluții: rescrieți codul aplicației noastre pentru a renunța la constrângerile amânate sau „învățați” pg_repack să lucreze cu ele. Noi l-am ales pe al doilea.

Înlocuiți indecșii din noul tabel cu constrângeri amânate din tabelul original

Scopul revizuirii a fost evident - dacă tabelul original are o constrângere amânată, atunci pentru cel nou trebuie să creați o astfel de constrângere și nu un index.

Pentru a ne testa modificările, am scris un test simplu:

  • tabel cu o constrângere amânată și o înregistrare;
  • inserați date într-o buclă care intră în conflict cu o înregistrare existentă;
  • faceți o actualizare – datele nu mai intră în conflict;
  • comite modificările.

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;

Versiunea originală a pg_repack s-a prăbușit întotdeauna la prima inserare, versiunea modificată a funcționat fără erori. Grozav.

Trecem la producție și primim din nou o eroare în aceeași fază de copiere a datelor din tabelul de jurnal într-unul nou:

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

Situație clasică: totul funcționează în medii de testare, dar nu și în producție?!

APPLY_COUNT și joncțiunea a două loturi

Am început să analizăm codul literal linie cu linie și am descoperit un punct important: datele sunt transferate din tabelul de jurnal într-unul nou în loturi, constanta APPLY_COUNT a indicat dimensiunea lotului:

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

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

Problema este că datele din tranzacția inițială, în care mai multe operațiuni ar putea încălca constrângerea, atunci când sunt transferate, pot ajunge la joncțiunea a două loturi - jumătate din comenzi vor fi comise în primul lot, iar cealaltă jumătate in secunda. Și aici, în funcție de norocul tău: dacă echipele nu încalcă nimic în primul lot, atunci totul este în regulă, dar dacă o fac, apare o eroare.

APPLY_COUNT este egal cu 1000 de înregistrări, ceea ce explică de ce testele noastre au avut succes - nu au acoperit cazul „joncțiunii lotului”. Am folosit două comenzi - inserare și actualizare, așa că exact 500 de tranzacții a două comenzi au fost întotdeauna plasate într-un lot și nu am întâmpinat probleme. După adăugarea celei de-a doua actualizări, editarea noastră a încetat să funcționeze:

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;

Deci, următoarea sarcină este să vă asigurați că datele din tabelul original, care a fost schimbat într-o singură tranzacție, ajung în noul tabel și într-o singură tranzacție.

Refuzul de la dozare

Și din nou am avut două soluții. În primul rând: să renunțăm complet la partiționarea în loturi și să transferăm date într-o singură tranzacție. Avantajul acestei soluții a fost simplitatea ei - modificările de cod necesare erau minime (apropo, în versiunile mai vechi pg_reorg funcționa exact așa). Dar există o problemă - creăm o tranzacție de lungă durată, iar aceasta, așa cum s-a spus anterior, este o amenințare la apariția unei noi balonări.

A doua soluție este mai complexă, dar probabil mai corectă: creați o coloană în tabelul de jurnal cu identificatorul tranzacției care a adăugat date în tabel. Apoi, când copiem datele, le putem grupa după acest atribut și ne asigurăm că modificările aferente sunt transferate împreună. Lotul va fi format din mai multe tranzacții (sau una mare) iar dimensiunea acestuia va varia în funcție de câte date au fost modificate în aceste tranzacții. Este important să rețineți că, deoarece datele din diferite tranzacții intră în tabelul de jurnal într-o ordine aleatorie, nu va mai fi posibil să le citiți secvenţial, așa cum era înainte. seqscan pentru fiecare cerere cu filtrare prin tx_id este prea scump, este nevoie de un index, dar va încetini și metoda din cauza suprasolicitarii actualizării acesteia. În general, ca întotdeauna, trebuie să sacrifici ceva.

Așadar, am decis să începem cu prima variantă, deoarece este mai simplă. În primul rând, a fost necesar să se înțeleagă dacă o tranzacție lungă ar fi o problemă reală. Deoarece transferul principal de date din tabelul vechi în cel nou are loc și într-o tranzacție lungă, întrebarea s-a transformat în „cât vom crește această tranzacție?” Durata primei tranzacții depinde în principal de dimensiunea tabelului. Durata unuia nou depinde de câte modificări se acumulează în tabel în timpul transferului de date, adică. asupra intensității sarcinii. Executarea pg_repack a avut loc într-o perioadă de încărcare minimă a serviciului, iar volumul modificărilor a fost disproporționat de mic în comparație cu dimensiunea inițială a tabelului. Am decis că putem neglija timpul unei noi tranzacții (pentru comparație, în medie este de 1 oră și 2-3 minute).

Experimentele au fost pozitive. Lansați și în producție. Pentru claritate, iată o imagine cu dimensiunea uneia dintre bazele de date după rulare:

Postgres: bloat, pg_repack și constrângeri amânate

Întrucât am fost complet mulțumiți de această soluție, nu am încercat să o implementăm pe a doua, dar luăm în considerare posibilitatea de a o discuta cu dezvoltatorii de extensii. Revizia noastră actuală, din păcate, nu este încă gata pentru publicare, deoarece am rezolvat problema doar cu restricții unice amânate, iar pentru un patch complet este necesar să oferim suport pentru alte tipuri. Sperăm să putem face acest lucru în viitor.

Poate aveți o întrebare, de ce ne-am implicat chiar în această poveste cu modificarea pg_repack și nu am folosit, de exemplu, analogii? La un moment dat ne-am gândit și la asta, dar experiența pozitivă de a-l folosi mai devreme, pe tabele fără constrângeri amânate, ne-a motivat să încercăm să înțelegem esența problemei și să o remediem. În plus, folosirea altor soluții necesită și timp pentru a efectua teste, așa că am decis că vom încerca mai întâi să remediem problema din ele, iar dacă ne-am da seama că nu putem face acest lucru într-un timp rezonabil, atunci am începe să ne uităm la analogi. .

Constatări

Ce putem recomanda pe baza propriei noastre experiențe:

  1. Monitorizați-vă balonarea. Pe baza datelor de monitorizare, puteți înțelege cât de bine este configurat autovacuum.
  2. Reglați AUTOVACUUM pentru a menține balonarea la un nivel acceptabil.
  3. Dacă balonarea este în continuare în creștere și nu o puteți depăși folosind instrumente ieșite din cutie, nu vă fie teamă să utilizați extensii externe. Principalul lucru este să testați totul bine.
  4. Nu vă fie teamă să modificați soluțiile externe pentru a se potrivi nevoilor dvs. - uneori acest lucru poate fi mai eficient și chiar mai ușor decât schimbarea propriului cod.

Sursa: www.habr.com

Adauga un comentariu