Postgres, MVCC e Locking


8

Ho una serie di istruzioni SQL che assomigliano al seguente:

BEGIN;
SELECT counter FROM table WHERE id=X FOR UPDATE;
REALLY COMPLEX QUERY;
UPDATE table SET counter=Y WHERE id=X;
END;

Vorrei impedire la lettura del contatore mentre ricalcolo il suo valore, ma secondo i documenti di Postgres "I blocchi a livello di riga non influiscono sulla query di dati; bloccano solo i writer sulla stessa riga".

Domande:

  1. Qual è il punto di un blocco di riga "esclusivo" se non impedisce le letture? È solo per impedire ad altre transazioni di ottenere blocchi di condivisione?
  2. Se leggo la riga con SELECT ... FOR SHARE, si ottiene lo stesso effetto di un blocco "esclusivo"?
  3. È possibile disattivare MVCC per una tabella / schema / database e consentire scritture sul posto?

Risposte:


5

a 1) Qualsiasi altra sessione leggerà i dati modificati dalla transazione come prima dell'istruzione "INIZIA" fintanto che la transazione non è stata eseguita. Non appena la transazione sarà impegnata, leggerà il nuovo valore del contatore. Il punto è che gli altri non devono aspettare e vedranno sempre una base di dati coerente.

a 2), 3) Perché non provarlo con "ACCESS EXCLUSIVE"? (vedi http://www.postgresql.org/docs/current/static/explicit-locking.html )

MODIFICA: Se non ti piace bloccare l'intera tabella con un blocco "ACCESS EXCLUSIVE", puoi anche utilizzare un "Blocco di avviso" (vedere la sezione 13.3.4 nel link sopra).


1

Se le letture che si desidera bloccare sono esecuzioni simultanee della stessa transazione solo con dati diversi, utilizzare un'istruzione UPDATE con clausola di ritorno.

Consulta i documenti sull'istruzione UPDATE. Per rispondere alla tua domanda sul punto di un blocco di riga esclusivo, è per impedire l'incoerenza dei dati da aggiornamenti simultanei. I lettori ottengono sempre una visione coerente del database.


1

Nell'esempio di codice che hai fornito, tutte le letture di quella riga vedranno il vecchio valore fino a quando la transazione non viene confermata.

1) Considerare che l'esempio di codice venga eseguito due volte contemporaneamente, con lo stesso valore di X. Se l'istanza A è stata eseguita select ... for update, ha bloccato quella riga fino al commit. L'istanza B, facendo lo stesso, si bloccherà nel tentativo di prendere la serratura allo stesso modo. Solo quando A si impegna, B può continuare. B otterrà quindi il valore A lasciato indietro nel suo aggiornamento finale.

Se Ydipende dal valore select ... for updateletto dalla query o da un'altra lettura nella parte "complessa", ciò avrà lo stesso effetto che se fossero eseguiti in serie: non si ottiene la condizione della competizione in cui si troverà uno dei risultati scartato.

Se Yè puramente il risultato delle complesse query nel mezzo, quindi eseguirlo in parallelo senza select ... for updateche entrambi eseguiranno lo stesso aggiornamento.

2) for shareconsentirà ad altre selezioni su quella riga di procedere, ma provocherà qualsiasi altro tentativo di bloccare select ... for updateo update ...bloccare fino al completamento. Se il codice di esempio viene eseguito due volte, contemporaneamente, si bloccherebbero al raggiungimento updatedell'istruzione, causando l'interruzione di uno di essi.

3) No. Fare questo è pericoloso, dal momento che un lettore potrebbe leggere un campo mezzo aggiornato. (Oppure leggi il resto di un campo aggiornato dopo l'inizio della lettura.) Può anche interrompere la coerenza, mostrando al lettore un valore aggiornato dopo l'inizio della sua transazione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.