Postgres: bloat, pg_repack og deferred constraints

Postgres: bloat, pg_repack og deferred constraints

Effekten av oppblåsthet på tabeller og indekser er viden kjent og finnes ikke bare i Postgres. Det finnes måter å håndtere det på, som VACUUM FULL eller CLUSTER, men de låser bord under drift og kan derfor ikke alltid brukes.

Artikkelen vil inneholde en liten teori om hvordan oppblåsthet oppstår, hvordan du kan bekjempe det, om utsatte begrensninger og problemene de medfører for bruken av utvidelsen pg_repack.

Denne artikkelen er skrevet basert på min tale på PgConf.Russia 2020.

Hvorfor oppstår oppblåsthet?

Postgres er basert på en multiversjonsmodell (MVCC). Essensen er at hver rad i tabellen kan ha flere versjoner, mens transaksjoner ikke ser mer enn én av disse versjonene, men ikke nødvendigvis den samme. Dette gjør at flere transaksjoner kan fungere samtidig og har praktisk talt ingen innvirkning på hverandre.

Det er klart at alle disse versjonene må lagres. Postgres jobber med minne side for side og en side er minimumsmengden data som kan leses fra disk eller skrives. La oss se på et lite eksempel for å forstå hvordan dette skjer.

La oss si at vi har en tabell som vi har lagt til flere poster til. Nye data har dukket opp på første side i filen der tabellen er lagret. Dette er live-versjoner av rader som er tilgjengelige for andre transaksjoner etter en commit (for enkelhets skyld vil vi anta at isolasjonsnivået er Read Committed).

Postgres: bloat, pg_repack og deferred constraints

Vi oppdaterte deretter en av oppføringene, og markerte dermed den gamle versjonen som ikke lenger relevant.

Postgres: bloat, pg_repack og deferred constraints

Trinn for trinn, oppdatering og sletting av radversjoner, endte vi opp med en side der omtrent halvparten av dataene er "søppel". Disse dataene er ikke synlige for noen transaksjon.

Postgres: bloat, pg_repack og deferred constraints

Postgres har en mekanisme VAKUUM, som rydder ut foreldede versjoner og gir plass til nye data. Men hvis den ikke er konfigurert aggressivt nok eller er opptatt med å jobbe i andre tabeller, gjenstår "søppeldata", og vi må bruke flere sider for nye data.

Så i vårt eksempel vil tabellen på et tidspunkt bestå av fire sider, men bare halvparten av den vil inneholde live-data. Som et resultat, når vi åpner tabellen, vil vi lese mye mer data enn nødvendig.

Postgres: bloat, pg_repack og deferred constraints

Selv om VACUUM nå sletter alle irrelevante radversjoner, vil ikke situasjonen forbedres dramatisk. Vi vil ha ledig plass på sider eller til og med hele sider for nye rader, men vi vil fortsatt lese mer data enn nødvendig.
Forresten, hvis en helt tom side (den andre i vårt eksempel) var på slutten av filen, ville VACUUM kunne trimme den. Men nå er hun i midten, så ingenting kan gjøres med henne.

Postgres: bloat, pg_repack og deferred constraints

Når antallet slike tomme eller svært sparsomme sider blir stort, som kalles bloat, begynner det å påvirke ytelsen.

Alt beskrevet ovenfor er mekanikken for forekomsten av oppblåsthet i tabeller. I indekser skjer dette omtrent på samme måte.

Har jeg oppblåsthet?

Det er flere måter å finne ut om du har oppblåsthet. Ideen med den første er å bruke intern Postgres-statistikk, som inneholder omtrentlig informasjon om antall rader i tabeller, antall «live»-rader osv. Du kan finne mange varianter av ferdige skript på Internett. Vi tok utgangspunkt i manus fra PostgreSQL Experts, som kan evaluere oppblåstningstabeller sammen med toast og oppblåst btree-indekser. Etter vår erfaring er feilen 10-20%.

En annen måte er å bruke utvidelsen pgstattuple, som lar deg se inne på sidene og få både en estimert og en eksakt oppblåst verdi. Men i det andre tilfellet må du skanne hele tabellen.

Vi anser en liten oppblåst verdi, opptil 20 %, som akseptabel. Det kan betraktes som en analog av fillfactor for tabeller и indekser. Ved 50 % og over kan ytelsesproblemer begynne.

Måter å bekjempe oppblåsthet

Postgres har flere måter å håndtere oppblåsthet ut av esken på, men de passer ikke alltid for alle.

Konfigurer AUTOVACUUM slik at oppblåsthet ikke oppstår. Eller mer presist, for å holde det på et nivå som er akseptabelt for deg. Dette virker som "kapteinens" råd, men i virkeligheten er dette ikke alltid lett å oppnå. For eksempel har du aktiv utvikling med regelmessige endringer i dataskjemaet, eller det foregår en form for datamigrering. Som et resultat kan lasteprofilen din endres ofte og vil vanligvis variere fra tabell til tabell. Dette betyr at du hele tiden må jobbe litt fremover og justere AUTOVAKUUM til den skiftende profilen til hvert bord. Men dette er åpenbart ikke lett å gjøre.

En annen vanlig årsak til at AUTOVACUUM ikke kan holde tritt med tabeller er fordi det er langvarige transaksjoner som hindrer det i å rydde opp i dataene som er tilgjengelige for disse transaksjonene. Anbefalingen her er også åpenbar - bli kvitt "dinglende" transaksjoner og minimer tiden for aktive transaksjoner. Men hvis belastningen på applikasjonen din er en hybrid av OLAP og OLTP, kan du samtidig ha mange hyppige oppdateringer og korte spørringer, samt langsiktige operasjoner - for eksempel å bygge en rapport. I en slik situasjon er det verdt å tenke på å spre belastningen over forskjellige baser, noe som vil gi mulighet for mer finjustering av hver av dem.

Et annet eksempel - selv om profilen er homogen, men databasen er under en veldig høy belastning, kan selv den mest aggressive AUTOVACUUM ikke takle, og oppblåsthet vil oppstå. Skalering (vertikal eller horisontal) er den eneste løsningen.

Hva du skal gjøre i en situasjon der du har satt opp AUTOVACUUM, men oppblåstheten fortsetter å vokse.

Lag VAKUUM FULL gjenoppbygger innholdet i tabeller og indekser og lar bare relevante data ligge i dem. For å eliminere oppblåsthet fungerer det perfekt, men under utførelsen fanges en eksklusiv lås på bordet (AccessExclusiveLock), som ikke vil tillate å utføre spørringer på dette bordet, selv velger. Hvis du har råd til å stoppe tjenesten eller deler av den i en stund (fra titalls minutter til flere timer avhengig av størrelsen på databasen og maskinvaren), så er dette alternativet det beste. Vi har dessverre ikke tid til å kjøre VAKUUM FULL under det planlagte vedlikeholdet, så denne metoden passer ikke for oss.

Lag CLUSTER Gjenoppbygger innholdet i tabeller på samme måte som VACUUM FULL, men lar deg spesifisere en indeks som dataene skal fysisk bestilles på disk (men i fremtiden er ikke rekkefølgen garantert for nye rader). I visse situasjoner er dette en god optimalisering for en rekke spørringer - med lesing av flere poster etter indeks. Ulempen med kommandoen er den samme som ved VACUUM FULL - den låser bordet under drift.

Lag REINDEKS ligner på de to foregående, men gjenoppbygger en spesifikk indeks eller alle indeksene i tabellen. Låsene er litt svakere: ShareLock på bordet (hindrer modifikasjoner, men tillater valg) og AccessExclusiveLock på indeksen som gjenoppbygges (blokkerer spørringer som bruker denne indeksen). Imidlertid dukket det opp en parameter i den 12. versjonen av Postgres SAMTIDIGT, som lar deg gjenoppbygge indeksen uten å blokkere samtidig tillegg, modifikasjon eller sletting av poster.

I tidligere versjoner av Postgres kan du oppnå et resultat som ligner på REINDEX SAMTIDIG ved å bruke OPPRETT INDEKS SAMTIDIGT. Den lar deg lage en indeks uten streng låsing (ShareUpdateExclusiveLock, som ikke forstyrrer parallelle spørringer), deretter erstatte den gamle indeksen med en ny og slette den gamle indeksen. Dette lar deg eliminere indeksoppblåsthet uten å forstyrre applikasjonen din. Det er viktig å tenke på at når du gjenoppbygger indekser, vil det være en ekstra belastning på diskundersystemet.

Derfor, hvis det for indekser er måter å eliminere oppblåsthet "i farten", så er det ingen for tabeller. Det er her ulike eksterne utvidelser spiller inn: pg_repack (tidligere pg_reorg), pgcompact, pgcompacttable og andre. I denne artikkelen vil jeg ikke sammenligne dem og vil bare snakke om pg_repack, som vi etter en viss modifikasjon bruker selv.

Hvordan pg_repack fungerer

Postgres: bloat, pg_repack og deferred constraints
La oss si at vi har en helt vanlig tabell – med indekser, restriksjoner og dessverre med oppblåsthet. Det første trinnet i pg_repack er å lage en loggtabell for å lagre data om alle endringer mens den kjører. Utløseren vil replikere disse endringene for hver innsetting, oppdatering og sletting. Deretter opprettes en tabell, lik den opprinnelige i strukturen, men uten indekser og begrensninger, for ikke å bremse prosessen med å sette inn data.

Deretter overfører pg_repack dataene fra den gamle tabellen til den nye tabellen, filtrerer automatisk ut alle irrelevante rader, og lager deretter indekser for den nye tabellen. Under utførelsen av alle disse operasjonene akkumuleres endringer i loggtabellen.

Neste trinn er å overføre endringene til den nye tabellen. Migreringen utføres over flere iterasjoner, og når det er færre enn 20 oppføringer igjen i loggtabellen, får pg_repack en sterk lås, migrerer de siste dataene og erstatter den gamle tabellen med den nye i Postgres systemtabeller. Dette er den eneste og svært korte tiden du ikke vil kunne jobbe med bordet. Etter dette slettes den gamle tabellen og tabellen med logger og det frigjøres plass i filsystemet. Prosessen er fullført.

Alt ser bra ut i teorien, men hva skjer i praksis? Vi testet pg_repack uten belastning og under belastning, og sjekket funksjonen i tilfelle for tidlig stopp (med andre ord ved å bruke Ctrl+C). Alle testene var positive.

Vi dro til matbutikken - og så gikk ikke alt som vi forventet.

Første pannekake på salg

På den første klyngen mottok vi en feilmelding om brudd på en unik begrensning:

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

Denne begrensningen hadde et automatisk generert navn index_16508 - den ble opprettet av pg_repack. Basert på attributtene som er inkludert i sammensetningen, bestemte vi "vår" begrensning som tilsvarer den. Problemet viste seg å være at dette ikke er en helt ordinær begrensning, men en utsatt (utsatt begrensning), dvs. verifiseringen utføres senere enn sql-kommandoen, noe som fører til uventede konsekvenser.

Utsatte begrensninger: hvorfor de trengs og hvordan de fungerer

En liten teori om utsatte restriksjoner.
La oss vurdere et enkelt eksempel: vi har en tabellreferansebok med biler med to attributter - navnet og rekkefølgen på bilen i katalogen.
Postgres: bloat, pg_repack og deferred constraints

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



La oss si at vi trengte å bytte den første og andre bilen. Den enkle løsningen er å oppdatere den første verdien til den andre, og den andre til den første:

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

Men når vi kjører denne koden, forventer vi et begrensningsbrudd fordi rekkefølgen på verdiene i tabellen er unik:

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

Hvordan kan jeg gjøre det annerledes? Alternativ én: legg til en ekstra verdierstatning til en ordre som garantert ikke eksisterer i tabellen, for eksempel "-1". I programmering kalles dette "utveksling av verdiene til to variabler med en tredje." Den eneste ulempen med denne metoden er den ekstra oppdateringen.

Alternativ to: Redesign tabellen for å bruke en flyttallsdatatype for ordreverdien i stedet for heltall. Når du deretter oppdaterer verdien fra 1, for eksempel til 2.5, vil den første oppføringen automatisk "stå" mellom den andre og den tredje. Denne løsningen fungerer, men det er to begrensninger. For det første vil det ikke fungere for deg hvis verdien brukes et sted i grensesnittet. For det andre, avhengig av nøyaktigheten til datatypen, vil du ha et begrenset antall mulige innsettinger før du beregner verdiene til alle poster på nytt.

Alternativ tre: gjør begrensningen utsatt slik at den kun blir sjekket på tidspunktet for forpliktelsen:

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

Siden logikken i vår første forespørsel sikrer at alle verdier er unike på tidspunktet for forpliktelsen, vil den lykkes.

Eksemplet diskutert ovenfor er selvfølgelig veldig syntetisk, men det avslører ideen. I applikasjonen vår bruker vi utsatte begrensninger for å implementere logikk som er ansvarlig for å løse konflikter når brukere samtidig jobber med delte widgetobjekter på tavlen. Ved å bruke slike begrensninger kan vi gjøre applikasjonskoden litt enklere.

Generelt, avhengig av typen begrensning, har Postgres tre granularitetsnivåer for å kontrollere dem: rad-, transaksjons- og uttrykksnivåer.
Postgres: bloat, pg_repack og deferred constraints
Kilde: begriffs

CHECK og NOT NULL er alltid sjekket på radnivå; for andre restriksjoner, som det fremgår av tabellen, er det forskjellige alternativer. Du kan lese mer her.

For å kort oppsummere, gir utsatte begrensninger i en rekke situasjoner mer lesbar kode og færre kommandoer. Du må imidlertid betale for dette ved å komplisere feilsøkingsprosessen, siden det øyeblikket feilen oppstår og det øyeblikket du finner ut om det, skilles i tid. Et annet mulig problem er at planleggeren kanskje ikke alltid er i stand til å konstruere en optimal plan hvis forespørselen innebærer en utsatt begrensning.

Forbedring av pg_repack

Vi har dekket hva utsatte begrensninger er, men hvordan forholder de seg til problemet vårt? La oss huske feilen vi mottok tidligere:

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

Det oppstår når data kopieres fra en loggtabell til en ny tabell. Dette ser rart ut fordi... dataene i loggtabellen er forpliktet sammen med dataene i kildetabellen. Hvis de tilfredsstiller begrensningene i den opprinnelige tabellen, hvordan kan de bryte de samme begrensningene i den nye?

Som det viser seg, ligger roten til problemet i forrige trinn av pg_repack, som bare oppretter indekser, men ikke begrensninger: den gamle tabellen hadde en unik begrensning, og den nye opprettet en unik indeks i stedet.

Postgres: bloat, pg_repack og deferred constraints

Det er viktig å merke seg her at hvis begrensningen er normal og ikke utsatt, så er den unike indeksen som opprettes i stedet ekvivalent med denne begrensningen, fordi Unike begrensninger i Postgres implementeres ved å lage en unik indeks. Men i tilfelle av en utsatt begrensning, er oppførselen ikke den samme, fordi indeksen ikke kan utsettes og kontrolleres alltid på det tidspunktet sql-kommandoen utføres.

Dermed ligger essensen av problemet i "forsinkelsen" av sjekken: i den opprinnelige tabellen skjer det på tidspunktet for commit, og i den nye tabellen på det tidspunktet sql-kommandoen utføres. Dette betyr at vi må sørge for at kontrollene utføres på samme måte i begge tilfeller: enten alltid forsinket, eller alltid umiddelbart.

Så hvilke ideer hadde vi?

Lag en indeks som ligner på utsatt

Den første ideen er å utføre begge kontrollene i umiddelbar modus. Dette kan generere flere falske positive restriksjoner, men hvis det er få av dem, bør dette ikke påvirke brukernes arbeid, siden slike konflikter er en normal situasjon for dem. De oppstår for eksempel når to brukere begynner å redigere den samme widgeten samtidig, og klienten til den andre brukeren ikke har tid til å motta informasjon om at widgeten allerede er blokkert for redigering av den første brukeren. I en slik situasjon nekter serveren den andre brukeren, og klienten ruller tilbake endringene og blokkerer widgeten. Litt senere, når den første brukeren fullfører redigeringen, vil den andre motta informasjon om at widgeten ikke lenger er blokkert og vil kunne gjenta handlingen.

Postgres: bloat, pg_repack og deferred constraints

For å sikre at sjekker alltid er i ikke-utsatt modus, opprettet vi en ny indeks som ligner på den opprinnelige utsatte begrensningen:

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

I testmiljøet fikk vi bare noen få forventede feil. Suksess! Vi kjørte pg_repack igjen på produksjon og fikk 5 feil på den første klyngen på en times arbeid. Dette er et akseptabelt resultat. Men allerede på den andre klyngen økte antallet feil betydelig og vi måtte stoppe pg_repack.

Hvorfor skjedde det? Sannsynligheten for at en feil oppstår avhenger av hvor mange brukere som jobber med de samme widgetene samtidig. Tilsynelatende var det i det øyeblikket mye færre konkurransemessige endringer med dataene lagret på den første klyngen enn på de andre, dvs. vi var bare "heldige".

Ideen fungerte ikke. På det tidspunktet så vi to andre løsninger: omskriv applikasjonskoden vår for å unngå utsatte begrensninger, eller "lær" pg_repack å jobbe med dem. Vi valgte den andre.

Erstatt indekser i den nye tabellen med utsatte begrensninger fra den opprinnelige tabellen

Hensikten med revisjonen var åpenbar - hvis den opprinnelige tabellen har en utsatt begrensning, må du opprette en slik begrensning for den nye, og ikke en indeks.

For å teste endringene våre skrev vi en enkel test:

  • tabell med en utsatt begrensning og én post;
  • sette inn data i en løkke som er i konflikt med en eksisterende post;
  • gjør en oppdatering – dataene er ikke lenger i konflikt;
  • begå endringene.

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;

Den opprinnelige versjonen av pg_repack krasjet alltid ved første innsetting, den modifiserte versjonen fungerte uten feil. Flott.

Vi går til produksjon og får igjen en feil i samme fase av kopiering av data fra loggtabellen til en ny:

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

Klassisk situasjon: alt fungerer i testmiljøer, men ikke i produksjon?!

APPLY_COUNT og krysset mellom to grupper

Vi begynte å analysere koden bokstavelig talt linje for linje og oppdaget et viktig poeng: data overføres fra loggtabellen til en ny i batcher, APPLY_COUNT konstanten indikerte størrelsen på batchen:

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

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

Problemet er at dataene fra den opprinnelige transaksjonen, der flere operasjoner potensielt kan bryte med begrensningen, når de overføres, kan ende opp i krysset mellom to batcher - halvparten av kommandoene vil bli utført i den første batchen, og den andre halvparten i den andre. Og her, avhengig av flaksen din: Hvis lagene ikke bryter noe i den første batchen, er alt bra, men hvis de gjør det, oppstår det en feil.

APPLY_COUNT er lik 1000 poster, noe som forklarer hvorfor testene våre var vellykkede - de dekket ikke tilfellet med "batch junction". Vi brukte to kommandoer - inn og oppdater, så nøyaktig 500 transaksjoner av to kommandoer ble alltid plassert i en batch og vi opplevde ingen problemer. Etter å ha lagt til den andre oppdateringen, sluttet redigeringen vår å fungere:

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;

Så, neste oppgave er å sørge for at data fra den opprinnelige tabellen, som ble endret i en transaksjon, havner i den nye tabellen også innenfor en transaksjon.

Avslag fra batching

Og igjen hadde vi to løsninger. Først: la oss fullstendig forlate partisjonering i batcher og overføre data i én transaksjon. Fordelen med denne løsningen var dens enkelhet - de nødvendige kodeendringene var minimale (forresten, i eldre versjoner fungerte pg_reorg akkurat slik). Men det er et problem - vi oppretter en langvarig transaksjon, og dette er, som tidligere sagt, en trussel mot fremveksten av en ny oppblåsthet.

Den andre løsningen er mer kompleks, men sannsynligvis mer korrekt: lag en kolonne i loggtabellen med identifikatoren for transaksjonen som la data til tabellen. Deretter, når vi kopierer data, kan vi gruppere dem etter dette attributtet og sikre at relaterte endringer overføres sammen. Batchen vil bli dannet av flere transaksjoner (eller en stor) og størrelsen vil variere avhengig av hvor mye data som ble endret i disse transaksjonene. Det er viktig å merke seg at siden data fra ulike transaksjoner kommer inn i loggtabellen i en tilfeldig rekkefølge, vil det ikke lenger være mulig å lese dem sekvensielt, slik det var før. seqscan for hver forespørsel med filtrering etter tx_id er for dyrt, en indeks er nødvendig, men det vil også bremse metoden på grunn av overheaden med å oppdatere den. Generelt, som alltid, må du ofre noe.

Så vi bestemte oss for å starte med det første alternativet, da det er enklere. Først var det nødvendig å forstå om en lang transaksjon ville være et reelt problem. Siden hovedoverføringen av data fra den gamle tabellen til den nye også skjer i en lang transaksjon, ble spørsmålet forvandlet til "hvor mye vil vi øke denne transaksjonen?" Varigheten av den første transaksjonen avhenger hovedsakelig av størrelsen på bordet. Varigheten av en ny avhenger av hvor mange endringer som samler seg i tabellen under dataoverføringen, dvs. på intensiteten av belastningen. Pg_repack-kjøringen skjedde i en tid med minimal tjenestebelastning, og volumet av endringer var uforholdsmessig lite sammenlignet med den opprinnelige størrelsen på tabellen. Vi bestemte oss for at vi kunne neglisjere tidspunktet for en ny transaksjon (til sammenligning er det i gjennomsnitt 1 time og 2-3 minutter).

Forsøkene var positive. Lansering på produksjon også. For klarhet, her er et bilde med størrelsen på en av databasene etter kjøring:

Postgres: bloat, pg_repack og deferred constraints

Siden vi var helt fornøyd med denne løsningen, prøvde vi ikke å implementere den andre, men vi vurderer muligheten for å diskutere den med utvidelsesutviklerne. Vår nåværende revisjon er dessverre ennå ikke klar for publisering, siden vi bare løste problemet med unike utsatte restriksjoner, og for en fullverdig oppdatering er det nødvendig å gi støtte for andre typer. Vi håper å kunne gjøre dette i fremtiden.

Kanskje du har et spørsmål, hvorfor ble vi til og med involvert i denne historien med modifikasjonen av pg_repack, og brukte for eksempel ikke analogene? På et tidspunkt tenkte vi også på dette, men den positive opplevelsen av å bruke det tidligere, på tabeller uten utsatte begrensninger, motiverte oss til å prøve å forstå essensen av problemet og fikse det. I tillegg krever bruk av andre løsninger også tid til å gjennomføre tester, så vi bestemte oss for at vi først ville prøve å fikse problemet i det, og hvis vi innså at vi ikke kunne gjøre dette innen rimelig tid, så ville vi begynne å se på analoger .

Funn

Hva vi kan anbefale basert på vår egen erfaring:

  1. Overvåk oppblåstheten din. Basert på overvåkingsdata kan du forstå hvor godt autovakuum er konfigurert.
  2. Juster AUTOVAKUUM for å holde oppblåstheten på et akseptabelt nivå.
  3. Hvis oppblåstheten fortsatt vokser og du ikke kan overvinne den ved å bruke ferdige verktøy, ikke vær redd for å bruke eksterne utvidelser. Det viktigste er å teste alt godt.
  4. Ikke vær redd for å modifisere eksterne løsninger for å passe dine behov – noen ganger kan dette være mer effektivt og enda enklere enn å endre din egen kode.

Kilde: www.habr.com

Legg til en kommentar