2012-01-26 14 views
5

Czasami postgreSQL wywołuje zakleszczenia błędów.zakleszczenie postgresql

W wyzwalaczu dla tabeli ustawionej DO AKTUALIZACJI.

Tabela komentarz:

http://pastebin.com/L1a8dbn4

Rejestr (zdania wkładka jest skróconych):

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; 

A wyzwalacz na komentarzu tabeli:

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

Dlaczego to może dzieje?

Dzięki!

Odpowiedz

10

Są to dwa komentarze wstawiane z tym samym parametrem content_id. Samo wstawienie komentarza spowoduje zablokowanie SHARE w wierszu zawartości, aby zatrzymać kolejną transakcję usunięcia tego wiersza do czasu zakończenia pierwszej transakcji.

Jednakże, wyzwalacz następnie przechodzi do aktualizacji blokady do WYŁĄCZNEJ, i to może być zablokowane przez równoczesną transakcję wykonującą ten sam proces. Rozważ następującą sekwencję zdarzeń:

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) 

Tak- impas.

Jednym z rozwiązań jest natychmiast wziąć wyłączną blokadę na wiersz zawartości przed wstawienie komentarza. tj

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

Innym rozwiązaniem jest po prostu unikać tego „buforowane liczy” wzór całkowicie, chyba że można udowodnić, że jest to konieczne do wykonania. Jeśli tak, pomyśl o zachowaniu liczby przechowywanych w pamięci podręcznej w innym miejscu niż tabela treści - np. dedykowana tabela dla licznika. To także ograniczy ruch aktualizacji do tabeli treści za każdym razem, gdy dodany zostanie komentarz. A może po prostu ponownie wybierz liczbę i użyj memcached w aplikacji. Nie ma mowy o tym, że wszędzie tam, gdzie przechowujesz ten buforowany licznik, będzie to bardzo trudne, należy go bezpiecznie aktualizować.

+0

Dzięki! Świetna robota! :) – lestat

+0

Piszę test Pythona do wykrywania zakleszczenia, wybierz do aktualizacji wydaje się nie pomaga :( – lestat

+0

robisz 'wybierz do aktualizacji' i wstawiając komentarz w tej samej transakcji ?, czy rzeczywiście blokuje inny proces próbuje 'select for update' lub obaj przechodzą do insert? – araqnid

Powiązane problemy