Unlocking the Postgres Lock Manager. Bruce Momjian

La trascrizione della relazione del 2020 di Bruce Momjian "Sbloccare il gestore di blocchi di Postgres".

Unlocking the Postgres Lock Manager. Bruce Momjian

(Nota: Tutte le query SQL delle diapositive possono essere ottenute tramite questo link: http://momjian.us/main/writings/pgsql/locking.sql)

Ciao! È fantastico essere di nuovo qui in Russia. Mi scuso per non essere potuto venire l'anno scorso, ma quest'anno Ivan e io abbiamo grandi piani. Spero di essere qui molto più spesso. Adoro venire in Russia. Visiterò Tyumen e Tver. Sono molto felice di poter essere in queste città.

Mi chiamo Bruce Momjian. Lavoro per EnterpriseDB e mi occupo di Postgres da oltre 23 anni. Vivo a Filadelfia, negli Stati Uniti. Viaggio circa 90 giorni all'anno e partecipo a circa 40 conferenze. Il mio sito web, che contiene le diapositive che vi mostrerò adesso. Pertanto, dopo la conferenza, potrete scaricarle dal mio sito personale. Ci sono anche circa 30 presentazioni. Inoltre, ci sono video e un gran numero di post sul blog, oltre 500. È una risorsa abbastanza ricca. E se siete interessati a questo materiale, vi invitiamo a utilizzarlo.

In passato ero un insegnante, professore, prima di iniziare a lavorare con Postgres. E sono molto felice di potervi ora raccontare ciò che mi appresto a raccontarvi. Questa è una delle mie presentazioni più interessanti. E questa presentazione contiene 110 diapositive. Inizieremo dalle cose più semplici e, verso la fine, la relazione diventerà sempre più complessa e impegnativa.

Unlocking the Postgres Lock Manager. Bruce Momjian

È una conversazione piuttosto scomoda. Il blocco non è un argomento molto popolare. Vogliamo che svanisca. È come andare dal dentista.

Unlocking the Postgres Lock Manager. Bruce Momjian

  1. Il blocco è un problema per molte persone che lavorano con database e hanno più processi che lavorano contemporaneamente. Hanno bisogno del blocco. Cioè, oggi vi darò conoscenze di base sul blocco.
  2. Identificatori di transazione. Questa è una parte piuttosto noiosa della presentazione, ma è necessario comprenderla.
  3. Poi parleremo dei tipi di blocco. Questa è una parte abbastanza meccanica.
  4. E poi presenteremo alcuni esempi di blocchi. E questo sarà piuttosto difficile da comprendere.

Unlocking the Postgres Lock Manager. Bruce Momjian

Parliamo dei blocchi.

Unlocking the Postgres Lock Manager. Bruce Momjian

La terminologia è piuttosto complessa. Quanti di voi sanno da dove proviene questo estratto? Due persone. È da un gioco chiamato "Colossale avventura nella caverna". Era un videogioco testuale negli anni '80, mi sembra. Si doveva entrare in una caverna, in un labirinto e il testo cambiava, ma il contenuto rimaneva sostanzialmente lo stesso ogni volta. Così ricordo questo gioco.

Unlocking the Postgres Lock Manager. Bruce Momjian

Qui vediamo i nomi dei blocchi che ci sono stati forniti da Oracle. Li utilizziamo.

Unlocking the Postgres Lock Manager. Bruce Momjian

Qui vediamo termini che mi confondono. Ad esempio, SHARE UPDATE EXCLUSIVE. Poi SHARE RAW EXCLUSIVE. A dire il vero, questi nomi non sono molto chiari. Cercheremo di esaminarli più nel dettaglio. Alcuni contengono la parola "share", che significa – separarsi. Alcuni contengono la parola "exclusive" — esclusivo. In alcuni si trovano entrambe le parole. Vorrei iniziare da come funzionano questi blocchi.

Unlocking the Postgres Lock Manager. Bruce Momjian

È anche molto importante la parola "accesso" — access. E la parola "row" — riga. Cioè, distribuzione dell'accesso, distribuzione delle righe.

Unlocking the Postgres Lock Manager. Bruce Momjian

Un altro problema che è necessario comprendere in Postgres, di cui purtroppo non potrò parlare nella mia presentazione, è il MVCC. Ho una presentazione separata su questo argomento sul mio sito web. E se pensate che questa presentazione sia complicata, allora il MVCC è probabilmente la mia più complessa. E se siete interessati, potete vederla sul sito. Potete guardare il video.

Unlocking the Postgres Lock Manager. Bruce Momjian

Un altro punto che dobbiamo capire sono gli identificatori delle transazioni. Molte transazioni non possono funzionare senza identificatori unici. Qui c'è una spiegazione di cosa sia una transazione. In Postgres ci sono due sistemi di numerazione delle transazioni. So che non è una soluzione molto elegante.

Unlocking the Postgres Lock Manager. Bruce Momjian

Tenete anche presente che le diapositive saranno piuttosto complesse da comprendere, quindi si deve prestare particolare attenzione a ciò che è evidenziato in rosso.

Unlocking the Postgres Lock Manager. Bruce Momjian

http://momjian.us/main/writings/pgsql/locking.sql

Guardiamo. È evidenziato in rosso il numero della transazione. Qui è mostrata la funzione SELECT pg_back. Restituisce la mia transazione e l'ID di questa transazione.

Un altro punto, se ti piace questa presentazione e vuoi lanciarla nel tuo database, puoi seguire questo link evidenziato in rosa e scaricare l'SQL per questa presentazione. E puoi semplicemente eseguirla nel tuo PSQL e l'intera presentazione apparirà sul tuo schermo immediatamente. Non conterrà colori, ma almeno potremo vederla.

Unlocking the Postgres Lock Manager. Bruce Momjian

In questo caso vediamo l'ID della transazione. Questo è il numero che le abbiamo assegnato. E c'è anche un altro tipo di ID transazione in Postgres, chiamato ID transazione virtuale.

E dobbiamo capire questo. È molto importante, altrimenti non saremo in grado di comprendere il blocco in Postgres.

L'ID transazione virtuale è un ID della transazione che non contiene valori permanenti. Ad esempio, se eseguo un comando SELECT, probabilmente non cambierò il database, non bloccherò nulla. Quindi, quando eseguiamo un semplice SELECT, non diamo a questa transazione un ID permanente. Le diamo solo un ID virtuale.

E questo aumenta le prestazioni di Postgres, migliorando le capacità di pulizia, quindi l'ID delle transazioni virtuali è composto da due numeri. Il primo numero prima della barra è l'ID del backend. E a destra vediamo solo un contatore.

Unlocking the Postgres Lock Manager. Bruce Momjian

Quindi se eseguo una query, dice che l'ID del backend è 2.

Unlocking the Postgres Lock Manager. Bruce Momjian

E se eseguo una serie di tali transazioni, vediamo che il contatore aumenta ogni volta che eseguo una query. Ad esempio, quando eseguo una query 2/10, 2/11, 2/12, ecc.

Unlocking the Postgres Lock Manager. Bruce Momjian

Si prega di notare che qui ci sono due colonne. A sinistra vediamo l'ID virtuale della transazione – 2/12. E a destra abbiamo l'ID permanente della transazione. E questo campo è vuoto. E questa transazione non modifica il database. Quindi non le assegno un ID permanente.

Unlocking the Postgres Lock Manager. Bruce Momjian

Non appena eseguo il comando ANALYZE, la stessa query mi restituisce un ID permanente per la transazione. Guarda come è cambiato. Prima non avevo questo ID, ora è apparso.

Unlocking the Postgres Lock Manager. Bruce Momjian

Quindi, qui c'è un'altra query, un'altra transazione. Il numero di transazione virtuale è 2/13. E se richiedo l'ID permanente della transazione, quando eseguo la query, lo ottengo.

Unlocking the Postgres Lock Manager. Bruce Momjian

Quindi, ancora una volta. Abbiamo un ID virtuale della transazione e un ID permanente della transazione. Comprendi semplicemente questo punto per capire il comportamento di Postgres.

Unlocking the Postgres Lock Manager. Bruce Momjian

Passiamo alla terza sezione. Qui attraverseremo semplicemente i vari tipi di blocchi in Postgres. Non è molto interessante. L'ultima sezione sarà molto più interessante. Ma dobbiamo esaminare le basi, altrimenti non capiremo ciò che seguirà.

Esamineremo questa sezione, guarderemo ogni tipo di blocco. E vi mostrerò esempi di come vengono impostati, come funzionano, e vi mostrerò alcune query che possono essere utilizzate per vedere come funziona il blocco in Postgres.

Unlocking the Postgres Lock Manager. Bruce Momjian

Per creare una query e vedere cosa sta succedendo in Postgres, dobbiamo eseguire una query in un sistema di visualizzazione. In questo caso, in rosso, abbiamo pg_lock. Pg_lock è una tabella di sistema che ci dice quali blocchi sono attualmente in uso in Postgres.

Tuttavia, è molto difficile per me mostrarti pg_lock da solo, perché è piuttosto complesso. Quindi ho creato una vista che mostra pg_locks. E fa anche un po' di lavoro per me che mi aiuta a capire meglio. Cioè, esclude i miei blocchi, la mia sessione, ecc. È semplicemente una SQL standard e permette di mostrarvi meglio cosa sta succedendo.

Unlocking the Postgres Lock Manager. Bruce Momjian

Un altro problema è che questa vista è molto ampia, quindi devo creare una seconda vista: lockview2.

Unlocking the Postgres Lock Manager. Bruce Momjian E mostra altre colonne dalla tabella. E un'altra che mi mostra le colonne rimanenti. È piuttosto complicato, quindi ho cercato di presentarlo nel modo più semplice possibile.

Unlocking the Postgres Lock Manager. Bruce Momjian

Quindi, abbiamo creato una tabella chiamata Lockdemo. E abbiamo creato una riga lì. Questa è la nostra tabella di esempio. Creeremo delle sezioni per mostrarvi semplicemente degli esempi di blocchi.

Unlocking the Postgres Lock Manager. Bruce Momjian

Quindi, una riga, una colonna. Il primo tipo di blocco si chiama ACCESS SHARE. È il blocco meno restrittivo. Questo significa che praticamente non entra in conflitto con gli altri blocchi.

E se vogliamo definire esplicitamente il blocco, eseguiamo il comando «lock table». E questo bloccherà esplicitamente, cioè in modalità ACCESS SHARE, eseguiamo lock table. E se avvio PSQL in background, avvio in questo modo una seconda sessione dalla mia prima sessione. Cosa faccio qui? Passo a un'altra sessione e le dico «fammi vedere lockview per questa richiesta». E qui ho AccessShareLock in questa tabella. È esattamente ciò che ho richiesto. E dice che il blocco è stato assegnato. Molto semplice.

Unlocking the Postgres Lock Manager. Bruce Momjian

Inoltre, se guardiamo nella seconda colonna, non c'è nulla. Sono vuote.

Unlocking the Postgres Lock Manager. Bruce Momjian

E se eseguo il comando «SELECT», questo è un modo implicito (esplicito) di richiedere AccessShareLock. Quindi rilascio la mia tabella e avvio la richiesta, e la richiesta restituisce alcune righe. E in una delle righe vediamo AccessShareLock. Pertanto SELECT genera AccessShareLock nella tabella. E non entra in conflitto praticamente con nulla, perché è un blocco di basso livello.

Unlocking the Postgres Lock Manager. Bruce Momjian

E se eseguo SELECT e ho tre tabelle diverse? In precedenza ho eseguito solo una tabella, ora ne eseguo tre: pg_class, pg_namespace e pg_attribute.

Unlocking the Postgres Lock Manager. Bruce Momjian

E ora, quando guardo alla richiesta, vedo 9 AccessShareLocks in tre tabelle. Perché? In blu sono evidenziate tre tabelle: pg_attribute, pg_class, pg_namespace. Ma puoi anche vedere che tutti gli indici definiti tramite queste tabelle hanno anche AccessShareLock.

E questo è un blocco che praticamente non entra in conflitto con altri. E tutto ciò che fa è semplicemente non permetterci di svuotare la tabella mentre la stiamo selezionando. Ha senso. Cioè, se stiamo selezionando una tabella, in quel momento scomparire, è sbagliato, quindi AccessShare è un blocco di basso livello che ci dice "non eliminare questa tabella mentre sto lavorando".. In sostanza, è tutto ciò che fa.

Unlocking the Postgres Lock Manager. Bruce Momjian

ROW SHARE è un blocco un po' diverso.

Unlocking the Postgres Lock Manager. Bruce Momjian

Prendiamo un esempio. SELECT ROW SHARE è un modo di bloccare ogni singola riga separatamente.. In questo modo nessuno può eliminarle o modificarle mentre le stiamo osservando.

Unlocking the Postgres Lock Manager. Bruce MomjianQuindi, cosa fa SHARE LOCK? Vediamo che l'ID della transazione 681 è per una SELECT. È interessante. Cosa è successo qui? È la prima volta che vediamo un numero nel campo "Lock". Prendiamo l'ID della transazione e dice che la blocca in modalità esclusiva. Tutto ciò che dice è che ho una riga che è tecnicamente bloccata da qualche parte nella tabella. Ma non dice dove esattamente. Più avanti esploreremo questo in dettaglio.

Unlocking the Postgres Lock Manager. Bruce Momjian

Qui diciamo che il blocco è utilizzato da noi.

Unlocking the Postgres Lock Manager. Bruce Momjian

Quindi, il blocco esclusivo dice esplicitamente che è esclusivo. E anche se elimini una riga da questa tabella, questo accadrà, come puoi vedere.

Unlocking the Postgres Lock Manager. Bruce Momjian

SHARE EXCLUSIVE è un blocco più lungo.

Unlocking the Postgres Lock Manager. Bruce Momjian

Questo è il comando ANALYZE, che sarà usato dall'analizzatore.

Unlocking the Postgres Lock Manager. Bruce Momjian

SHARE LOCK - puoi bloccare esplicitamente in modalità condivisa.

Unlocking the Postgres Lock Manager. Bruce Momjian

Puoi anche creare un indice unico. E lì puoi vedere SHARE LOCK, che fa parte di questo. E blocca la tabella e applica SHARE LOCK su di essa.

Per impostazione predefinita, SHARE LOCK sulla tabella significa che altre persone possono leggere la tabella, ma nessuno può modificarla. Ed è proprio ciò che accade quando crei un indice unico.

Se creo un indice unico concurrently, avrò un altro tipo di blocco, perché, come ricordi, l'uso di indici concurrently riduce la necessità di blocchi. E se utilizzo un blocco normale, con un indice normale, ostacolerò così la scrittura nell'indice della tabella durante la sua creazione. Se utilizzo un indice concurrently, dovrò usare un altro tipo di blocco.

Unlocking the Postgres Lock Manager. Bruce Momjian

SHARE ROW EXCLUSIVE - può anche essere impostato esplicitamente.

Unlocking the Postgres Lock Manager. Bruce Momjian

Oppure possiamo creare una regola, cioè prendere un caso specifico in cui sarà utilizzato.

Unlocking the Postgres Lock Manager. Bruce Momjian

Il blocco EXCLUSIVE significa che nessun altro potrà modificare la tabella.

Unlocking the Postgres Lock Manager. Bruce Momjian

Qui vediamo diversi tipi di blocchi.

Unlocking the Postgres Lock Manager. Bruce Momjian

ACCESS EXCLUSIVE, ad esempio, è un comando di blocco. Ad esempio, se stai facendo CLUSTER table, questo significherà che nessuno potrà scrivere in essa. E blocca non solo la tabella stessa, ma anche gli indici.

Unlocking the Postgres Lock Manager. Bruce Momjian

Questa è la seconda pagina del blocco ACCESS EXCLUSIVE, dove vediamo specificamente cosa blocca nella tabella. Blocca le singole righe nella tabella, il che è piuttosto interessante.

Queste sono tutte le informazioni di base che volevo fornire. Abbiamo parlato dei blocchi, degli ID delle transazioni, abbiamo discusso degli ID virtuali delle transazioni e degli ID permanenti delle transazioni.

Unlocking the Postgres Lock Manager. Bruce Momjian

Ora passeremo attraverso esempi di blocchi. Questa è la parte più interessante. Vedremo casi molto interessanti. Il mio obiettivo in questa presentazione è darvi una migliore comprensione di cosa fa realmente Postgres quando cerca di bloccare diverse cose. Mi sembra che sia molto abile nel bloccare singole parti.

Esaminiamo alcuni esempi specifici.

Unlocking the Postgres Lock Manager. Bruce Momjian

Iniziamo con le tabelle e con una riga nella tabella. Quando inserisco qualcosa, vedo un ExclusiveLock, l'ID della transazione e l'ExclusiveLock sulla tabella.

Unlocking the Postgres Lock Manager. Bruce Momjian

E cosa succede se inserisco altre due righe? Ora abbiamo tre righe nella nostra tabella. Ho inserito una riga e ho ottenuto questo in uscita. Se inserisco ancora due righe, cosa c'è di strano qui? C'è qualcosa di strano, perché ho aggiunto tre righe a questa tabella, ma ho ancora due righe nella tabella di blocco. E questo, fondamentalmente, è il comportamento fondamentale di Postgres.

Molti pensano che se in un database bloccate 100 righe, sarà necessario creare 100 voci di blocco. Se blocco 1.000 righe contemporaneamente, avrò bisogno di 1.000 di queste richieste. E se devo bloccare un milione o un miliardo? Ma se facciamo così, non funzionerà molto bene. Se avete utilizzato un sistema che crea voci di blocco per ogni singola riga, vedete che è complicato. Perché dovete definire subito la tabella di blocco, che potrebbe sovraccaricarsi, ma Postgres non fa in questo modo.

E in questa diapositiva è molto importante notare che qui viene chiaramente dimostrato che esiste un altro sistema che opera all'interno del MVCC, che blocca singole righe. Pertanto, quando bloccate miliardi di righe, Postgres non crea un miliardo di comandi separati per il blocco. Questo ha un effetto molto positivo sulle prestazioni.

Unlocking the Postgres Lock Manager. Bruce Momjian

E per quanto riguarda l'aggiornamento? Sto aggiornando una riga e puoi notare che ha eseguito immediatamente due operazioni diverse. Ha bloccato la tabella, ma ha anche bloccato l'indice. E doveva bloccare l'indice perché ci sono vincoli univoci su questa tabella. Vogliamo assicurarci che nessuno lo modifichi, quindi lo blocchiamo.

Unlocking the Postgres Lock Manager. Bruce Momjian

E cosa succede se voglio aggiornare due righe? E vediamo che si comporta allo stesso modo. Effettuiamo il doppio degli aggiornamenti, ma esattamente lo stesso numero di righe bloccate.

Se ti interessa sapere come lo fa Postgres, devi ascoltare le mie presentazioni su MVCC, per scoprire come Postgres contrassegna internamente queste righe che modifica. E Postgres ha un modo in cui lo fa, ma non lo fa a livello di blocco delle tabelle, lo fa a un livello inferiore e più efficiente.

Unlocking the Postgres Lock Manager. Bruce Momjian

E se voglio eliminare qualcosa? Se elimino, ad esempio, una riga e ho ancora i miei due vincoli di blocco, anche se volessi eliminarli tutti, rimarrebbero comunque lì.

Unlocking the Postgres Lock Manager. Bruce Momjian

E, ad esempio, voglio inserire 1.000 righe e poi eliminare o aggiungere altre 1.000 righe, le singole righe che aggiungo o modifico non vengono registrate qui. Vengono registrate a un livello inferiore all'interno della stessa riga. E durante la presentazione su MVCC ne ho parlato nei dettagli. Ma è molto importante, quando analizzi i blocchi, assicurarti che tu abbia un blocco a livello di tabella e che qui non vedi come vengono registrate singole righe.

Unlocking the Postgres Lock Manager. Bruce Momjian

E per quanto riguarda il blocco esplicito?

Unlocking the Postgres Lock Manager. Bruce Momjian

Se faccio clic su 'aggiorna', ho due righe bloccate. E se le seleziono tutte e clicco su 'aggiorna tutto', ho comunque due registrazioni di blocco.

Unlocking the Postgres Lock Manager. Bruce Momjian

Non creiamo registrazioni separate per ogni riga. Perché così la performance scende, potrebbe essercene troppo. E potremmo trovarci in una situazione spiacevole.

Unlocking the Postgres Lock Manager. Bruce Momjian

E lo stesso vale se facciamo un shared, possiamo farlo per tutte le 30 volte.

Unlocking the Postgres Lock Manager. Bruce Momjian

Ripristiniamo la nostra tabella, eliminiamo tutto, poi reinseriamo una riga.

Unlocking the Postgres Lock Manager. Bruce Momjian

Un altro tipo di comportamento che si osserva in Postgres è un comportamento molto noto e desiderato: si può effettuare un update o una select. E si può fare contemporaneamente. E la select non blocca l'update, e viceversa. Diciamo al lettore di non bloccare chi scrive, e chi scrive non blocca il lettore.

Vi mostrerò un esempio di questo. Ora farò una selezione. Poi faremo un INSERT. E poi potrete vedere – 694. Potrete vedere l'ID della transazione che ha effettuato questo inserimento. Ed è così che funziona.

Unlocking the Postgres Lock Manager. Bruce Momjian

E se ora guardo il mio ID di backend, è diventato – 695.

Unlocking the Postgres Lock Manager. Bruce Momjian

E posso vedere che 695 appare nella mia tabella.

Unlocking the Postgres Lock Manager. Bruce Momjian

E se eseguo un aggiornamento qui in questo modo, ottengo un altro caso. In questo caso, 695 è un blocco esclusivo, e l'update ha lo stesso comportamento, ma non si verifica alcun conflitto tra di essi, il che è piuttosto insolito.

E potrete notare che in alto c'è un ShareLock, e in basso c'è un ExclusiveLock. E entrambe le transazioni sono andate a buon fine.

E bisogna ascoltare la mia presentazione su MVCC per capire come funziona. Ma questa è un'illustrazione di come si può fare tutto questo contemporaneamente, cioè effettuare SELECT e UPDATE allo stesso tempo.

Unlocking the Postgres Lock Manager. Bruce Momjian

Facciamo un reset e facciamo un'operazione una volta di più.

Unlocking the Postgres Lock Manager. Bruce Momjian

Se provate a eseguire contemporaneamente due update sulla stessa riga, si bloccherà. E ricordate, ho detto che il lettore non blocca lo scrittore, e lo scrittore blocca il lettore, ma uno scrittore blocca un altro scrittore. Cioè, non possiamo fare in modo che due persone aggiornino contemporaneamente la stessa riga. Dobbiamo aspettare che uno di loro finisca.

Unlocking the Postgres Lock Manager. Bruce Momjian

E per illustrare questo, darò un'occhiata alla tabella Lockdemo. E guarderemo una riga. Nella transazione 698.

L'abbiamo aggiornato a 2. 699 è il primo aggiornamento. E ha avuto successo o si trova in una transazione in attesa e sta aspettando che confermiamo o annulliamo.

Unlocking the Postgres Lock Manager. Bruce Momjian

Ma guarda un'altra cosa – 2/51 – questa è la nostra prima transazione, la nostra prima sessione. 3/112 – questa è la seconda richiesta, che è apparsa in alto e ha cambiato questo valore in 3. E se noti, il superiore ha bloccato se stesso, che è 699. Ma 3/112 non ha fornito alcun blocco. Nella colonna Lock_mode c'è scritto che sta aspettando. Sta aspettando 699. E se guardi dove si trova 699, è sopra. E cosa ha fatto la prima sessione? Ha creato un blocco esclusivo sul proprio ID di transazione. È così che Postgres funziona. Blocca il proprio ID di transazione. E se vuoi aspettare che qualcuno confermi o annulli, devi aspettare che ci sia una transazione in attesa. Ecco perché possiamo vedere una riga strana.

Guardiamo di nuovo. A sinistra vediamo il nostro ID di elaborazione. Nella seconda colonna vediamo il nostro ID virtuale di transazione, e nella terza vediamo il lock_type. Cosa significa? Fondamentalmente, indica che blocca l'ID di transazione. Ma nota che in tutte le righe in fondo c'è scritto relation. Ecco perché hai due tipi di blocchi nella tabella. C'è il blocco relation. E c'è anche il blocco transactionid, dove ci blocchiamo autonomamente, è proprio ciò che accade nella prima riga o in fondo, dove transactionid, dove aspettiamo che 699 porti a termine la propria operazione.

Sto osservando cosa sta succedendo qui. E qui stanno accadendo due cose simultaneamente. Stai guardando il blocco per ID di transazione nella prima riga, che blocca se stessa. E blocca se stessa per costringere le persone ad aspettare.

Se guardi la sesta riga, è la stessa registrazione della prima. Ecco perché la transazione 699 è bloccata. Anche 700 si auto-blocca. E poi nella riga inferiore vedrai che stiamo aspettando che 699 porti a termine la propria operazione.

Unlocking the Postgres Lock Manager. Bruce Momjian

E nel lock_type, tuple vedi dei numeri.

Unlocking the Postgres Lock Manager. Bruce Momjian

Puoi vedere che è 0/10. E questo è il numero di pagina e anche l'offset di questa specifica riga.

Unlocking the Postgres Lock Manager. Bruce Momjian

E vedi che diventa 0/11 quando aggiorni.

Unlocking the Postgres Lock Manager. Bruce Momjian

Ma in realtà – è 0/10, perché si sta aspettando questa operazione. Abbiamo la possibilità di vedere che è quella riga che sto aspettando di confermare.

Unlocking the Postgres Lock Manager. Bruce Momjian

Non appena l'abbiamo confermato e premuto commit, e quando l'aggiornamento è terminato, ecco cosa otteniamo di nuovo. La transazione 700 è l'unico blocco, non sta aspettando nessun altro, perché è stata impegnata. Sta solo aspettando che la transazione si concluda. Non appena la 699 termina, non stiamo aspettando più niente. E ora la transazione 700 dice che va tutto bene, che tutti i blocchi necessari sono presenti in tutte le tabelle autorizzate.

Unlocking the Postgres Lock Manager. Bruce Momjian

E per complicare ulteriormente il tutto, stiamo creando un altro view, che questa volta ci fornirà un'idea gerarchica. Non mi aspetto che tu capisca questa query. Ma ci darà una visione più chiara di cosa sta succedendo.

Unlocking the Postgres Lock Manager. Bruce Momjian

Questo è un view ricorsivo, che ha anche un'altra sezione. E poi restituisce tutto insieme. Usandolo.

Unlocking the Postgres Lock Manager. Bruce Momjian

E se facciamo tre aggiornamenti simultanei e diciamo che la riga ora è tre. E cambiamo 3 in 4.

Unlocking the Postgres Lock Manager. Bruce Momjian

Ecco, vediamo 4. E l'ID transazionale 702.

Unlocking the Postgres Lock Manager. Bruce Momjian

Poi cambierò 4 in 5. E 5 in 6, e 6 in 7. E metto in coda le persone che stanno aspettando che questa singola transazione si concluda.

Unlocking the Postgres Lock Manager. Bruce Momjian

E tutto diventa chiaro. Qual è la prima riga? È 702. Questo è l'ID transazionale che ha inizialmente impostato questo valore. E cosa ho scritto nella colonna Granted? Ho segni f. Questi sono i miei aggiornamenti, che (5, 6, 7) non possono essere approvati, perché stiamo aspettando che l'ID transazionale 702 si concluda. Abbiamo un blocco sull'ID transazionale. E ci sono 5 blocchi transazionali ID.

E se guardi 704, 705, lì non c'è ancora nulla scritto, perché non sanno ancora cosa sta succedendo. Scrivono solo che non hanno idea di cosa stia accadendo. E semplicemente andranno a dormire, perché stanno aspettando che qualcuno finisca e li svegli quando ci sarà la possibilità di cambiare la riga.

Unlocking the Postgres Lock Manager. Bruce Momjian

Ecco come appare. È chiaro che stanno tutti aspettando la 12a riga.

Unlocking the Postgres Lock Manager. Bruce Momjian

Questo è ciò che abbiamo visto qui. Ecco 0/12.

Unlocking the Postgres Lock Manager. Bruce Momjian

Quindi, non appena la prima transazione è approvata, puoi vedere qui come funziona la gerarchia. E ora diventa tutto chiaro. Sono tutti liberi. E in realtà sono ancora in attesa.

Unlocking the Postgres Lock Manager. Bruce Momjian

Ecco cosa sta succedendo. 702 si blocca. E ora 703 ottiene questa blocco di riga, e poi 704 inizia ad aspettare che 703 si impegni. E anche 705 attende questo. E quando tutto questo si conclude, si auto-puliscono. E vorrei sottolineare che tutti si mettono in coda. Ed è molto simile alla situazione di una coda, dove tutti aspettano la prima macchina. La prima macchina si ferma, e tutti si mettono in fila lunga. Poi si muove, poi la macchina successiva può passare avanti e ottenere il proprio blocco, e così via.

Unlocking the Postgres Lock Manager. Bruce Momjian

E se questo vi è sembrato abbastanza complicato, ora parleremo di deadlock. Non so quanti di voi ci siano già stati. È un problema abbastanza comune nei sistemi di database. Ma i deadlock sono il caso in cui una sessione aspetta che un'altra esegua qualcosa. E nel frattempo, l'altra sessione sta aspettando che la prima esegua qualcosa.

E, per esempio, se Ivan dice: «Dammi qualcosa», e io dico: «No, te lo darò solo se tu mi dai qualcos'altro». E lui risponde: «No, non te lo darò se tu non mi dai». E ci troviamo in una situazione di deadlock. Sono sicuro che Ivan non farebbe così, ma capite il senso, abbiamo due persone che vogliono ottenere qualcosa e non sono pronte a darlo fino a quando l'altra persona non gli dà ciò che vogliono. E qui non c'è soluzione.

E, fondamentalmente, il vostro database deve rilevarlo. E poi è necessario chiudere o terminare una delle sessioni, perché altrimenti resteranno bloccate per sempre. E lo vediamo nei database, lo vediamo nei sistemi operativi. E in tutti i luoghi dove abbiamo processi paralleli, ciò può accadere.

Unlocking the Postgres Lock Manager. Bruce Momjian

E ora creeremo due deadlock. Imposteremo 50 e 80. Nella prima riga eseguirò un aggiornamento da 50 a 50. Otterrò il numero di transazione 710.

Unlocking the Postgres Lock Manager. Bruce Momjian

E poi cambierò 80 in 81, e 50 in 51.

Unlocking the Postgres Lock Manager. Bruce Momjian

Ecco come apparirà. E quindi 710 ha il blocco di riga, mentre 711 attende conferma. Lo abbiamo visto quando abbiamo aggiornato. 710 è il proprietario della nostra riga. E 711 sta aspettando che 710 completi la transazione.

Unlocking the Postgres Lock Manager. Bruce Momjian

E c'è anche scritto su quale riga precisamente avviene il deadlock. Ed è qui che inizia a diventare strano.

Unlocking the Postgres Lock Manager. Bruce Momjian

Ora aggiorniamo 80 in 80.

Unlocking the Postgres Lock Manager. Bruce Momjian

Ecco dove iniziano i deadlock. 710 aspetta una risposta da 711, e 711 aspetta 710. E questo non finirà bene. Non c'è uscita da questa situazione. E si aspetteranno reciprocamente.

Unlocking the Postgres Lock Manager. Bruce Momjian

E questo inizierà a causare ritardi. E non lo vogliamo.

Unlocking the Postgres Lock Manager. Bruce Momjian

In Postgres ci sono modi per rilevare quando ciò accade. E quando accade, si ottiene un errore come questo. E da questo è chiaro che un certo processo aspetta un SHARE LOCK da un altro processo, cioè quello bloccato dal processo 711. E quel processo aspettava di ricevere un SHARE LOCK su un certo ID di transazione, bloccato da un determinato processo. Quindi, qui abbiamo una situazione di deadlock.

Unlocking the Postgres Lock Manager. Bruce Momjian

Esistono deadlock a tre vie? È possibile? Sì.

Unlocking the Postgres Lock Manager. Bruce Momjian

Inseriamo questi numeri nella tabella. Cambiamo 40 in 40, applichiamo un blocco.

Unlocking the Postgres Lock Manager. Bruce Momjian

Cambiamo 60 in 61, 80 in 81.

Unlocking the Postgres Lock Manager. Bruce Momjian

E poi cambiamo 80, e poi – boom!

Unlocking the Postgres Lock Manager. Bruce Momjian

E ora 714 aspetta 715. Il 716 aspetta il 715. E non c'è nulla che si possa fare.

Unlocking the Postgres Lock Manager. Bruce Momjian

Qui non ci sono più due persone, ma tre. Voglio qualcosa da te, questo vuole qualcosa dal terzo, e il terzo vuole qualcosa da me. E ci troviamo in un'attesa circolare, perché stiamo tutti aspettando che l'altro completi ciò che deve fare.

Unlocking the Postgres Lock Manager. Bruce Momjian

E Postgres sa su quale riga ciò avviene. E quindi ti mostrerà il seguente messaggio, che indica che hai un problema in cui tre input si bloccano a vicenda. E non ci sono limiti. Questo può accadere quando 20 record si bloccano a vicenda.

Unlocking the Postgres Lock Manager. Bruce Momjian

Il problema successivo è serializzabile.

Unlocking the Postgres Lock Manager. Bruce Momjian

Se si tratta di un blocco serializzabile speciale.

Unlocking the Postgres Lock Manager. Bruce Momjian

E torniamo al 719. Ha un output perfettamente normale.

Unlocking the Postgres Lock Manager. Bruce Momjian

E puoi fare clic per eseguire una transazione serializzabile.

Unlocking the Postgres Lock Manager. Bruce Momjian

E ora capisci che hai un altro tipo di blocco SA – che significa serializzabile.

Unlocking the Postgres Lock Manager. Bruce Momjian

Unlocking the Postgres Lock Manager. Bruce Momjian

E quindi abbiamo un nuovo tipo di blocco chiamato SARieadLock, che è un blocco seriale che consente di inserire seriali.

Unlocking the Postgres Lock Manager. Bruce Momjian

E puoi anche inserire indici unici.

Unlocking the Postgres Lock Manager. Bruce Momjian

In questa tabella abbiamo indici unici.

Unlocking the Postgres Lock Manager. Bruce Momjian

Quindi, se inserisco qui il numero 2, quindi ho 2. Ma nella parte superiore inserisco un altro 2. E puoi vedere che il 721 ha un blocco esclusivo. Ma ora il 722 aspetta che il 721 completi la sua operazione, perché non può inserire 2 fino a quando non sa cosa succederà con il 721.

Unlocking the Postgres Lock Manager. Bruce Momjian

E se facciamo una subtransazione.

Unlocking the Postgres Lock Manager. Bruce Momjian

Ecco il 723.

Unlocking the Postgres Lock Manager. Bruce Momjian

E se manteniamo il punto e poi lo aggiorniamo, otteniamo un nuovo ID di transazione. Questo è un altro comportamento che devi conoscere. Se lo restituiamo, l'ID di transazione viene perso. 724 viene perso. Ma ora abbiamo 725.

E cosa sto cercando di fare qui? Sto cercando di mostrarti esempi di blocchi insoliti che puoi trovare: che si tratti di blocchi serializzabili o di SAVEPOINT, sono diversi tipi di blocchi che appariranno nella tabella dei blocchi.

Unlocking the Postgres Lock Manager. Bruce Momjian

Questo è creare blocchi espliciti (chiari), che hanno pg_advisory_lock.

Unlocking the Postgres Lock Manager. Bruce Momjian

E vedi che il tipo di blocco è elencato come advisory. E qui è scritto in rosso «advisory». E puoi bloccare contemporaneamente con pg_advisory_unlock.

Unlocking the Postgres Lock Manager. Bruce Momjian

E per finire, vorrei mostrarti un'altra cosa sconvolgente. Creerò un altro tipo. Ma unirò la tabella pg_locks con la tabella pg_stat_activity. E perché voglio farlo? Perché mi permetterà di vedere tutte le sessioni correnti e verificare quali blocchi stanno aspettando. Ed è abbastanza interessante quando mettiamo insieme la tabella dei blocchi e la tabella delle query.

Unlocking the Postgres Lock Manager. Bruce Momjian

E qui creiamo pg_stat_view.

Unlocking the Postgres Lock Manager. Bruce Momjian

E aggiorniamo una riga di uno. E qui vediamo 724. Poi aggiorniamo la nostra riga a tre. E cosa vedi qui adesso? Queste sono le query, cioè vedi l'intera lista di query elencate nella colonna di sinistra. E poi sul lato destro puoi vedere i blocchi e ciò che creano. E questo potrebbe essere più comprensibile per te, così non devi tornare ogni volta a ciascuna sessione per vedere se è necessario unirsi o meno. Lo fanno per noi.

Un'altra funzione che è molto utile è pg_blocking_pids. Probabilmente non ne hai mai sentito parlare. Cosa fa? Ci permette di dire che per questa sessione 11740, quali esatti ID dei processi sta aspettando. E puoi vedere che 11740 sta aspettando 724. E 724 è in cima. E 11306 è il tuo ID processo. Fondamentalmente, questa funzione scorre la tua tabella di blocco. E so che è un po' complicato, ma stai riuscendo a capirlo. Fondamentalmente, questa funzione attraversa questa tabella di blocco e cerca di trovare dove si trova questo ID processo, tenendo conto dei blocchi che sta aspettando. E cerca anche di calcolare quale sia l'ID processo di quel processo che aspetta i blocchi. Quindi puoi eseguire questa funzione pg_blocking_pids.

E questo può essere molto utile. Lo abbiamo aggiunto solo dalla versione 9.6, quindi questa funzione ha solo 5 anni, ma è molto, molto utile. E lo stesso vale per la seconda richiesta. Mostra esattamente ciò che dobbiamo vedere.

Unlocking the Postgres Lock Manager. Bruce Momjian

È di questo che volevo parlare con te. E come mi aspettavo, abbiamo utilizzato tutto il nostro tempo, perché c'era un numero così elevato di diapositive. E le diapositive sono disponibili per il download. Vorrei ringraziarti per essere stato qui. Sono certo che ti piacerà il resto della conferenza, grazie mille!

Domande:

Per esempio, se cerco di aggiornare righe, e una seconda sessione cerca di eliminare l'intera tabella. Da quello che capisco, là deve esserci qualcosa come un intent lock. Esiste qualcosa del genere in Postgres?

Unlocking the Postgres Lock Manager. Bruce Momjian

Torniamo all'inizio. Forse ricordi che quando fai qualsiasi cosa, ad esempio quando esegui un SELECT, diamo un AccessShareLock. E questo impedisce l'eliminazione della tabella. Quindi se, ad esempio, vuoi aggiornare una riga nella tabella o eliminare una riga, qualcuno non può eliminare l'intera tabella contemporaneamente a questo, perché tu detieni questo AccessShareLock sull'intera tabella e sulla riga. E non appena hai finito, possono eliminarla. Ma finché stai modificando qualcosa, non potranno farlo.

Facciamolo di nuovo. Passiamo all'esempio dell'eliminazione. E vedi come su una riga c'è un blocco esclusivo su tutta la tabella.

Sarà simile a un lock esclusivo, giusto?

Sì, assomiglia a questo. Capisco di cosa stai parlando. Stai dicendo che, se eseguo un SELECT, ho ShareExclusive, e poi lo traduco in Row Exclusive, diventa un problema? Ma sorprendentemente, non crea un problema. Sembra un aumento del livello di blocco, ma in realtà, ho un lock che impedisce la cancellazione. E ora, quando faccio questo lock più potente, continua ancora a impedire la cancellazione. Quindi non è che stia salendo. Cioè, lo impediva anche quando era a un livello inferiore, quindi, quando ne aumento il livello, continua a prevenire la cancellazione della tabella.

Capisco di cosa stai parlando. Non c'è un caso di aumento del livello di blocco, dove stai cercando di rimuovere un lock per introdurne uno più potente. Qui si tratta semplicemente di aumentare ovunque questa prevenzione, quindi non causa alcun conflitto. Ma è una buona domanda. Grazie mille per averla posta!

Cosa dobbiamo fare per evitare situazioni di deadlock quando abbiamo molte sessioni e un gran numero di utenti?

Postgres rileva automaticamente le situazioni di deadlock. E automaticamente rimuove una delle sessioni. L'unico modo per evitare situazioni di deadlock è bloccare le persone nello stesso ordine. Quindi, quando guardi alla tua applicazione, spesso la causa dei deadlock... Immagina che voglio bloccare due cose diverse. Un'applicazione blocca la tabella 1, mentre un'altra applicazione blocca la tabella 2, e poi la tabella 1. E il modo più semplice per evitare deadlock è esaminare la tua applicazione e cercare di assicurarti che il blocco avvenga nello stesso ordine in tutte le applicazioni. E questo generalmente elimina l'80% dei problemi, perché persone diverse scrivono queste applicazioni. E se li blocchi nello stesso ordine, non ti trovi di fronte a situazioni di deadlock.

Grazie mille per il tuo intervento! Hai parlato di vacuum full e, se ho capito bene, vacuum full riorganizza l'ordine delle righe in uno storage separato, quindi mantiene le voci attuali inalterate. Ma perché vacuum full richiede l'accesso in lock esclusivo e perché confligge con le operazioni di scrittura?

È una buona domanda. La ragione è che vacuum full prende la tabella. E noi, in sostanza, creiamo una nuova versione della tabella. E la tabella sarà nuova. Quindi, in effetti, questa sarà completamente una nuova versione della tabella. E il problema è che, quando facciamo ciò, non vogliamo che le persone la leggano, perché abbiamo bisogno che vedano la nuova tabella. E quindi si collega alla domanda precedente. Se potessimo leggere contemporaneamente, non potremmo spostare le persone sulla nuova tabella. Dovremmo aspettare che tutti finissero di leggere questa tabella e quindi, in sostanza, è una situazione di lock exclusive.
Stiamo semplicemente dicendo che blocchiamo dall'inizio, perché sappiamo che alla fine avremo bisogno di un blocco esclusivo per spostare tutti sulla nuova copia. Quindi potenzialmente possiamo risolverlo. E lo facciamo con l'indicizzazione simultanea. Ma è molto più complicato da fare. E questo si riferisce molto alla tua domanda precedente sul lock exclusive.

È possibile aggiungere un timeout di locking in Postgres? In Oracle posso, ad esempio, scrivere 'seleziona per aggiornare' e aspettare 50 secondi prima di aggiornare. Questo era buono per l'applicazione. Ma in Postgres devo farlo immediatamente e non aspettare affatto, oppure aspettare fino a un certo momento.

Sì, puoi selezionare un timeout per i tuoi blocchi, sui tuoi locks. Puoi anche emettere un comando no way, che sarà …, se non puoi ottenere immediatamente un blocco. Quindi o lock timeout, o altro che ti permetta di farlo. Non si fa a livello sintattico. Si fa come variabile sul server. A volte non è utilizzabile.

Puoi aprire il 75° slide?

Sì.

Unlocking the Postgres Lock Manager. Bruce Momjian

E la mia domanda è la seguente. Perché entrambi i processi di aggiornamento aspettano il 703?

E questa è una domanda interessante. Non capisco, tra l'altro, perché Postgres si comporti in questo modo. Quando è stato creato 703, si aspettava 702. E quando 704 e 705 compaiono, sembra che non sappiano cosa stanno aspettando, perché non c'è ancora nulla. E Postgres fa in questo modo: quando non riesci a ottenere un blocco, scrive "E qual è il senso di elaborarti?", perché stai comunque aspettando qualcuno. Quindi lasciamolo semplicemente pendere in aria, non lo aggiorna proprio. Ma cosa è successo qui? Appena 702 ha completato il processo e 703 ha ottenuto il suo blocco, il sistema è tornato indietro. E ha detto che ora abbiamo due persone che stanno aspettando. E poi aggiorniamo entrambe insieme. E indichiamo che entrambe stanno aspettando.

Non so perché Postgres agisca in questo modo. Ma c'è un problema che si chiama f…. Mi sembra che non sia un termine russo. È quando tutti aspettano un solo lock, anche se ci sono 20 istanze che aspettano il lock. E all'improvviso si svegliano tutte insieme. E tutti iniziano a cercare di reagire. Ma il sistema fa sì che tutti aspettino 703. Perché stanno tutti aspettando, e li mettiamo in fila immediatamente. E se appare qualche altra nuova richiesta, che è stata formata dopo, per esempio 707, ci sarà di nuovo un vuoto.

E mi sembra che questo venga fatto in modo da poter dire che a questo punto 702 sta aspettando 703, mentre per chi arriva dopo non ci sarà alcuna registrazione in questo campo. Ma appena il primo in attesa se ne va, quelli che stavano aspettando in quel momento prima dell'aggiornamento ricevono lo stesso marcatore. E quindi, mi sembra che questo sia fatto per gestire in ordine, in modo che siano disposti correttamente.

Io l'ho sempre considerato un fenomeno piuttosto strano. Perché qui, ad esempio, non li elenchiamo affatto. Ma, mi sembra, ogni volta che diamo un nuovo lock, guardiamo a tutti coloro che stanno aspettando. Quindi li mettiamo tutti in fila. E poi chiunque nuovi arrivi entra in coda solo quando la persona successiva ha finito di essere elaborata. Domanda molto buona. Grazie mille per la domanda!

Mi sembra molto più logico quando 705 aspetta 704.

Ma il problema qui è il seguente. Tecnologicamente, puoi svegliare o l'uno o l'altro. E quindi sveglieremo l'uno o l'altro. Ma cosa succede nel funzionamento del sistema? Vedi, come 703 in cima ha bloccato il proprio ID transazionale. È così che funziona Postgres. E 703 viene bloccato dal proprio ID transazionale, quindi, se qualcuno vuole aspettare, aspetterà 703. E, in sostanza, 703 termina. E solo dopo che si completa, uno dei processi si risveglia. E non sappiamo quale sarà quel processo. Poi li gestiamo gradualmente. Ma non è chiaro quale processo si risveglierà per primo, perché potrebbe essere qualsiasi di questi processi. In sostanza, avevamo uno scheduler che diceva che ora possiamo risvegliare qualsiasi di questi processi. Semplicemente scegliamo uno a caso. Quindi entrambi devono essere contrassegnati, perché possiamo risvegliare uno di essi.

E il problema è che abbiamo un'infinità di CP. E quindi è molto probabile che possiamo risvegliare quello più recente. E se, ad esempio, risvegliamo quello più recente, allora aspetteremo chi ha appena ottenuto il blocco, quindi non possiamo determinare qual è esattamente il primo a essere risvegliato. Creiamo semplicemente una situazione del genere, e il sistema li risveglierà in ordine casuale.

C'è articoli sui locks di Egor Rogov. Dai un'occhiata, sono anche interessanti e utili. L'argomento è, ovviamente, terribilmente complesso. Grazie mille, Bruce!

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