2013-08-18 41 views
14

Tôi đang làm việc với postgres 9.1 và nhận được ngoại lệ bế tắc dưới sự thực thi quá mức của một phương thức cập nhật đơn giản.bế tắc trong postgres trên truy vấn cập nhật đơn giản

Theo nhật ký, bế tắc xảy ra do thực hiện hai cập nhật giống hệt nhau cùng một lúc.

cập nhật public.vm_action_info thiết last_on_demand_task_id = $ 1, version = phiên bản + 1

Làm thế nào để hai bản cập nhật đơn giản giống hệt nhau có thể bế tắc lẫn nhau?

Các lỗi mà tôi nhận được trong nhật ký

2013-08-18 11:00:24 IDT HINT: See server log for query details. 
2013-08-18 11:00:24 IDT STATEMENT: update public.vm_action_info set last_on_demand_task_id=$1, version=version+1 where id=$2 
2013-08-18 11:00:25 IDT ERROR: deadlock detected 
2013-08-18 11:00:25 IDT DETAIL: Process 31533 waits for ShareLock on transaction 4228275; blocked by process 31530. 
     Process 31530 waits for ExclusiveLock on tuple (0,68) of relation 70337 of database 69205; blocked by process 31533. 
     Process 31533: update public.vm_action_info set last_on_demand_task_id=$1, version=version+1 where id=$2 
     Process 31530: update public.vm_action_info set last_on_demand_task_id=$1, version=version+1 where id=$2 
2013-08-18 11:00:25 IDT HINT: See server log for query details. 
2013-08-18 11:00:25 IDT STATEMENT: update public.vm_action_info set last_on_demand_task_id=$1, version=version+1 where id=$2 
2013-08-18 11:00:25 IDT ERROR: deadlock detected 
2013-08-18 11:00:25 IDT DETAIL: Process 31530 waits for ExclusiveLock on tuple (0,68) of relation 70337 of database 69205; blocked by process 31876. 
     Process 31876 waits for ShareLock on transaction 4228275; blocked by process 31530. 
     Process 31530: update public.vm_action_info set last_on_demand_task_id=$1, version=version+1 where id=$2 
     Process 31876: update public.vm_action_info set last_on_demand_task_id=$1, version=version+1 where id=$2 

schema là:

CREATE TABLE vm_action_info(
    id integer NOT NULL, 
    version integer NOT NULL DEFAULT 0, 
    vm_info_id integer NOT NULL, 
last_exit_code integer, 
    bundle_action_id integer NOT NULL, 
    last_result_change_time numeric NOT NULL, 
    last_completed_vm_task_id integer, 
    last_on_demand_task_id bigint, 
    CONSTRAINT vm_action_info_pkey PRIMARY KEY (id), 
    CONSTRAINT vm_action_info_bundle_action_id_fk FOREIGN KEY (bundle_action_id) 
     REFERENCES bundle_action (id) MATCH SIMPLE 
     ON UPDATE NO ACTION ON DELETE CASCADE, 
    CONSTRAINT vm_discovery_info_fk FOREIGN KEY (vm_info_id) 
     REFERENCES vm_info (id) MATCH SIMPLE 
     ON UPDATE NO ACTION ON DELETE CASCADE, 
    CONSTRAINT vm_task_last_on_demand_task_fk FOREIGN KEY (last_on_demand_task_id) 
     REFERENCES vm_task (id) MATCH SIMPLE 
     ON UPDATE NO ACTION ON DELETE NO ACTION, 

    CONSTRAINT vm_task_last_task_fk FOREIGN KEY (last_completed_vm_task_id) 
     REFERENCES vm_task (id) MATCH SIMPLE 
     ON UPDATE NO ACTION ON DELETE NO ACTION 
) 
WITH (OIDS=FALSE); 

ALTER TABLE vm_action_info 
    OWNER TO vadm; 

-- Index: vm_action_info_vm_info_id_index 

-- DROP INDEX vm_action_info_vm_info_id_index; 

CREATE INDEX vm_action_info_vm_info_id_index 
    ON vm_action_info 
    USING btree (vm_info_id); 

CREATE TABLE vm_task 
(
    id integer NOT NULL, 
    version integer NOT NULL DEFAULT 0, 
    vm_action_info_id integer NOT NULL, 
    creation_time numeric NOT NULL DEFAULT 0, 
    task_state text NOT NULL, 
    triggered_by text NOT NULL, 
    bundle_param_revision bigint NOT NULL DEFAULT 0, 
    execution_time bigint, 
    expiration_time bigint, 
    username text, 
    completion_time bigint, 
    completion_status text, 
    completion_error text, 
    CONSTRAINT vm_task_pkey PRIMARY KEY (id), 
    CONSTRAINT vm_action_info_fk FOREIGN KEY (vm_action_info_id) 
    REFERENCES vm_action_info (id) MATCH SIMPLE 
    ON UPDATE NO ACTION ON DELETE CASCADE 
) 
WITH (
OIDS=FALSE 
); 
ALTER TABLE vm_task 
    OWNER TO vadm; 

-- Index: vm_task_creation_time_index 

-- DROP INDEX vm_task_creation_time_index  ; 

CREATE INDEX vm_task_creation_time_index 
    ON vm_task 
    USING btree 
(creation_time); 
+0

Họ không phải là đơn giản. Có một FK constrant trên lĩnh vực này (mà kết quả trong một chỉ số cần phải được cập nhật) Có lẽ thử trì hoãn ban đầu trì hoãn? (không nghĩ rằng nó có thể làm cho bất kỳ sự khác biệt) – wildplasser

+1

Tôi không muốn thay đổi các hạn chế FK như tôi không hoàn toàn chắc chắn như thế nào nó sẽ có hiệu lực hệ thống trong genral. Thêm một hạn chế trong mã mà chỉ truy vấn duy nhất có thể thực hiện tại một thời điểm nhất định giải quyết vấn đề nhưng tôi không hiểu làm thế nào một truy vấn có thể gây ra một bế tắc với chính nó. Tất cả các ổ khóa đều được mua theo thứ tự như vậy về mặt tehoritically nó không nên xảy ra. Có khả năng rằng các hậu duệ bí ẩn phát hiện một bế tắc không thực sự tồn tại? – moshe

+0

Bạn đã viết 'tất cả khóa được mua cùng thứ tự', có nghĩa là nó không chỉ là một bản cập nhật đơn giản, nhưng toàn bộ giao dịch bao gồm nhiều lệnh khóa hơn bản cập nhật đơn này? Nếu có, vui lòng cho chúng tôi biết toàn bộ mã. – krokodilko

Trả lời

4

Nó chỉ có thể là hệ thống của bạn là đặc biệt bận rộn. Bạn nói rằng bạn đã chỉ nhìn thấy điều này với "thực thi quá mức" của truy vấn.

gì dường như là tình hình là thế này:

pid=31530 wants to lock tuple (0,68) on rel 70337 (vm_action_info I suspect) for update 
    it is waiting behind pid=31533, pid=31876 
pid=31533 is waiting behind transaction 4228275 
pid=31876 is waiting behind transaction 4228275 

Vì vậy - chúng tôi có những gì dường như có bốn giao dịch tất cả cập nhật hàng này cùng một lúc. Giao dịch 4228275 chưa cam kết hoặc quay lại và đang nắm giữ những người khác. Hai trong số họ đã chờ đợi deadlock_timeout giây nếu không chúng tôi sẽ không thấy thời gian chờ. Hết thời gian chờ, thiết bị dò tìm bế tắc sẽ nhìn, thấy một loạt các giao dịch đan xen và hủy bỏ một trong số chúng. Có thể không hoàn toàn là bế tắc, nhưng tôi không chắc liệu máy dò có đủ thông minh để tìm ra điều đó không.

Hãy thử một trong số:

  1. Giảm tỷ lệ cập nhật
  2. Nhận một máy chủ nhanh hơn
  3. Tăng deadlock_timeout

lẽ # 3 là đơn giản nhất :-) Có thể muốn thiết lập log_lock_waits cũng vậy nên bạn có thể thấy nếu/khi hệ thống của bạn bị căng thẳng.

+0

Với tốc độ cập nhật chậm hơn kịch bản này không xảy ra. Về đề xuất # 3: theo tài liệu postgres, tham số deadloak_timeout chỉ xác định khoảng thời gian trước khi cơ chế phát hiện deadlock được thực hiện và không có tác động đến việc tình trạng bế tắc có được khai báo hay không. từ tài liệu: "của anh ấy là lượng thời gian, tính bằng mili giây, để chờ trên khóa trước khi kiểm tra xem có tình trạng bế tắc hay không. Việc kiểm tra bế tắc là tương đối tốn kém, vì vậy máy chủ không chạy nó mỗi lần chờ khóa " – moshe

+0

Bản nâng cấp lên phiên bản 9.2 cũng có thể giúp, nó có một số cải tiến về hành vi khóa và tốc độ tổng thể. –

+0

Điều kiện là bế tắc, trừ khi một giao dịch bị hủy bỏ. –

14

Đoán của tôi là nguồn của sự cố là tham chiếu khóa ngoài tròn trong bảng của bạn.

TABLE vm_action_info
==> FOREIGN KEY (last_completed_vm_task_id) Tài liệu tham khảo vm_task (id)

TABLE vm_task
==> FOREIGN KEY (vm_action_info_id) Tài liệu tham khảo vm_action_info (id)

Các giao dịch bao gồm hai bước sau:

  1. thêm một mục mới để có thể thao tác bảng
  2. cập nhật mục trong vm_action_in tương ứng fo bảng vm_task.

Khi hai giao dịch sẽ cập nhật cùng một kỷ lục trong bảng vm_action_info cùng một lúc, điều này sẽ kết thúc với một bế tắc.

Nhìn vào trường hợp thử nghiệm đơn giản:

CREATE TABLE vm_task 
(
    id integer NOT NULL, 
    version integer NOT NULL DEFAULT 0, 
    vm_action_info_id integer NOT NULL, 
    CONSTRAINT vm_task_pkey PRIMARY KEY (id) 
) 
WITH (OIDS=FALSE); 

insert into vm_task values 
(0, 0, 0), (1, 1, 1), (2, 2, 2); 

CREATE TABLE vm_action_info(
    id integer NOT NULL, 
    version integer NOT NULL DEFAULT 0, 
    last_on_demand_task_id bigint, 
    CONSTRAINT vm_action_info_pkey PRIMARY KEY (id) 
) 
WITH (OIDS=FALSE); 
insert into vm_action_info values 
(0, 0, 0), (1, 1, 1), (2, 2, 2); 

alter table vm_task 
add CONSTRAINT vm_action_info_fk FOREIGN KEY (vm_action_info_id) 
    REFERENCES vm_action_info (id) MATCH SIMPLE 
    ON UPDATE NO ACTION ON DELETE CASCADE 
    ; 
Alter table vm_action_info 
add CONSTRAINT vm_task_last_on_demand_task_fk FOREIGN KEY (last_on_demand_task_id) 
     REFERENCES vm_task (id) MATCH SIMPLE 
     ON UPDATE NO ACTION ON DELETE NO ACTION 
     ; 


Trong phiên 1 chúng ta thêm một kỷ lục để vm_task mà tham chiếu đến id = 2 trong vm_action_info

session1=> begin; 
BEGIN 
session1=> insert into vm_task values(100, 0, 2); 
INSERT 0 1 
session1=> 

Đồng thời trong phiên 2 một một giao dịch khác bắt đầu:

session2=> begin; 
BEGIN 
session2=> insert into vm_task values(200, 0, 2); 
INSERT 0 1 
session2=> 

Sau đó, giao dịch thứ nhất thực hiện cập nhật ăn:

session1=> update vm_action_info set last_on_demand_task_id=100, version=version+1 
session1=> where id=2; 

nhưng điều này lệnh treo và đang chờ đợi một khóa .....

sau đó phiên thứ 2 thực hiện việc cập nhật ........

session2=> update vm_action_info set last_on_demand_task_id=200, version=version+1 where id=2; 
BŁĄD: wykryto zakleszczenie 
SZCZEGÓŁY: Proces 9384 oczekuje na ExclusiveLock na krotka (0,5) relacji 33083 bazy danych 16393; zablokowany przez 380 
8. 
Proces 3808 oczekuje na ShareLock na transakcja 976; zablokowany przez 9384. 
PODPOWIEDŹ: Przejrzyj dziennik serwera by znaleźć szczegóły zapytania. 
session2=> 

Deadlock phát hiện !!!

Điều này là do cả hai INSERT vào vm_task đặt một khóa chia sẻ trên hàng id = 2 trong bảng vm_action_info do tham chiếu khóa ngoài. Sau đó, bản cập nhật đầu tiên cố gắng đặt một khóa ghi trên hàng này và bị treo vì hàng bị khóa bởi giao dịch thứ hai (thứ hai) khác. Sau đó, bản cập nhật thứ hai cố gắng khóa cùng một bản ghi trong chế độ ghi, nhưng nó bị khóa trong chế độ chia sẻ bởi giao dịch đầu tiên. Và điều này gây ra bế tắc.

Tôi nghĩ rằng điều này có thể tránh được nếu bạn đặt một khóa ghi trên kỷ lục trong vm_action_info, toàn bộ giao dịch phải bao gồm 5 bước:

begin; 
select * from vm_action_info where id=2 for update; 
insert into vm_task values(100, 0, 2); 
update vm_action_info set last_on_demand_task_id=100, 
     version=version+1 where id=2; 
commit; 
Các vấn đề liên quan