mos*_*she 15 postgresql postgresql-9.1
我正在使用postgres 9.1并在过度执行简单更新方法时获得死锁异常.
根据日志,由于同时执行两个相同的更新而发生死锁.
update public.vm_action_info set last_on_demand_task_id = $ 1,version = version + 1
两个相同的简单更新如何相互僵持?
我在日志中收到的错误
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
Run Code Online (Sandbox Code Playgroud)
架构是:
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 );
Run Code Online (Sandbox Code Playgroud)
kro*_*lko 19
我的猜测是问题的根源是表中的循环外键引用.
表vm_action_info
==> FOREIGN KEY(last_completed_vm_task_id)参考vm_task(id)
表vm_task
==> FOREIGN KEY(vm_action_info_id)参考vm_action_info(id)
事务包含两个步骤:
- 向任务表添加新条目
- 更新vm_action_info vm_task表中的相应条目.
当两个事务要同时更新vm_action_info表中的同一记录时,这将以死锁结束.
看看简单的测试用例:
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
;
Run Code Online (Sandbox Code Playgroud)
在会话1中,我们向vm_task添加一条记录,该记录在vm_action_info中引用id = 2
session1=> begin;
BEGIN
session1=> insert into vm_task values( 100, 0, 2 );
INSERT 0 1
session1=>
Run Code Online (Sandbox Code Playgroud)
在会话2的同时,另一个交易开始:
session2=> begin;
BEGIN
session2=> insert into vm_task values( 200, 0, 2 );
INSERT 0 1
session2=>
Run Code Online (Sandbox Code Playgroud)
然后第一个事务执行更新:
session1=> update vm_action_info set last_on_demand_task_id=100, version=version+1
session1=> where id=2;
Run Code Online (Sandbox Code Playgroud)
但是这个命令挂起并等待锁.....
然后第二个会话执行更新........
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=>
Run Code Online (Sandbox Code Playgroud)
检测到死锁!!!
这是因为对于vm_task的INSERT都由于外键引用而在vm_action_info表中的行id = 2上放置共享锁.然后第一次更新尝试在此行上放置写锁并挂起,因为该行被另一个(第二个)事务锁定.然后第二次更新尝试在写入模式下锁定相同的记录,但第一次事务将其锁定在共享模式下.这导致了僵局.
我认为如果你在vm_action_info中的记录上放置一个写锁,这可以避免,整个事务必须包含5个步骤:
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;
Run Code Online (Sandbox Code Playgroud)
可能只是您的系统异常繁忙。您说您只在查询“过度执行”时看到过这种情况。
看起来的情况是这样的:
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
Run Code Online (Sandbox Code Playgroud)
因此,我们似乎有四个事务同时更新该行。事务 4228275 尚未提交或回滚,并且阻碍了其他事务。其中两个一直在等待deadlock_timeout秒,否则我们不会看到超时。超时到期,死锁检测器查看,发现一堆相互交织的事务并取消其中一个。严格来说可能不是僵局,但我不确定探测器是否足够聪明来解决这个问题。
尝试以下之一:
可能 #3 是最简单的:-) 可能也想设置 log_lock_waits 以便您可以查看您的系统是否/何时处于这种压力之下。
| 归档时间: |
|
| 查看次数: |
37429 次 |
| 最近记录: |