2012-01-26 5 views
5

Иногда postgresql вызывает ошибки взаимоблокировки.postgresql тупик

В триггере для таблицы, установленной для ОБНОВЛЕНИЯ.

Таблица Комментарий:

http://pastebin.com/L1a8dbn4

LOG (ВСТАВИТЬ предложения является вырезанные):

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; 

И триггер на столе комментарий:

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

Почему это может случится?

Спасибо!

ответ

10

Это два комментария, вставляемых с тем же content_id. Простое вложение комментария выведет блокировку SHARE в строке содержимого, чтобы остановить другую транзакцию, удалив эту строку до завершения первой транзакции.

Однако триггер затем обновляет блокировку до ЭКСКЛЮЗИВ, и это может быть заблокировано параллельной транзакцией, выполняющей тот же процесс. Рассмотрим следующую последовательность событий:

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.

Одним из решений является сразу взять эксклюзивную блокировку на строку содержимого, прежде чем вставки комментария. т.е.

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

Другим решением является просто избегать «кэшированные отсчитывает» образец полностью, за исключением того, где вы можете доказать, что это необходимо для работы. Если это так, подумайте о сохранении кешированного счета где-то, кроме таблицы содержимого. специальная таблица для счетчика. Это также уменьшит трафик обновления в таблице содержимого каждый раз, когда добавляется комментарий. Или, может быть, просто повторно выберите счетчик и используйте memcached в приложении. Нельзя обойти тот факт, что везде, где вы храните этот кеш-счет, это будет точка задувания, его нужно безопасно обновлять.

+0

Спасибо! Отличная работа! :) – lestat

+0

Я пишу некоторый тест python для обнаружения тупиков, выбор для обновления кажется не помогает :( – lestat

+0

вы делаете 'select for update' и вставляете комментарий в ту же транзакцию? Это фактически блокирует другой процесс, пытающийся 'select for update' или они оба переходят к вставке? – araqnid

Смежные вопросы