2012-01-26 29 views
5

En ocasiones, postgresql provoca un bloqueo de error.postgresql deadlock

En desencadenador para tabla establecida PARA ACTUALIZAR.

comentario Tabla:

http://pastebin.com/L1a8dbn4

log (sentencias INSERT cutted):

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; 

Y gatillo de comentario de la tabla:

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(); 

por lo que puede sucede?

Gracias!

Respuesta

10

Se están insertando dos comentarios con el mismo content_id. La mera inserción del comentario eliminará un bloqueo de SHARE en la fila de contenido, para detener otra transacción que borre esa fila hasta que se complete la primera transacción.

Sin embargo, el disparador continúa actualizando el bloqueo a EXCLUSIVO, y esto puede ser bloqueado por una transacción simultánea que realiza el mismo proceso. Tenga en cuenta la siguiente secuencia de eventos:

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 solución es inmediatamente tomar un bloqueo exclusivo en la fila de contenido antes de insertar el comentario. es decir,

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

Otra solución es simplemente evitar este patrón de "recuento de caché" por completo, excepto donde se puede demostrar que es necesaria para el funcionamiento. De ser así, considere mantener el recuento en caché en otro lugar que no sea la tabla de contenido, p. Ej. una mesa dedicada para el mostrador. Eso también reducirá el tráfico de actualización a la tabla de contenido cada vez que se agregue un comentario. O tal vez simplemente vuelva a seleccionar el conteo y use memcached en la aplicación. No hay forma de evitar el hecho de que donde sea que almacene este recuento en caché será un punto de estrangulamiento, debe actualizarse de manera segura.

+0

Gracias! ¡Buen trabajo! :) – lestat

+0

Escribo una prueba de Python para detectar interbloqueos, seleccionar para la actualización parece que no ayuda :( – lestat

+0

estás haciendo 'seleccionar para actualizar' e insertando el comentario en la misma transacción? En realidad está bloqueando el otro proceso intentando 'seleccionar para actualizar' o ambos están pasando por la inserción? – araqnid

Cuestiones relacionadas