sql >> Database >  >> RDS >> PostgreSQL

impasse in postgres op eenvoudige update-query

Ik vermoed dat de oorzaak van het probleem een ​​circulaire refererende sleutelreferentie in uw tabellen is.

TABEL vm_action_info
==> BUITENLANDSE SLEUTEL (last_completed_vm_task_id) REFERENTIES vm_task (id)

TABEL vm_task
==> BUITENLANDSE SLEUTEL (vm_action_info_id) REFERENTIES vm_action_info (id)

De transactie bestaat uit twee stappen:

Wanneer twee transacties hetzelfde record gaan bijwerken in de vm_action_info tafel tegelijkertijd, zal dit eindigen met een impasse.

Kijk naar een eenvoudige testcase:

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
      ;


In sessie 1 voegen we een record toe aan vm_task dat verwijst naar id=2 in vm_action_info

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

Tegelijkertijd begint in sessie 2 een andere transactie:

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

Vervolgens voert de 1e transactie de update uit:

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

maar dit commando blijft hangen en wacht op een slot.....

vervolgens voert de 2e sessie de update uit ........

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 gedetecteerd !!!

Dit komt doordat beide INSERT's in vm_task een gedeeld slot plaatsen op rij id=2 in de vm_action_info tabel vanwege de refererende sleutelreferentie. Vervolgens probeert de eerste update een schrijfblokkering op deze rij te plaatsen en blijft hangen omdat de rij is vergrendeld door een andere (tweede) transactie. Vervolgens probeert de tweede update hetzelfde record te vergrendelen in de schrijfmodus, maar het wordt vergrendeld in de gedeelde modus door de eerste transactie. En dit veroorzaakt een impasse.

Ik denk dat dit kan worden vermeden als je een schrijfblokkering op record plaatst in vm_action_info, de hele transactie moet uit 5 stappen bestaan:

 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. Stopwoorden en Stoplist gebruiken om SQL Server Full-Text Search (FTS) te verbeteren

  2. Exporteer eenvoudige Excel-gegevens naar MySQL met behulp van PHP

  3. Postgres maakt een tabel met een reeks externe sleutels

  4. Hoe efficiënt is het vanuit prestatieperspectief om een ​​tijdelijke MySQL-tabel te gebruiken voor een veelgebruikte websitefunctie?