PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Errore di deadlock nell'istruzione INSERT

Un modo per far fronte ai deadlock è disporre di un meccanismo di ripetizione che attende un intervallo casuale e tenta di eseguire nuovamente la transazione. L'intervallo casuale è necessario in modo che le transazioni in collisione non continuino a scontrarsi continuamente, causando quello che viene chiamato un blocco live, qualcosa di ancora più sgradevole per il debug. In realtà, le applicazioni più complesse avranno bisogno di un tale meccanismo di tentativi prima o poi quando dovranno gestire gli errori di serializzazione delle transazioni.

Ovviamente, se sei in grado di determinare la causa del deadlock, di solito è molto meglio eliminarlo o lo farà torna a morderti. Per quasi tutti i casi, anche quando la condizione di deadlock è rara, vale la pena spendere un po' di velocità effettiva e sovraccarico di codifica per ottenere i blocchi in ordine deterministico o ottenere blocchi più grossolani per evitare l'occasionale grande colpo di latenza e l'improvviso calo delle prestazioni quando si ridimensiona la concorrenza.

Quando si ottengono costantemente due deadlock di istruzioni INSERT, è molto probabile che si tratti di un problema di ordine di inserimento dell'indice univoco. Prova ad esempio quanto segue in due finestre di comando di psql:

Thread A           | Thread B
BEGIN;             | BEGIN;
                   | INSERT uniq=1;
INSERT uniq=2;     | 
                   | INSERT uniq=2; 
                   |   block waiting for thread A to commit or rollback, to
                   |   see if this is an unique key error.
INSERT uniq=1;     |
   blocks waiting  |
   for thread B,   |
     DEADLOCK      | 
                   V    

Di solito la migliore linea d'azione per risolvere questo problema è capire gli oggetti padre che proteggono tutte queste transazioni. La maggior parte delle applicazioni ha una o due entità primarie, come utenti o account, che sono buoni candidati per questo. Quindi tutto ciò di cui hai bisogno è che ogni transazione ottenga i blocchi sull'entità primaria che tocca tramite SELECT ... FOR UPDATE. Oppure, se ne tocchi diversi, bloccali tutti ma nello stesso ordine ogni volta (l'ordine per chiave primaria è una buona scelta).