2012-01-26 23 views
5

Đôi khi postgresql tăng deadlocks lỗi.deadlock postgresql

Kích hoạt cho bảng được cài đặt CHO CẬP NHẬT.

Bảng bình luận:

http://pastebin.com/L1a8dbn4

Log (câu INSERT được 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; 

Và kích hoạt trên bảng bình luận:

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

Tại sao nó có thể xảy ra?

Cảm ơn!

Trả lời

10

Đây là hai chú thích được chèn với cùng content_id. Chỉ việc chèn nhận xét sẽ đưa ra một khóa CHIA SẺ trên hàng nội dung, để ngăn chặn một giao dịch khác xóa hàng đó cho đến khi giao dịch đầu tiên hoàn tất.

Tuy nhiên, trình kích hoạt sau đó tiếp tục nâng cấp khóa thành ĐỘC QUYỀN, và điều này có thể bị chặn bởi một giao dịch đồng thời thực hiện cùng một quá trình. Xem xét chuỗi sự kiện sau đây:

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) 

Vì vậy, bế tắc.

Một giải pháp là ngay lập tức lấy khóa độc quyền trên hàng nội dung trước khi chèn nhận xét. tức là

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

Một giải pháp khác chỉ đơn giản là tránh mẫu "đếm bộ nhớ cache" này hoàn toàn, trừ khi bạn có thể chứng minh điều đó là cần thiết cho hiệu suất. Nếu vậy, hãy cân nhắc việc giữ số lượng được lưu trong bộ nhớ cache ở đâu đó ngoài bảng nội dung-- ví dụ: một bảng chuyên dụng cho bộ đếm. Điều đó cũng sẽ cắt giảm lưu lượng cập nhật xuống bảng nội dung mỗi khi nhận xét được thêm. Hoặc có thể chỉ cần chọn lại số lượng và sử dụng memcached trong ứng dụng. Không có vòng thực tế là bất cứ nơi nào bạn lưu trữ số lượng lưu trữ này sẽ là một điểm sặc, nó phải được cập nhật một cách an toàn.

+0

Cảm ơn! Làm tốt lắm! :) – lestat

+0

Tôi viết một số thử nghiệm python cho phát hiện deadlocks, chọn để cập nhật có vẻ không giúp: ( – lestat

+0

bạn đang làm 'chọn để cập nhật' và chèn các bình luận trong cùng một giao dịch? Là nó thực sự ngăn chặn quá trình khác cố gắng 'chọn để cập nhật' hoặc cả hai đều đi qua để chèn? – araqnid