2012-01-26 18 views
5

A volte postgresql genera errori di deadlock.deadlock postgresql

Nel trigger per la tabella impostata PER AGGIORNAMENTO.

Tabella commento:

http://pastebin.com/L1a8dbn4

log (frasi inserto è tagliati):

2012-01-26 17:21:06 MSK ERROR: deadlock detected 
2012-01-26 17:21:06 MSK DETAIL: Process 2754 waits for ExclusiveLock on tuple (40224,15) of relation 735493 of database 734745; blocked by process 2053. 
Process 2053 waits for ShareLock on transaction 25162240; blocked by process 2754. 
Process 2754: INSERT INTO comment (user_id, content_id, reply_id, text) VALUES (1756235868, 935967, 11378142, 'text1') RETURNING comment.id; 
Process 2053: INSERT INTO comment (user_id, content_id, reply_id, text) VALUES (4071267066, 935967, 11372945, 'text2') RETURNING comment.id; 
2012-01-26 17:21:06 MSK HINT: See server log for query details. 
2012-01-26 17:21:06 MSK CONTEXT: SQL statement "SELECT comments_count FROM content WHERE content.id = NEW.content_id FOR UPDATE" 
PL/pgSQL function "increase_comment_counter" line 5 at SQL statement 
2012-01-26 17:21:06 MSK STATEMENT: INSERT INTO comment (user_id, content_id, reply_id, text) VALUES (1756235868, 935967, 11378142, 'text1') RETURNING comment.id; 

E grilletto sul tavolo commento:

CREATE OR REPLACE FUNCTION increase_comment_counter() RETURNS TRIGGER AS $$ 
DECLARE 
comments_count_var INTEGER; 
BEGIN 
    SELECT INTO comments_count_var comments_count FROM content WHERE content.id = NEW.content_id FOR UPDATE; 
    UPDATE content SET comments_count = comments_count_var + 1, last_comment_dt = now() WHERE content.id = NEW.content_id; 
    RETURN NEW; 
END; 
$$ LANGUAGE plpgsql; 



CREATE TRIGGER increase_comment_counter_trigger AFTER INSERT ON comment FOR EACH ROW EXECUTE PROCEDURE increase_comment_counter(); 

perché può succede?

Grazie!

risposta

10

Questi sono due commenti inseriti con lo stesso content_id. Inserendo semplicemente il commento, verrà eliminato un blocco SHARE nella riga del contenuto, al fine di interrompere un'altra transazione eliminando quella riga fino al completamento della prima transazione.

Tuttavia, il trigger procede quindi all'aggiornamento del blocco su EXCLUSIVE e può essere bloccato da una transazione simultanea che esegue lo stesso processo. Si consideri la seguente sequenza di eventi:

Txn 2754      Txn 2053 
Insert Comment 
           Insert Comment 
Lock Content#935967 SHARE 
    (performed by fkey) 
           Lock Content#935967 SHARE 
           (performed by fkey) 
Trigger 
Lock Content#935967 EXCLUSIVE 
(blocks on 2053's share lock) 
           Trigger 
           Lock Content#935967 EXCLUSIVE 
           (blocks on 2754's share lock) 

So-deadlock.

Una soluzione è immediatamente prendere un blocco esclusivo sulla riga contenuto prima inserire il commento. vale a dire

SELECT 1 FROM content WHERE content.id = 935967 FOR UPDATE 
INSERT INTO comment(.....) 

Un'altra soluzione è semplicemente quello di evitare questo modello "conta nella cache" del tutto, tranne quando si può dimostrare che è necessario per le prestazioni. In tal caso, considera di tenere il conteggio memorizzato nella cache in un punto diverso dalla tabella del contenuto-- ad es. un tavolo dedicato per il bancone. Ciò ridurrà anche il traffico di aggiornamento alla tabella dei contenuti ogni volta che viene aggiunto un commento. O forse basta selezionare nuovamente il conteggio e usare memcached nell'applicazione. Non c'è modo di aggirare il fatto che ovunque si memorizzi questo conteggio nella cache sarà un punto di strozzatura, deve essere aggiornato in modo sicuro.

+0

Grazie! Ottimo lavoro! :) – lestat

+0

Scrivo alcuni test python per rilevare deadlock, selezionare per l'aggiornamento sembra non aiutare :( – lestat

+0

stai facendo il 'select for update' e inserendo il commento nella stessa transazione? È in realtà bloccando l'altro processo cercando 'selezionare per l'aggiornamento' o entrambi stanno passando per l'inserto? – araqnid