Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

Vi invito a prendere visione della trascrizione della presentazione di inizio 2016 di Andrei Sal'nikov "Errori comuni nelle applicazioni che portano a bloat in PostgreSQL"

In questa presentazione, analizzerò gli errori principali nelle applicazioni che si verificano durante la fase di progettazione e scrittura del codice. Mi concentrerò solo su quegli errori che portano a bloat in PostgreSQL. Di norma, questo segna l'inizio della fine delle prestazioni del vostro sistema complessivo, anche se inizialmente non sembrava esserci alcuna indicazione di ciò.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

Sono lieto di darvi il benvenuto! Questa presentazione non è così tecnica come quella precedente del mio collega. È rivolta principalmente agli sviluppatori di sistemi backend, dato che abbiamo un numero abbastanza elevato di clienti. E tutti loro commettono gli stessi errori. Di questi parlerò. Spiegherò quali conseguenze fatali e negative derivano da tali errori.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

Perché si commettono errori? Gli errori si verificano per due motivi: per incuria, pensando 'forse andrà bene' e per la mancanza di conoscenza di alcuni meccanismi che si verificano a livello tra il database e l'applicazione, così come all'interno del database stesso.

Vi presenterò tre esempi con immagini terribili di come tutto sia andato male. In breve, spiegherò il meccanismo che sta dietro a questi problemi. E come affrontarli quando si presentano, e quali metodi preventivi utilizzare per evitare errori. Parlerò di strumenti utili e fornirò link interessanti.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

Ho usato un database di test, dove avevo due tabelle. Una tabella con le fatture dei clienti, l'altra con le operazioni su queste fatture. E con una certa periodicità aggiorniamo i saldi su queste fatture.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

Dati iniziali della tabella: è piuttosto piccola, 2 MB. Il tempo di risposta del database e in particolare della tabella è molto buono. E un carico piuttosto elevato – 2.000 operazioni al secondo sulla tabella.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

E lungo questa presentazione vi mostrerò grafici, per rendere evidente cosa stia accadendo. Ci saranno sempre 2 diapositive con grafici. La prima diapositiva mostra quello che accade complessivamente sul server.

In questa situazione vediamo che effettivamente abbiamo una tabella di piccole dimensioni. L'indice è piccolo, di 2 MB. Questo è il primo grafico a sinistra.

Il tempo medio di risposta del server è anche stabile e ridotto. Questo è il grafico in alto a destra.

Il grafico in basso a sinistra mostra le transazioni più lunghe. Possiamo vedere che le transazioni vengono eseguite rapidamente. E l'autovacuum non sta ancora funzionando, perché questo è stato solo un test iniziale. Successivamente funzionerà e sarà utile per noi.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

La seconda diapositiva sarà sempre dedicata alla tabella in esame. In questa situazione, aggiorniamo costantemente i saldi sui conti dei clienti. Possiamo vedere che il tempo medio di risposta per l'operazione di aggiornamento è piuttosto buono, meno di un millisecondo. Possiamo notare che le risorse della CPU (questo è il grafico in alto a destra) vengono consumate in modo uniforme e sono abbastanza basse.

Il grafico in basso a destra mostra quanta memoria operativa e di disco stiamo utilizzando mentre cerchiamo la riga che ci serve, prima di aggiornarla. E il numero di operazioni sulla tabella è di 2.000 al secondo, come ho già detto prima.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

E ora si verifica una tragedia. Per qualche motivo, si presenta una transazione lunga e dimenticata. Di solito, le ragioni sono piuttosto banali:

  • Una delle più comuni è che nel codice dell'applicazione abbiamo iniziato a contattare un servizio esterno. E questo servizio non risponde. Cioè, abbiamo aperto una transazione, apportato delle modifiche al database e ci siamo messi a controllare la posta o a utilizzare un altro servizio della nostra infrastruttura, e per qualche motivo non riceviamo risposta. E la nostra sessione è rimasta in uno stato di attesa, non sapendo quando si risolverà.
  • La seconda situazione è quella in cui nel codice si è verificata un'eccezione per qualche motivo. E non abbiamo gestito la chiusura della transazione nell'eccezione. Così abbiamo una sessione bloccata con una transazione aperta.
  • E infine, un altro caso piuttosto comune è il codice di bassa qualità. Alcuni framework aprono una transazione. Questa rimane aperta, e potresti non sapere che è attiva nell'applicazione.

A cosa portano queste situazioni?

Portano al fatto che le tabelle e gli indici iniziano a espandersi bruscamente. Questo è esattamente l'effetto bloat. Per il database, questo si tradurrà in un notevole aumento del tempo di risposta, e un aumento del carico sul server del database. Di conseguenza, l'applicazione ne risentirà. Perché se nel codice impiegavi 10 millisecondi per una richiesta al database e 10 millisecondi per la tua logica, la tua funzione si eseguiva in 20 millisecondi. Ma ora la tua situazione sarà molto più critica.

E vediamo cosa sta succedendo. Il grafico in basso a sinistra mostra che abbiamo una transazione lunga. Se guardiamo al grafico in alto a sinistra, vediamo che la dimensione della tabella è passata da due megabyte a ben 300 megabyte. Tuttavia, la quantità di dati nella tabella non è cambiata, il che significa che c'è una notevole quantità di dati inutili.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

Anche la situazione riguardante il tempo medio di risposta del server è cambiata drasticamente. Tutte le richieste al server hanno cominciato a rallentare notevolmente. Inoltre, sono stati avviati processi interni di Postgres, come l’autovacuum, che tentano di eseguire operazioni e consumano risorse.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

Cosa sta succedendo alla nostra tabella? La situazione è simile. Il tempo medio di risposta per la tabella è aumentato notevolmente. Se osserviamo i consumi di risorse, notiamo un forte aumento del carico sulla CPU. Questo è mostrato nel grafico in alto a destra. È aumentato perché la CPU deve esaminare un gran numero di righe inutili nella ricerca di quella necessaria. Questo è illustrato nel grafico in basso a destra. Di conseguenza, il numero di chiamate al secondo ha cominciato a diminuire drasticamente, poiché il database non riesce a gestire lo stesso numero di richieste.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

Dobbiamo tornare a una situazione normale. Ci informiamo online e scopriamo che le transazioni lunghe causano problemi. Identifichiamo e interrompiamo questa transazione. E le cose tornano a funzionare normalmente. Tutto riprende a funzionare come dovrebbe.

Ci siamo calmati, ma dopo un po' iniziamo a notare che l'applicazione non funziona come prima dell'interruzione. Le richieste continuano a essere elaborate più lentamente, significativamente più lentamente. Nella mia esperienza specifica, il rallentamento è stato di circa il 50-100%. Il carico sul server è anche superiore a quello che era prima dell'interruzione.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

E la domanda è: «Cosa sta succedendo al database in questo momento?». Ecco cosa accade. Nel grafico delle transazioni vedete che è fermo e in effetti non ci sono transazioni lunghe. Ma la dimensione della tabella è aumentata drammaticamente durante l'interruzione e non è diminuita da allora. Il tempo medio del database si è stabilizzato. E le risposte sembrano avere una velocità accettabile per noi. L’autovacuum è diventato più attivo e ha iniziato a elaborare una maggiore quantità di dati.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

Per quanto riguarda la tabella dei conti, dove stiamo modificando i saldi: il tempo di risposta per le richieste sembra tornare alla normalità. Ma in realtà è aumentato di circa il 50%.

Per quanto riguarda il carico sulla CPU, vediamo che non è tornato ai livelli desiderati pre-interruzione. Le ragioni si possono rintracciare nel grafico in basso a destra, dove si osserva l'uso di una certa quantità di memoria. Per cercare la riga corretta, stiamo sprecando risorse del server Database nel setacciare dati inutili. Il numero di transazioni al secondo si è stabilizzato.

Insomma, va bene, ma la situazione è peggiore di prima. C'è una chiara degradazione del database come conseguenza della nostra applicazione che interagisce con esso.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

Per capire cosa sta succedendo, se non eravate presenti alla presentazione precedente, ecco un po' di teoria. Teoria sul processo interno. Perché esiste l’autovacuum e cosa fa?

In sintesi, in un certo momento abbiamo una tabella. La tabella contiene righe. Queste righe possono essere attive, vive, necessarie al momento. Nell'immagine sono contrassegnate in verde. Ci sono anche righe morte, che hanno già eseguito il loro compito, sono state aggiornate e hanno nuovi record. Queste righe sono contrassegnate come non più rilevanti per il database, ma rimangono nella tabella a causa delle peculiarità di Postgres.

Perché è necessario l’autovacuum? A un certo punto, l’autovacuum accede al database e chiede: «Per favore, dammi l'id della transazione più vecchia attualmente aperta nel database». Il database restituisce questo id. Basandosi su questo, l’autovacuum esamina le righe nella tabella e se vede che alcune righe sono state modificate da transazioni molto più vecchie, ha il diritto di contrassegnarle come righe che possiamo riutilizzare in futuro, scrivendo nuovi dati al loro interno. Questo è un processo in background.

Nel frattempo, continuiamo a lavorare con il database, apportando delle modifiche alle tabelle. E a queste righe, che possiamo riutilizzare, scriviamo nuovi dati. In questo modo, abbiamo un ciclo continuo: continuamente si formano righe morte e vecchie, e sopra di esse scriviamo nuove righe necessarie. Questo è uno stato normale per il funzionamento di PostgreSQL.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

Cosa è successo durante l'interruzione? Come si è svolto questo processo?

Abbiamo avuto una tabella in uno stato vario, con alcune righe vive e altre morte. È intervenuto l'auto-vacuum. Ha chiesto al database qual è la nostra transazione più antica e qual è il suo id. Ha ottenuto questo id, che può risalire a ore fa o a dieci minuti fa. Questo dipende da quanto è alta la carico nel tuo database. E ha iniziato a cercare righe che potesse contrassegnare come riutilizzabili. Ma non ha trovato righe in nostra tabella.

Ma nel frattempo continuiamo a lavorare con la tabella. Stiamo modificando, aggiornando i dati. E cosa può fare il database in quel momento? Non ha altra scelta che scrivere nuove righe alla fine della tabella esistente. Così, la dimensione della tabella comincia ad aumentare.

In realtà abbiamo bisogno di righe verdi per lavorare. Ma durante un problema di questo tipo si verifica che la percentuale di righe verdi è molto bassa in tutta la tabella.

E quando eseguiamo una query, il database deve scorrere tutte le righe: sia quelle rosse che quelle verdi, per trovare la riga giusta. L'effetto dell'ingrossamento della tabella con dati inutili si chiama «bloat», che consuma anche il nostro spazio su disco. Ricordi, era 2 MB e ora è 300 MB? Ora cambia megabyte in gigabyte e perderai rapidamente tutte le tue risorse di disco.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

Quali possono essere le conseguenze per noi?

  • Nel mio esempio, la tabella e l'indice sono cresciuti 150 volte. Alcuni dei nostri clienti hanno vissuto casi più gravi, dove lo spazio su disco ha cominciato a scarseggiare.
  • La dimensione delle tabelle non diminuirà mai da sola. L'auto-vacuum in alcuni casi può tagliare la coda della tabella, se ci sono solo righe morte. Ma poiché avviene un costante ricambio, una riga verde può rimanere sospesa alla fine e non essere aggiornata, mentre tutte le altre saranno scritte all'inizio della tabella. Ma questo è un evento tanto improbabile che non si può contare su una riduzione naturale della dimensione della tabella.
  • Il database deve scandire un accumulo di righe inutili. E noi stiamo consumando risorse di disco, risorse della CPU e energia.
  • E questo influisce direttamente sulla nostra applicazione, perché se inizialmente impiegavamo 10 millisecondi per una query e 10 millisecondi per il nostro codice, durante l'emergenza abbiamo cominciato a impiegare un secondo per la query e 10 millisecondi per il codice, cioè la prestazione dell'applicazione è diminuita di un ordine di grandezza. E quando l'emergenza è stata risolta, abbiamo cominciato a spendere 20 millisecondi per la query e 10 millisecondi per il codice. Significa che abbiamo comunque perso un 50% di prestazione. E tutto questo a causa di una transazione che è rimasta sospesa, forse per nostra colpa.
  • E la domanda è: «Come possiamo tornare indietro?», affinché tutto funzioni di nuovo e le query siano veloci come prima dell'emergenza.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

Per questo esiste un certo ciclo di lavori da eseguire.

Innanzitutto, dobbiamo identificare le tabelle problematiche che si sono gonfiate. Comprendiamo che per alcune tabelle le scritture avvengono più attivamente, per altre meno. E per questo si utilizza l'estensione pgstattuple. Installando questa estensione, puoi scrivere query che ti aiuteranno a individuare le tabelle che si sono gonfiate significativamente.

Dopo aver trovato queste tabelle, è necessario comprimerle. Per questo ci sono già strumenti. Nella nostra azienda utilizziamo tre strumenti. Il primo è il VACUUM FULL integrato. È severo, duro e spietato, ma a volte è molto utile. Pg_repack e pgcompacttable sono utilità di terze parti per comprimere le tabelle. E trattano il database in modo più delicato.

Vengono utilizzati a seconda di cosa ti risulta più comodo. Ma di questo parlerò alla fine. L'importante è che ci siano tre strumenti. Hai delle scelte.

Dopo aver sistemato tutto e aver verificato che sia andato tutto bene, dobbiamo sapere come prevenire questa situazione in futuro:

  • È abbastanza facile prevenirla. È necessario monitorare la durata delle sessioni sul Master-server. Le sessioni particolarmente pericolose sono in stato di idle in transaction. Queste sono quelle in cui hanno aperto una transazione, hanno fatto qualcosa e se ne sono andati o sono semplicemente rimaste sospese, perdersi nel codice.
  • E per voi, come sviluppatori, è importante testare il codice al momento di queste situazioni. Non è difficile farlo. Sarà un controllo utile. Eviterai un gran numero di problemi «infantili» legati a transazioni lunghe.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

In questi grafici volevo mostrarti come è cambiata la tabella e il comportamento del database dopo che ho eseguito un VACUUM FULL su questa tabella. Questo non è in produzione.

La dimensione della tabella è tornata rapidamente nella norma, a pochi megabyte. Questo non ha avuto un grande impatto sul tempo medio di risposta del server.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

Tuttavia, per la nostra tabella in esame, dove aggiornavamo i saldi, notiamo che il tempo medio di risposta per le query di aggiornamento dei dati è diminuito a livelli pre-crisi. Anche le risorse richieste dal processore per eseguire questa query sono tornate a livelli pre-crisi. Il grafico in basso a destra mostra che ora troviamo esattamente la riga necessaria senza dover sfogliare un insieme di righe inutili presenti prima della compressione della tabella. Il tempo medio delle query è rimasto più o meno allo stesso livello. Tuttavia, questo potrebbe essere più un errore del mio hardware.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

Questa è la prima storia. È la più comune e può capitare a chiunque, indipendentemente dall'esperienza del cliente o dalla competenza dei programmatori. Prima o poi succede.

La seconda storia riguarda come distribuiamo il carico e ottimizziamo le risorse del server.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

  • Siamo già cresciuti e ci siamo affermati. Comprendiamo che abbiamo una replica e sarebbe utile bilanciare il carico: scriviamo sul master e leggiamo dalla replica. Di solito, questa situazione si verifica quando vogliamo generare rapporti o ETL. E questo fa molto piacere al business. Vuole rapporti vari con una complessa analisi.
  • I rapporti richiedono ore, poiché un’analisi complessa non può essere elaborata in millisecondi. Noi, come bravi ragazzi, scriviamo codice. Effettuiamo inserimenti nell'applicazione, registrando i dati sul master e generando i rapporti sulla replica.
  • Distribuiamo il carico.
  • Tutto funziona alla perfezione. Siamo bravi.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

E come appare questa situazione? Sui grafici ho anche aggiunto la durata delle transazioni dalla replica. Tutti gli altri grafici riguardano solo il master server.

Nel frattempo, la mia tabella dei rapporti è cresciuta. Ce ne sono di più. Notiamo che il tempo medio di risposta del server è stabile. Vedo che sulla replica abbiamo una transazione lunga che dura 2 ore. Vedo anche il funzionamento tranquillo dell'autovacuum che elabora le righe morte. E va tutto bene.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

In particolare, per la tabella in esame, continuiamo ad aggiornare i saldi. Anche noi abbiamo un tempo di risposta stabile per la query e un consumo di risorse costante. Tutto è a posto.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

Tutto va bene finché questi rapporti non iniziano a causare conflitti con la replica. E i conflitti si verificano con una certa periodicità.

Ci rivolgiamo a Internet e iniziamo a leggere perché questo sta accadendo. E troviamo una soluzione.

La prima soluzione è aumentare la latenza della replica. Sappiamo che il nostro rapporto richiede 3 ore. Impostiamo la latenza della replica a 3 ore. Avviamo il tutto, ma continuiamo a riscontrare problemi con rapporti che a volte falliscono.

Vogliamo che tutto sia perfetto. Approfondiamo la ricerca e troviamo una fantastica impostazione su internet: hot_standby_feedback. La attiviamo. L'hot_standby_feedback permette di ritardare l'operazione dell'autovacuum sul master. In questo modo, ci liberiamo completamente dai conflitti di replica e tutto funziona bene con i rapporti.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

E cosa succede nel frattempo con il master server? Il master server è in crisi totale. In questo momento stiamo osservando i grafici, quando ho attivato entrambe queste impostazioni. Vediamo che la sessione sulla replica, in qualche modo, ha iniziato a influire sulla situazione del master server. Di fatto influisce, poiché ha sospeso l'autovacuum che elimina le righe morte. La dimensione della tabella è nuovamente schizzata in alto. Anche il tempo medio di esecuzione delle query sull'intero database è aumentato. Gli autovacuum sono tornati a essere un po' stressati.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

In particolare, nella nostra tabella vediamo che l'aggiornamento dei dati è anch'esso schizzato in alto. Il consumo delle risorse della CPU è aumentato notevolmente. Dobbiamo nuovamente esaminare un gran numero di righe morte e inutili. E il tempo di risposta per questa tabella e il numero di transazioni sono diminuiti.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

Come apparirà questa situazione se non sappiamo di cosa stavo parlando prima?

  • Iniziamo a cercare problemi. Se abbiamo avuto problemi nella prima parte, sappiamo che potrebbe esserci una causa in una transazione lunga e andiamo sul master. Il problema è sul master. Viene sovraccaricato. Si sta surriscaldando, con un carico medio che sfiora il centinaio.
  • Le query rallentano, ma non vediamo transazioni lunghe. E non capiamo qual è il problema. Non riusciamo a capire dove cercare.
  • Controlliamo l'hardware del server. Potrebbe essersi rotto il RAID. Potrebbe esserci un banco di memoria guasto. Può succedere di tutto. Ma no, i server sono nuovi, tutto funziona benissimo.
  • Tutti corrono: amministratori, sviluppatori e il direttore. Niente sembra aiutare.
  • E a un certo punto, inspiegabilmente, tutto inizia a sistemarsi da solo.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

Sulla replica, nel frattempo, la richiesta è stata elaborata ed è andata. Abbiamo ricevuto il rapporto. L'attività continua a essere soddisfacente. Come possiamo vedere, la nostra tabella è di nuovo cresciuta e non sembra avere intenzione di ridursi. Nel grafico delle sessioni, ho lasciato un frammento di questa lunga transazione dalla replica, così potete valutare quanto tempo ci vuole affinché la situazione si stabilizzi.

La sessione è andata. E solo dopo un po' di tempo il server torna più o meno in ordine. E il tempo medio di risposta alle richieste sul Master-server torna alla normalità. Perché finalmente l'autovacuum ha avuto la possibilità di pulire, contrassegnare queste righe morte. E ha iniziato a fare il suo lavoro. E tanto più velocemente lo fa, tanto più rapidamente torneremo alla normalità.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

Per la tabella in cui aggiorniamo i saldi dei conti, osserviamo esattamente la stessa situazione. Anche il tempo medio di aggiornamento del conto si normalizza gradualmente. Le risorse utilizzate dal processore diminuiscono. E il numero di transazioni al secondo torna alla normalità. Ma di nuovo alla normalità, non come era prima dell'incidente.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

In ogni caso, dobbiamo aspettarci un calo delle prestazioni come nel primo caso, da un una volta e mezza a due volte, e talvolta anche di più.

Sembra che abbiamo fatto tutto correttamente. Abbiamo distribuito il carico. L'hardware non è inattivo. Abbiamo suddiviso le richieste in modo intelligente, ma comunque non è andata come sperato.

  • Disattivare hot_standby_feedback? Sì, in effetti, non è consigliato attivarlo senza una valida motivazione. Perché questo parametro influisce direttamente sul Master-server e sospende il funzionamento dell'autovacuum lì. Se lo attivi su qualche replica e poi te ne dimentichi, potresti danneggiare il Master e avere problemi seri con l'applicazione.
  • Aumentare max_standby_streaming_delay? Sì, per i rapporti – è così. Se hai un rapporto di tre ore e non vuoi che fallisca a causa dei conflitti di replica, puoi semplicemente aumentare il ritardo. Un rapporto lungo non richiede mai dati che sono stati appena inseriti nel database. Se è un rapporto di tre ore, significa che lo stai eseguendo su un dato di un periodo anteriore. E per te, tre o sei ore di ritardo non fanno alcuna differenza, ma così garantirai rapporti stabili senza problemi di fallimenti.
  • Naturalmente, è necessario monitorare le sessioni lunghe sulle repliche, specialmente se hai deciso di attivare hot_standby_feedback su una replica. Perché può succedere qualsiasi cosa. Hai dato quella replica a uno sviluppatore perché testasse delle richieste. Ha scritto una richiesta complessa. L'ha eseguita e se n'è andato a bere un tè, mentre noi abbiamo avuto un Master sovraccarico. O abbiamo inserito l'applicazione sbagliata. Le situazioni sono varie. Le sessioni sulle repliche devono essere monitorate con la stessa attenzione che si riserva a quelle sul Master.
  • E se hai richieste veloci e lunghe sulle repliche, in questo caso è meglio suddividerle per distribuire il carico. Questo è un riferimento al streaming_delay. Per le richieste veloci, avere una replica con un piccolissimo ritardo nella replica. Per le richieste di rapporti lunghi, avere una replica che può ritardare di 6 ore o persino un giorno. È una situazione del tutto normale.

Eliminiamo le conseguenze nello stesso modo:

  • Troviamo le tabelle gonfiate.
  • E comprimiamo con lo strumento più comodo che abbiamo a disposizione.

Questa storia è finita qui. Passiamo alla terza storia.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

Anche questa è piuttosto comune per noi, in cui eseguiamo una migrazione.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

  • Qualsiasi prodotto software cresce. I requisiti cambiano. Vogliamo svilupparci in ogni caso. E a volte è necessario aggiornare i dati in una tabella, effettuando un aggiornamento per la nostra migrazione rispetto alla nuova funzionalità che stiamo introducendo nel nostro percorso di sviluppo.
  • Il vecchio formato dei dati non è soddisfacente. Supponiamo di fare riferimento alla seconda tabella, dove ho operazioni su questi conti. E supponiamo che erano in rubli, ma abbiamo deciso di aumentare la precisione e rappresentarli in copechi. E per questo dobbiamo eseguire un aggiornamento: moltiplicare il campo con l'importo dell'operazione per cento.
  • Nel mondo moderno utilizziamo strumenti automatizzati per il controllo delle versioni del database. Supponiamo che Liquibase. Registriamo la nostra migrazione lì. La testiamo sul nostro database di prova. Tutto va bene. L'aggiornamento viene eseguito. Blocca l'operazione per un breve periodo, ma così otteniamo dati aggiornati. E possiamo attivare nuove funzionalità su questo. Abbiamo testato tutto, verificato. Tutto confermato.
  • Abbiamo eseguito lavori pianificati e completato la migrazione.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

Ecco la migrazione con l'aggiornamento presentata davanti a voi. Poiché si trattava delle mie operazioni contabili, la tabella era di 15 GB. E poiché stiamo aggiornando ogni riga, abbiamo raddoppiato la tabella, perché abbiamo riscritto ogni riga.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

Durante la migrazione non abbiamo potuto fare nulla con questa tabella, poiché tutte le richieste ad essa sono state messe in coda, in attesa che questo aggiornamento terminasse. Ma vorrei richiamare la vostra attenzione sui numeri sull'asse verticale. Cioè, abbiamo un tempo medio di richiesta prima della migrazione di circa 5 millisecondi e il carico della CPU, il numero di operazioni di lettura da disco è inferiore a 7,5.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

Abbiamo completato la migrazione e abbiamo di nuovo problemi.

La migrazione è stata eseguita con successo, ma:

  • La funzionalità precedente ha iniziato a funzionare più lentamente.
  • La tabella è di nuovo cresciuta in dimensioni.
  • Il carico del server è di nuovo aumentato rispetto a prima.
  • E, ovviamente, stiamo ancora lavorando su quella funzionalità che funzionava bene, l'abbiamo migliorata un po'.

E questo è di nuovo bloat, che ci crea ulteriori problemi.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

Qui dimostro che la tabella, come nei casi precedenti, non ha intenzione di tornare alle dimensioni precedenti. Il carico medio sul server sembra essere adeguato.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

E se guardiamo la tabella delle fatture, vediamo che il tempo medio di richiesta per quella tabella è raddoppiato. Il carico della CPU e il numero di righe esaminate in memoria sono saltati oltre 7,5, mentre erano inferiori. E sono aumentati nel caso dei processori di 2 volte, nel caso delle operazioni di lettura di 1,5 volte, cioè abbiamo ottenuto una degradazione delle prestazioni del server. E di conseguenza, una degradazione delle prestazioni della nostra applicazione. Anche se il numero di chiamate è rimasto all'incirca allo stesso livello.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

È fondamentale capire come effettuare correttamente tali migrazioni. E devono essere fatte. Eseguiamo queste migrazioni piuttosto frequentemente.

  • Tali grandi migrazioni non vengono fatte automaticamente. Devono sempre essere sotto controllo.
  • È necessario un controllo da parte di una persona competente. Se avete un DBA nel team, lasciate che sia lui a farlo. È il suo lavoro. Se non c'è, la persona più esperta dovrebbe farlo, quella che sa come lavorare con i database.
  • Una nuova struttura del database, anche se aggiorniamo solo una colonna, la prepariamo sempre in fasi, cioè anticipatamente rispetto al rilascio della nuova versione dell'applicazione:
  • Vengono aggiunti nuovi campi in cui memorizzeremo proprio i dati aggiornati.
  • Trasferiamo i dati dal vecchio campo al nuovo campo in piccole porzioni. Perché lo facciamo? In primo luogo, controlliamo sempre il processo. Sappiamo che abbiamo trasferito già un certo numero di batch e ci rimane da trasferire ancora.
  • E il secondo vantaggio è che tra ogni batch chiudiamo la transazione, ne apriamo una nuova e questo consente all'autovacuum di operare sulla tabella, contrassegnando le righe morte per il riutilizzo.
  • Per le righe che appariranno durante il funzionamento dell'applicazione (abbiamo ancora l'applicazione precedente in funzione) aggiungiamo un trigger che memorizza i nuovi valori nei nuovi campi. Nel nostro caso, questo è il vecchio valore moltiplicato per cento.
  • Se siamo molto determinati e vogliamo lo stesso campo, al termine di tutte le migrazioni e prima del rilascio della nuova versione dell'applicazione, semplicemente rinominiamo i campi. I vecchi in un nome inventato, e i nuovi campi vengono rinominati in quelli vecchi.
  • E solo dopo di ciò avviamo la nuova versione dell'applicazione.

E in questo modo non avremo bloat e non comprometteremo le prestazioni.

Con questo si conclude la terza storia.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

https://github.com/dataegret/pg-utils/blob/master/sql/table_bloat.sql

https://github.com/dataegret/pg-utils/blob/master/sql/table_bloat_approx.sql

E ora parlerò più in dettaglio degli strumenti che ho menzionato nella prima storia.

Prima di cercare il bloat, è fondamentale installare l'estensione. pgstattuple.

Per non inventare le query, nel nostro lavoro abbiamo già scritto queste query. Potete usarle. Qui sono presentate due query.

  • La prima funziona piuttosto a lungo, ma vi mostrerà i valori esatti del bloat per la tabella.
  • La seconda funziona più rapidamente ed è molto efficace quando bisogna valutare rapidamente se c'è bloat o meno per la tabella. E dovete inoltre capire che il bloat nella tabella Postgres è sempre presente. È una caratteristica del suo modello MVCC.
  • E un 20% di bloat è normale per le tabelle nella maggior parte dei casi. Cioè, non dovete preoccuparvi e comprimere questa tabella.

Abbiamo capito come identificare le tabelle che sono diventate gonfie, soprattutto quando sono aumentate di dati inutili.

Ora parliamo di come correggere il bloat:

  • Se abbiamo una tabella piccola e dischi di buona qualità, ad esempio, su una tabella fino a un gigabyte è assolutamente possibile utilizzare VACUUM FULL. Ti prenderà un blocco esclusivo sulla tabella per alcuni secondi e sarà tutto a posto; in cambio, eseguirà rapidamente e in modo efficace il lavoro. Cosa fa VACUUM FULL? Si prende un blocco esclusivo sulla tabella e riscrive le righe vive da vecchi file in una nuova tabella. Alla fine, sostituisce i file vecchi con quelli nuovi. Rimuove i vecchi file e inserisce i nuovi al loro posto. Ma durante il suo lavoro, occuperà un blocco esclusivo sulla tabella. Questo significa che non potrai fare nulla con questa tabella: né scrivere, né leggere, né modificarla. Inoltre, VACUUM FULL richiede spazio aggiuntivo su disco per registrare i dati.
  • Il prossimo strumento pg_repack. Dal punto di vista del suo principio, è molto simile a VACUUM FULL, poiché anch'esso riscrive i dati dai vecchi file in nuovi e li sostituisce nella tabella. Tuttavia, non prende un blocco esclusivo sulla tabella all'inizio del suo lavoro, ma lo fa solo quando ha già i dati pronti per sostituire i file. I requisiti di risorse del disco sono simili a quelli di VACUUM FULL. Hai bisogno di spazio aggiuntivo su disco, il che può essere critico quando hai tabelle da un terabyte. È anche piuttosto affamato di CPU, poiché gestisce attivamente le operazioni di input/output.
  • Il terzo strumento è pgcompacttable. È più attento alle risorse, perché funziona secondo principi leggermente diversi. L'idea principale di pgcompacttable è che utilizza gli aggiornamenti nella tabella per spostare tutte le righe vive all'inizio della tabella. Poi esegue un vacuume su questa tabella, poiché sappiamo che all'inizio ci sono righe vive e alla fine ci sono righe morte. E il vacuum taglia già questa parte finale, cioè richiede poco spazio aggiuntivo su disco. Inoltre, può essere ulteriormente ottimizzato per le risorse.

Con gli strumenti è tutto.

Errori comuni nelle applicazioni che portano a bloat in postgresql. Andrey Sal'nikov

Se il tema del bloat ti sembra interessante e vuoi approfondire ulteriormente, ecco alcuni link utili:

Ho cercato di presentare uno spavento per i sviluppatori, poiché sono i nostri principali clienti per i database e devono capire quali sono le conseguenze delle azioni intraprese. Spero di esserci riuscito. Grazie per l'attenzione!

Domande

Grazie per la relazione! Hai parlato di come si possono identificare i problemi. Ma come si possono prevenire? Ad esempio, ho avuto situazioni in cui le query si bloccavano non solo a causa di richieste a servizi esterni. Erano semplicemente giunzioni eccessive. C'erano piccole query inoffensive che restavano in attesa per un giorno, e poi iniziavano a causare problemi. Sembra molto simile a quanto descrivi. Come si può monitorare ciò? Dobbiamo semplicemente stare a controllare quale query è bloccata? Come si può prevenire questo?

In questo caso, è un compito per gli amministratori della tua azienda, non necessariamente per il DBA.

Io sono un amministratore.

In PostgreSQL c'è una visualizzazione chiamata pg_stat_activity, che mostra le query bloccate. E puoi vedere per quanto tempo sono rimaste bloccate.

Devo controllare ogni 5 minuti?

Imposta un cron e controlla. Se si verifica una query lunga, invia una mail e basta. Quindi, non è necessario guardare a occhio, è possibile automatizzare. Ti arriverà un'email e reagisci a essa. Puoi anche impostare di terminare automaticamente.

Ci sono motivi evidenti per cui ciò accade?

Ne ho elencati alcuni. Altri sono esempi più complessi. E la discussione potrebbe durare a lungo.

Grazie per la relazione! Volevo chiederti dell'utility pg_repack. Se non fa un blocco esclusivo, allora…

Fa un blocco esclusivo.

quindi posso potenzialmente perdere dati. La mia applicazione non dovrebbe scrivere nulla in quel momento?

No, funziona tranquillamente con la tabella, cioè pg_repack sposta prima tutte le righe vive. Naturalmente, ci sono dei registri nella tabella. Sta solo aggiungendo quel pezzetto alla fine.

Cioè, alla fine lo fa comunque?

Alla fine prende un blocco esclusivo per sostituire i file.

Sarà più veloce di VACUUM FULL?

VACUUM FULL, appena avviato, prende subito un blocco esclusivo. E finché non completa l'operazione, non lo rilascia. Mentre pg_repack prende un blocco esclusivo solo al momento della sostituzione dei file. In quel momento non puoi scrivere, ma i dati non andranno persi, tutto sarà a posto.

Salve! Lei ha parlato del funzionamento dell'autovacuum. C'era un grafico con celle rosse, gialle e verdi. Cioè, quelle gialle sono state contrassegnate come eliminate. E di conseguenza, in esse si può registrare qualcosa di nuovo?

Sì. Postgres non elimina le righe. Ha una peculiarità. Se aggiorniamo una riga, contrassegniamo quella vecchia come eliminata. Viene inserito l'id della transazione che ha modificato quella riga, e scriviamo una nuova riga. E abbiamo delle sessioni che potenzialmente possono leggerle. A un certo punto, queste diventano vecchie. La funzione dell'autovacuum è quella di scorrere queste righe e contrassegnarle come non necessarie. E vi si possono riscrivere dei dati.

Ho capito. Ma la domanda è un po' diversa. Non ho concluso. Supponiamo di avere una tabella. Ha campi di dimensioni variabili. E se provo ad inserire qualcosa di nuovo, potrebbe semplicemente non entrare nella vecchia cella.

No, in ogni caso l'intera riga viene aggiornata. In Postgres ci sono due modelli di archiviazione dei dati. Viene scelto a seconda del tipo di dati. Ci sono dati che vengono memorizzati direttamente nella tabella e ci sono anche i tos-dati. Questi sono grandi volumi di dati: testo, json. Vengono memorizzati in tabelle separate. E anche per queste tabelle vale la stessa storia con il bloat, cioè è tutto lo stesso. Solo che sono stati isolati.

Grazie per la presentazione! Quanto è accettabile utilizzare il timeout per le query come limite di durata?

È molto accettabile. Lo usiamo ovunque. E poiché non abbiamo i nostri servizi, ma forniamo supporto remoto, abbiamo clienti abbastanza diversi. E tutti ne sono piuttosto soddisfatti. Cioè, abbiamo compiti in cron che controllano. Si concorda con il cliente la durata delle sessioni, prima della quale non interrompiamo. Può essere un minuto, possono essere 10 minuti. Dipende dal carico sul database e dal suo scopo. Ma per tutti utilizziamo pg_stat_activity.

Grazie per la presentazione! Sto cercando di adattare la tua presentazione alle mie applicazioni. E sembra che stiamo avviando la transazione ovunque, la stiamo concludendo esplicitamente in tutti i casi. Se c'è qualche eccezione, avviene comunque il rollback. E qui mi sono chiesto. Infatti, potrebbe iniziare una transazione non esplicitamente. Questo è un suggerimento per una ragazza, immagino. Se aggiorno semplicemente un record, la transazione inizierà in PostgreSQL e si concluderà solo quando avverrà la disconnessione?

Se parliamo del livello dell'applicazione, dipende dal driver che stai utilizzando, dall'ORM che è in uso. Ci sono tantissime impostazioni. Se hai attivato l'auto commit, allora la transazione inizia e si chiude immediatamente.

Cioè, si chiude subito dopo l'aggiornamento?

Dipende dalle impostazioni. Ho menzionato un'impostazione. È auto commit on. È piuttosto comune. Se è attivata, la transazione si apre e si chiude. Se non hai esplicitamente detto 'start transaction' e 'end transaction', ma hai semplicemente avviato la query nella sessione.

Salve! Grazie per la presentazione! Immaginiamo di avere un database che continua a crescere e improvvisamente sul server finisce lo spazio. Ci sono degli strumenti per risolvere questa situazione?

Lo spazio sul server dovrebbe essere monitorato bene.

Ad esempio, il DBA è andato a prendere un tè, era in vacanza, ecc.

Quando viene creata una filesystem, viene riservato almeno un certo spazio addizionale dove non vengono scritti dati.

E se arriva a zero?

Lì si chiama spazio riservato, cioè può essere liberato e a seconda di quanto grande l'hanno creato, si ottiene spazio libero. Non so quanto ci sia per default. In un altro caso, è necessario fornire dischi, affinché ci sia spazio per effettuare operazioni di ripristino. Puoi eliminare qualche tabella che sai per certo non ti serve.

Non ci sono altri strumenti?

È sempre lavoro manuale. E per lo spazio si determina cosa fare, perché ci sono dati critici e non critici. E per ogni database e per ogni applicazione che con esso lavora, dipende dal business. Si decide sempre in base allo spazio.

Grazie per la presentazione! Ho due domande. Prima di tutto, hai mostrato delle diapositive in cui era evidente che in caso di transazioni bloccate cresce sia il volume dello spazio tabellare sia la dimensione dell'indice. E poi nella presentazione c'erano un sacco di utility che impacchettano la tabella. E per quanto riguarda l'indice?

Anche loro vengono impacchettati.

Ma il vacuum non tocca l'indice?

Alcuni lavorano con gli indici. Ad esempio, pg_rapack, pgcompacttable. Il VACUUM ricrea gli indici e li tocca. La sostanza di VACUUM FULL è riscrivere tutto, cioè lavora con ogni indice.

E la seconda domanda. Non ho capito perché i report sulle repliche dipendano così tanto dalla replicazione stessa. Pensavo che i report fossero letture e la replicazione scritture.

Dove si verifica il conflitto di replicazione? Abbiamo un Master su cui avvengono i processi. Abbiamo l'autovacuum. L'autovacuum in pratica cosa fa? Elimina alcune righe vecchie. Se in quel momento c'è una richiesta sulla replica che legge queste righe vecchie, e sul Master è successo che l'autovacuum ha contrassegnato queste righe come possibili per riscrittura, allora le riscriviamo. E riceviamo un pacchetto di dati quando dobbiamo riscrivere quelle righe necessarie alla richiesta sulla replica; il processo di replicazione attenderà il timeout che hai impostato. Poi PostgreSQL deciderà cosa è più importante per lui. E la replicazione è più importante della richiesta, quindi scarterà la richiesta per eseguire queste modifiche sulla replica.

Andrey, ho una domanda. Questi grafici fantastici che hai mostrato durante la presentazione sono il risultato di un tuo strumento? Come sono stati creati i grafici?

È un servizio Okmeter.

È un prodotto commerciale?

Sì. È un prodotto commerciale.

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, server VPS VDS 🔥 Acquista hosting affidabile per siti web con protezione DDoS, server VPS VDS | ProHoster