Postgres: bloat, pg_repack e vincoli differiti

Postgres: bloat, pg_repack e vincoli differiti

L'effetto di rigonfiamento su tabelle e indici è ampiamente noto ed è presente non solo in Postgres. Esistono modi per gestirlo immediatamente, come VACUUM FULL o CLUSTER, ma bloccano le tabelle durante il funzionamento e quindi non possono essere sempre utilizzati.

L'articolo conterrà una piccola teoria su come si verifica il bloat, come combatterlo, sui vincoli differiti e sui problemi che comportano all'uso dell'estensione pg_repack.

Questo articolo è scritto sulla base di il mio discorso alla PgConf.Russia 2020.

Perché si verifica il gonfiore?

Postgres si basa su un modello multiversione (MVCC). La sua essenza è che ogni riga della tabella può avere più versioni, mentre le transazioni non vedono più di una di queste versioni, ma non necessariamente la stessa. Ciò consente a più transazioni di funzionare contemporaneamente e di non avere praticamente alcun impatto l'una sull'altra.

Ovviamente, tutte queste versioni devono essere archiviate. Postgres funziona con la memoria pagina per pagina e una pagina è la quantità minima di dati che può essere letta o scritta da un disco. Facciamo un piccolo esempio per capire come ciò avviene.

Supponiamo di avere una tabella alla quale abbiamo aggiunto diversi record. Nuovi dati sono apparsi nella prima pagina del file in cui è archiviata la tabella. Si tratta di versioni live di righe disponibili per altre transazioni dopo un commit (per semplicità, supponiamo che il livello di isolamento sia Read Commit).

Postgres: bloat, pg_repack e vincoli differiti

Abbiamo quindi aggiornato una delle voci, contrassegnando così la vecchia versione come non più rilevante.

Postgres: bloat, pg_repack e vincoli differiti

Passo dopo passo, aggiornando ed eliminando le versioni delle righe, ci siamo ritrovati con una pagina in cui circa la metà dei dati sono "spazzatura". Questi dati non sono visibili ad alcuna transazione.

Postgres: bloat, pg_repack e vincoli differiti

Postgres ha un meccanismo VUOTO, che elimina le versioni obsolete e fa spazio a nuovi dati. Ma se non è configurato in modo sufficientemente aggressivo o è occupato a lavorare in altre tabelle, rimangono "dati spazzatura" e dobbiamo utilizzare pagine aggiuntive per i nuovi dati.

Quindi nel nostro esempio, ad un certo punto la tabella sarà composta da quattro pagine, ma solo la metà conterrà dati in tempo reale. Di conseguenza, quando accediamo alla tabella, leggeremo molti più dati del necessario.

Postgres: bloat, pg_repack e vincoli differiti

Anche se VACUUM ora elimina tutte le versioni di riga irrilevanti, la situazione non migliorerà notevolmente. Avremo spazio libero in pagine o addirittura intere pagine per nuove righe, ma leggeremo comunque più dati del necessario.
A proposito, se alla fine del file ci fosse una pagina completamente vuota (la seconda nel nostro esempio), VACUUM sarebbe in grado di ritagliarla. Ma ora lei è nel mezzo, quindi non si può fare nulla con lei.

Postgres: bloat, pg_repack e vincoli differiti

Quando il numero di pagine vuote o molto sparse diventa elevato, fenomeno chiamato bloat, inizia a influire sulle prestazioni.

Tutto quanto sopra descritto è il meccanismo del verificarsi del gonfiore nelle tabelle. Negli indici ciò avviene più o meno allo stesso modo.

Ho gonfiore?

Esistono diversi modi per determinare se hai gonfiore. L'idea del primo è quella di utilizzare le statistiche interne di Postgres, che contengono informazioni approssimative sul numero di righe nelle tabelle, sul numero di righe "live", ecc. Su Internet puoi trovare molte varianti di script già pronti. Abbiamo preso come base copione dagli esperti PostgreSQL, che possono valutare tabelle bloat insieme agli indici toast e bloat btree. Secondo la nostra esperienza, il suo errore è del 10-20%.

Un altro modo è utilizzare l'estensione pgstattuple, che ti consente di guardare all'interno delle pagine e ottenere sia un valore stimato che un valore esatto. Ma nel secondo caso dovrai scansionare l'intera tabella.

Consideriamo accettabile un piccolo valore eccessivo, fino al 20%. Può essere considerato un analogo del fillfactor per tabulazione и indici. Al 50% e oltre potrebbero iniziare problemi di prestazioni.

Modi per combattere il gonfiore

Postgres ha diversi modi per gestire il gonfiore fuori dagli schemi, ma non sempre sono adatti a tutti.

Configurare AUTOVACUUM in modo che non si verifichi un rigonfiamento. O più precisamente, per mantenerlo a un livello accettabile per te. Sembra un consiglio da “capitano”, ma in realtà non è sempre facile da realizzare. Ad esempio, è in corso uno sviluppo attivo con modifiche regolari allo schema dei dati oppure è in corso una sorta di migrazione dei dati. Di conseguenza, il profilo di carico potrebbe cambiare frequentemente e in genere varierà da tavolo a tavolo. Ciò significa che è necessario lavorare costantemente un po' in anticipo e adattare AUTOVACUUM al profilo mutevole di ciascun tavolo. Ma ovviamente questo non è facile da fare.

Un altro motivo comune per cui AUTOVACUUM non riesce a tenere il passo con le tabelle è perché ci sono transazioni di lunga durata che gli impediscono di ripulire i dati disponibili per tali transazioni. Anche qui la raccomandazione è ovvia: eliminare le transazioni "pendenti" e ridurre al minimo il tempo delle transazioni attive. Ma se il carico sulla tua applicazione è un ibrido di OLAP e OLTP, puoi avere contemporaneamente molti aggiornamenti frequenti e query brevi, nonché operazioni a lungo termine, ad esempio la creazione di un report. In una situazione del genere, vale la pena pensare di distribuire il carico su diverse basi, il che consentirà una maggiore messa a punto di ciascuna di esse.

Un altro esempio: anche se il profilo è omogeneo, ma il database è sottoposto a un carico molto elevato, anche l'AUTOVACUUM più aggressivo potrebbe non farcela e si verificherà un rigonfiamento. Il ridimensionamento (verticale o orizzontale) è l'unica soluzione.

Cosa fare in una situazione in cui hai impostato AUTOVACUUM, ma il gonfiore continua a crescere.

Squadra VUOTO PIENO ricostruisce il contenuto di tabelle e indici e lascia al loro interno solo i dati rilevanti. Per eliminare il gonfiore, funziona perfettamente, ma durante la sua esecuzione viene catturato un blocco esclusivo sulla tabella (AccessExclusiveLock), che non consentirà l'esecuzione di query su questa tabella, nemmeno selezioni. Se puoi permetterti di interrompere il tuo servizio o parte di esso per un certo periodo (da decine di minuti a diverse ore a seconda delle dimensioni del database e del tuo hardware), allora questa opzione è la migliore. Purtroppo non abbiamo tempo per eseguire VACUUM FULL durante la manutenzione programmata, quindi questo metodo non è adatto a noi.

Squadra CLUSTER Ricostruisce il contenuto delle tabelle allo stesso modo di VACUUM FULL, ma permette di specificare un indice in base al quale i dati verranno ordinati fisicamente su disco (ma in futuro l'ordine non sarà garantito per le nuove righe). In alcune situazioni, questa è una buona ottimizzazione per un numero di query, con la lettura di più record per indice. Lo svantaggio del comando è lo stesso di VACUUM FULL: blocca il tavolo durante il funzionamento.

Squadra REINDICE simile ai due precedenti, ma ricostruisce un indice specifico o tutti gli indici della tabella. I blocchi sono leggermente più deboli: ShareLock sulla tabella (impedisce le modifiche, ma consente la selezione) e AccessExclusiveLock sull'indice in fase di ricostruzione (blocca le query che utilizzano questo indice). Tuttavia, nella dodicesima versione di Postgres è apparso un parametro IN CONCOMITANZA, che consente di ricostruire l'indice senza bloccare l'aggiunta, la modifica o l'eliminazione simultanea di record.

Nelle versioni precedenti di Postgres, puoi ottenere un risultato simile a REINDEX CONCORRENTLY utilizzando CREA INDICE CONTEMPORANEAMENTE. Ti consente di creare un indice senza blocco rigoroso (ShareUpdateExclusiveLock, che non interferisce con le query parallele), quindi sostituire il vecchio indice con uno nuovo ed eliminare il vecchio indice. Ciò ti consente di eliminare il rigonfiamento dell'indice senza interferire con la tua applicazione. È importante considerare che durante la ricostruzione degli indici ci sarà un carico aggiuntivo sul sottosistema del disco.

Pertanto, se per gli indici esistono modi per eliminare il gonfiore “al volo”, allora non ce ne sono per le tabelle. È qui che entrano in gioco varie estensioni esterne: pg_repack (precedentemente pg_reorg), pgcompact, pgcompacttable e altri. In questo articolo non li confronterò e parlerò solo di pg_repack, che, dopo qualche modifica, utilizziamo noi stessi.

Come funziona pg_repack

Postgres: bloat, pg_repack e vincoli differiti
Diciamo che abbiamo una tabella del tutto normale, con indici, restrizioni e, sfortunatamente, gonfiata. Il primo passo di pg_repack è creare una tabella di registro per archiviare i dati su tutte le modifiche durante l'esecuzione. Il trigger replicherà queste modifiche per ogni inserimento, aggiornamento ed eliminazione. Successivamente viene creata una tabella, simile a quella originale nella struttura, ma senza indici e vincoli, per non rallentare il processo di inserimento dei dati.

Successivamente, pg_repack trasferisce i dati dalla vecchia tabella alla nuova tabella, filtrando automaticamente tutte le righe irrilevanti e quindi crea gli indici per la nuova tabella. Durante l'esecuzione di tutte queste operazioni, le modifiche si accumulano nella tabella di registro.

Il passo successivo è trasferire le modifiche alla nuova tabella. La migrazione viene eseguita su diverse iterazioni e quando rimangono meno di 20 voci nella tabella di log, pg_repack acquisisce un blocco forte, migra i dati più recenti e sostituisce la vecchia tabella con quella nuova nelle tabelle di sistema Postgres. Questo è l'unico e brevissimo periodo in cui non sarai in grado di lavorare con il tavolo. Successivamente la vecchia tabella e la tabella con i log vengono cancellate e viene liberato spazio nel file system. Il processo è completo.

Tutto sembra fantastico in teoria, ma cosa succede nella pratica? Abbiamo testato pg_repack senza carico e sotto carico, e ne abbiamo verificato il funzionamento in caso di arresto prematuro (in altre parole, utilizzando Ctrl+C). Tutti i test sono risultati positivi.

Siamo andati al negozio di alimentari e non è andato tutto come ci aspettavamo.

Primo pancake in vendita

Sul primo cluster abbiamo ricevuto un errore relativo alla violazione di un vincolo univoco:

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

Questa limitazione aveva un nome generato automaticamente index_16508: è stata creata da pg_repack. In base agli attributi inclusi nella sua composizione, abbiamo determinato il “nostro” vincolo che gli corrisponde. Il problema si è rivelato che questa non è una limitazione del tutto ordinaria, ma differita (vincolo differito), cioè. la sua verifica viene eseguita successivamente al comando sql, il che porta a conseguenze inaspettate.

Vincoli differiti: perché sono necessari e come funzionano

Una piccola teoria sulle restrizioni differite.
Consideriamo un semplice esempio: abbiamo una tabella di riferimento delle auto con due attributi: il nome e l'ordine dell'auto nella directory.
Postgres: bloat, pg_repack e vincoli differiti

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



Diciamo che dovevamo scambiare la prima e la seconda macchina. La soluzione semplice è aggiornare il primo valore al secondo e il secondo al primo:

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

Ma quando eseguiamo questo codice, ci aspettiamo una violazione del vincolo perché l'ordine dei valori nella tabella è unico:

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

Come posso farlo diversamente? Opzione uno: aggiungere un ulteriore valore sostitutivo a un ordine di cui è garantito che non esista nella tabella, ad esempio "-1". Nella programmazione, questo si chiama “scambiare i valori di due variabili attraverso una terza”. L'unico inconveniente di questo metodo è l'aggiornamento aggiuntivo.

Opzione due: riprogettare la tabella per utilizzare un tipo di dati a virgola mobile per il valore dell'ordine anziché numeri interi. Quindi, quando si aggiorna il valore, ad esempio, da 1 a 2.5, la prima voce si “posiziona” automaticamente tra la seconda e la terza. Questa soluzione funziona, ma presenta due limitazioni. Innanzitutto, non funzionerà se il valore viene utilizzato da qualche parte nell'interfaccia. In secondo luogo, a seconda della precisione del tipo di dati, avrai un numero limitato di possibili inserimenti prima di ricalcolare i valori di tutti i record.

Opzione tre: rinviare il vincolo in modo che venga controllato solo al momento del commit:

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

Poiché la logica della nostra richiesta iniziale garantisce che tutti i valori siano univoci al momento del commit, l'operazione avrà esito positivo.

L'esempio discusso sopra è, ovviamente, molto sintetico, ma rivela l'idea. Nella nostra applicazione, utilizziamo vincoli differiti per implementare la logica responsabile della risoluzione dei conflitti quando gli utenti lavorano simultaneamente con oggetti widget condivisi sulla scheda. L'utilizzo di tali restrizioni ci consente di rendere il codice dell'applicazione un po' più semplice.

In generale, a seconda del tipo di vincolo, Postgres ha tre livelli di granularità per controllarli: livelli di riga, transazione ed espressione.
Postgres: bloat, pg_repack e vincoli differiti
Fonte: begriff

CHECK e NOT NULL vengono sempre controllati a livello di riga; per altre restrizioni, come si vede dalla tabella, ci sono diverse opzioni. Puoi leggere di più qui.

Per riassumere brevemente, i vincoli differiti in una serie di situazioni forniscono codice più leggibile e meno comandi. Tuttavia, devi pagare per questo complicando il processo di debug, poiché il momento in cui si verifica l'errore e il momento in cui lo scopri sono separati nel tempo. Un altro possibile problema è che lo scheduler potrebbe non essere sempre in grado di costruire un piano ottimale se la richiesta comporta un vincolo differito.

Miglioramento di pg_repack

Abbiamo spiegato cosa sono i vincoli differiti, ma come si collegano al nostro problema? Ricordiamo l'errore ricevuto in precedenza:

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

Si verifica quando i dati vengono copiati da una tabella di registro a una nuova tabella. Sembra strano perché... i dati nella tabella di log vengono salvati insieme ai dati nella tabella di origine. Se soddisfano i vincoli della tabella originale, come possono violare gli stessi vincoli in quella nuova?

A quanto pare, la radice del problema risiede nel passaggio precedente di pg_repack, che crea solo indici, ma non vincoli: la vecchia tabella aveva un vincolo univoco e quella nuova creava invece un indice univoco.

Postgres: bloat, pg_repack e vincoli differiti

È importante notare qui che se il vincolo è normale e non differito, allora l'indice univoco creato invece è equivalente a questo vincolo, perché I vincoli univoci in Postgres vengono implementati creando un indice univoco. Ma nel caso di un vincolo differito, il comportamento non è lo stesso, perché l'indice non può essere differito e viene sempre controllato nel momento in cui viene eseguito il comando sql.

Pertanto, l'essenza del problema sta nel “ritardo” del controllo: nella tabella originale avviene al momento del commit e nella nuova tabella al momento dell'esecuzione del comando sql. Ciò significa che dobbiamo assicurarci che i controlli vengano eseguiti allo stesso modo in entrambi i casi: o sempre in ritardo, o sempre immediatamente.

Quindi quali idee abbiamo avuto?

Crea un indice simile a quello differito

La prima idea è eseguire entrambi i controlli in modalità immediata. Ciò può generare diverse restrizioni false positive, ma se ce ne sono poche, ciò non dovrebbe influire sul lavoro degli utenti, poiché tali conflitti sono una situazione normale per loro. Si verificano, ad esempio, quando due utenti iniziano a modificare lo stesso widget contemporaneamente e il client del secondo utente non ha il tempo di ricevere informazioni che il widget è già bloccato per la modifica da parte del primo utente. In una situazione del genere, il server rifiuta il secondo utente e il suo client ripristina le modifiche e blocca il widget. Poco dopo, quando il primo utente avrà completato la modifica, il secondo riceverà l'informazione che il widget non è più bloccato e potrà ripetere la propria azione.

Postgres: bloat, pg_repack e vincoli differiti

Per garantire che gli assegni siano sempre in modalità non differita, abbiamo creato un nuovo indice simile al vincolo differito originale:

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

Nell'ambiente di test, abbiamo ricevuto solo pochi errori previsti. Successo! Abbiamo eseguito nuovamente pg_repack in produzione e abbiamo ottenuto 5 errori sul primo cluster in un'ora di lavoro. Questo è un risultato accettabile. Tuttavia, già nel secondo cluster il numero di errori è aumentato in modo significativo e abbiamo dovuto interrompere pg_repack.

Perchè è successo? La probabilità che si verifichi un errore dipende dal numero di utenti che lavorano contemporaneamente con gli stessi widget. Apparentemente in quel momento si verificarono molti meno cambiamenti competitivi con i dati archiviati nel primo cluster rispetto agli altri, ad es. siamo stati semplicemente “fortunati”.

L'idea non ha funzionato. A quel punto, abbiamo visto altre due soluzioni: riscrivere il codice della nostra applicazione per eliminare i vincoli differiti, oppure “insegnare” a pg_repack a lavorare con essi. Abbiamo scelto la seconda.

Sostituisci gli indici nella nuova tabella con vincoli differiti dalla tabella originale

Lo scopo della revisione era ovvio: se la tabella originale ha un vincolo posticipato, per quella nuova è necessario creare tale vincolo e non un indice.

Per testare le nostre modifiche, abbiamo scritto un semplice test:

  • tabella con vincolo differito e un record;
  • inserire dati in un loop che è in conflitto con un record esistente;
  • fare un aggiornamento – i dati non sono più in conflitto;
  • confermare le modifiche.

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;

La versione originale di pg_repack si bloccava sempre al primo inserimento, la versione modificata funzionava senza errori. Grande.

Andiamo in produzione e riceviamo nuovamente un errore nella stessa fase di copia dei dati dalla tabella di registro a una nuova:

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

Situazione classica: tutto funziona negli ambienti di test, ma non in produzione?!

APPLY_COUNT e la giunzione di due batch

Abbiamo iniziato ad analizzare il codice letteralmente riga per riga e abbiamo scoperto un punto importante: i dati vengono trasferiti dalla tabella di registro a una nuova in batch, la costante APPLY_COUNT indicava la dimensione del batch:

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

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

Il problema è che i dati della transazione originale, in cui diverse operazioni potrebbero potenzialmente violare il vincolo, una volta trasferiti, possono finire all'incrocio di due batch: metà dei comandi verrà impegnata nel primo batch e l'altra metà nel secondo. E qui, a seconda della fortuna: se le squadre non violano nulla nel primo lotto, allora va tutto bene, ma se lo fanno si verifica un errore.

APPLY_COUNT è pari a 1000 record, il che spiega perché i nostri test hanno avuto successo: non hanno coperto il caso di "giunzione batch". Abbiamo utilizzato due comandi: inserisci e aggiorna, quindi esattamente 500 transazioni di due comandi sono state sempre inserite in un batch e non abbiamo riscontrato alcun problema. Dopo aver aggiunto il secondo aggiornamento, la nostra modifica ha smesso di funzionare:

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;

Quindi, il compito successivo è assicurarsi che i dati della tabella originale, che è stata modificata in una transazione, finiscano anche nella nuova tabella all'interno di una transazione.

Rifiuto dal dosaggio

E ancora una volta avevamo due soluzioni. Primo: abbandoniamo completamente la partizione in batch e trasferiamo i dati in un'unica transazione. Il vantaggio di questa soluzione era la sua semplicità: le modifiche al codice richieste erano minime (a proposito, nelle versioni precedenti pg_reorg funzionava esattamente così). Ma c'è un problema: stiamo creando una transazione a lungo termine e questo, come detto in precedenza, rappresenta una minaccia per l'emergere di un nuovo rigonfiamento.

La seconda soluzione è più complessa, ma probabilmente più corretta: creare una colonna nella tabella di log con l'identificativo della transazione che ha aggiunto dati alla tabella. Quindi, quando copiamo i dati, possiamo raggrupparli in base a questo attributo e garantire che le modifiche correlate vengano trasferite insieme. Il batch sarà formato da più transazioni (o da una grande) e le sue dimensioni varieranno a seconda della quantità di dati modificati in queste transazioni. È importante notare che poiché i dati provenienti da transazioni diverse entrano nella tabella di log in ordine casuale, non sarà più possibile leggerli in sequenza, come avveniva prima. seqscan per ogni richiesta con filtro tramite tx_id è troppo costoso, è necessario un indice, ma rallenterà anche il metodo a causa del sovraccarico di aggiornamento. In generale, come sempre, devi sacrificare qualcosa.

Quindi, abbiamo deciso di iniziare con la prima opzione, poiché è più semplice. Innanzitutto era necessario capire se una transazione lunga sarebbe stata un vero problema. Poiché anche il trasferimento principale dei dati dalla vecchia tabella a quella nuova avviene in un'unica lunga transazione, la domanda si è trasformata in "quanto aumenteremo questa transazione?" La durata della prima transazione dipende principalmente dalla dimensione della tabella. La durata di una nuova dipende da quante modifiche si accumulano nella tabella durante il trasferimento dei dati, ad es. dall'intensità del carico. L'esecuzione di pg_repack è avvenuta durante un periodo di carico minimo del servizio e il volume delle modifiche era sproporzionatamente piccolo rispetto alla dimensione originale della tabella. Abbiamo deciso che potremmo trascurare il tempo di una nuova transazione (per confronto, in media è 1 ora e 2-3 minuti).

Gli esperimenti sono stati positivi. Avvio anche della produzione. Per chiarezza, ecco un'immagine con le dimensioni di uno dei database dopo l'esecuzione:

Postgres: bloat, pg_repack e vincoli differiti

Poiché siamo rimasti completamente soddisfatti di questa soluzione, non abbiamo provato ad implementare la seconda, ma stiamo valutando la possibilità di discuterne con gli sviluppatori dell'estensione. La nostra revisione attuale, sfortunatamente, non è ancora pronta per la pubblicazione, poiché abbiamo risolto il problema solo con restrizioni differite uniche e per una patch a tutti gli effetti è necessario fornire supporto per altri tipi. Speriamo di poterlo fare in futuro.

Forse hai una domanda: perché siamo stati coinvolti in questa storia con la modifica di pg_repack e, ad esempio, non abbiamo utilizzato i suoi analoghi? Ad un certo punto abbiamo pensato anche a questo, ma l'esperienza positiva di averlo utilizzato prima, su tavoli senza vincoli differiti, ci ha motivato a cercare di capire l'essenza del problema e risolverlo. Inoltre, l'utilizzo di altre soluzioni richiede tempo per condurre i test, quindi abbiamo deciso che avremmo prima provato a risolvere il problema e, se ci fossimo resi conto che non potevamo farlo in un tempo ragionevole, avremmo iniziato a guardare gli analoghi .

risultati

Cosa possiamo consigliare in base alla nostra esperienza:

  1. Controlla il tuo gonfiore. Sulla base dei dati di monitoraggio, puoi capire quanto bene è configurato l'autovacuum.
  2. Regola l'AUTOVACUUM per mantenere il gonfiore a un livello accettabile.
  3. Se il gonfiore è ancora in crescita e non riesci a superarlo utilizzando strumenti pronti all'uso, non aver paura di utilizzare estensioni esterne. La cosa principale è testare tutto bene.
  4. Non aver paura di modificare soluzioni esterne per adattarle alle tue esigenze: a volte questo può essere più efficace e persino più semplice che modificare il tuo codice.

Fonte: habr.com

Aggiungi un commento