sql >> Databasteknik >  >> RDS >> PostgreSQL

dödläge i postgres på enkel uppdateringsfråga

Min gissning är att källan till problemet är en cirkulär främmande nyckelreferens i dina tabeller.

TABELL vm_action_info
==> UTLÄNDLIG KEY (last_completed_vm_task_id) REFERENCER vm_task (id)

TABELL vm_task
==> UTLÄNDSK NYCKEL (vm_action_info_id) REFERENSER vm_action_info (id)

Transaktionen består av två steg:

När två transaktioner kommer att uppdatera samma post i vm_action_info tabell samtidigt kommer detta att avslutas med ett dödläge.

Titta på enkla testfall:

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
      ;


I session 1 lägger vi till en post i vm_task som refererar till id=2 i vm_action_info

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

Samtidigt i session 2 börjar en annan transaktion:

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

Sedan utför den första transaktionen uppdateringen:

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

men det här kommandot hänger sig och väntar på ett lås.....

då utför den andra sessionen uppdateringen ........

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 upptäckt !!!

Detta beror på att båda INSERTs i vm_task placerar ett delat lås på rad id=2 i tabellen vm_action_info på grund av referensen för den främmande nyckeln. Sedan försöker den första uppdateringen placera ett skrivlås på denna rad och hänger sig eftersom raden är låst av en annan (andra) transaktion. Sedan försöker den andra uppdateringen låsa samma post i skrivläge, men den låses i delat läge av den första transaktionen. Och detta orsakar ett dödläge.

Jag tror att detta kan undvikas om du registrerar ett skrivlås i vm_action_info, hela transaktionen måste bestå av 5 steg:

 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;


  1. PHP ser inte mysql-tillägget

  2. visar data från sql från att klicka på svg-karta

  3. MySQL + JAVA Undantag:Före start av resultatuppsättning

  4. ORA-00936 saknar uttryck