Tôi đoán rằng nguồn gốc của vấn đề là một tham chiếu khóa ngoại vòng tròn trong các bảng của bạn.
BẢNG vm_action_info
==> NGOẠI KHÓA (last_completed_vm_task_id) TÀI LIỆU THAM KHẢO vm_task (id)
BẢNG vm_task
==> NGOẠI KHÓA (vm_action_info_id) TÀI LIỆU THAM KHẢO vm_action_info (id)
Giao dịch bao gồm hai bước:
Khi hai giao dịch sẽ cập nhật cùng một bản ghi trong vm_action_info
đồng thời, quá trình này sẽ kết thúc với một bế tắc.
Hãy xem 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 tôi thêm một bản ghi vào vm_task 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 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 đầu tiên thực hiện cập nhật:
session1=> update vm_action_info set last_on_demand_task_id=100, version=version+1
session1=> where id=2;
nhưng lệnh này bị treo và đang chờ khóa .....
thì phiên thứ 2 thực hiện 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=>
Đã phát hiện thấy khóa chết !!!
Điều này là do cả hai INSERT vào vm_task đều đặt khóa dùng chung 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 khóa ghi trên hàng này và bị treo vì hàng bị khóa bởi một giao dịch (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 ở chế độ ghi, nhưng nó bị khóa ở 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 khóa ghi 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;