sql >> Databáze >  >> RDS >> PostgreSQL

postgresql - skript, který používá transakční bloky, nedokáže vytvořit všechny záznamy

Ano, něco děláte špatně.
Podívejte se na jednoduchý příklad.

Relace 1

postgres=# select * from user_reservation_table;
 id | usedyesno | userid | uservalue
----+-----------+--------+-----------
  1 | f         |      0 |         1
  2 | f         |      0 |         2
  3 | f         |      0 |         3
  4 | f         |      0 |         4
  5 | f         |      0 |         5
(5 wierszy)


postgres=# \set user 1
postgres=#
postgres=# begin;
BEGIN
postgres=# UPDATE user_reservation_table
postgres-# SET UsedYesNo = true, userid=:user
postgres-# WHERE uservalue IN(
postgres(#    SELECT uservalue FROM user_reservation_table
postgres(#    WHERE UsedYesNo=false Order By id ASC Limit 1)
postgres-# RETURNING uservalue;
 uservalue
-----------
         1
(1 wiersz)


UPDATE 1
postgres=#


Relace 2 - ve stejnou dobu, ale pouze o 10 ms později

postgres=# \set user 2
postgres=# begin;
BEGIN
postgres=# UPDATE user_reservation_table
postgres-# SET UsedYesNo = true, userid=:user
postgres-# WHERE uservalue IN(
postgres(#    SELECT uservalue FROM user_reservation_table
postgres(#    WHERE UsedYesNo=false Order By id ASC Limit 1)
postgres-# RETURNING uservalue;

Relace 2 visí ....... a na něco čeká ....

zpět do Relace 1

postgres=# commit;
COMMIT
postgres=#



a znovu Relace 2

 uservalue
-----------
         1
(1 wiersz)


UPDATE 1
postgres=# commit;
COMMIT
postgres=#

Relace 2 již nečeká a dokončuje svou transakci.

A jaký je konečný výsledek?:

postgres=# select * from user_reservation_table order by id;
 id | usedyesno | userid | uservalue
----+-----------+--------+-----------
  1 | t         |      2 |         1
  2 | f         |      0 |         2
  3 | f         |      0 |         3
  4 | f         |      0 |         4
  5 | f         |      0 |         5
(5 wierszy)

Dva uživatelé získali stejnou hodnotu 1, ale v tabulce je registrován pouze uživatel 2





=======================UPRAVIT ===================================

V tomto scénáři můžeme použít SELECT .. FOR UPDATE a využít způsob, kterým postgre přehodnotí dotaz v režimu Read Committed Isolation Level,
viz dokumentace:http://www.postgresql.org/docs/9.2/static/transaction-iso.html

Stručně řečeno:
pokud jedna relace uzamkla řádek a druhá relace se pokouší uzamknout stejný řádek, druhá relace se „zasekne“ a bude čekat na potvrzení nebo vrácení první relace. potvrdí transakci, pak druhá relace znovu vyhodnotí podmínku vyhledávání WHERE. Pokud se podmínka vyhledávání neshoduje (protože první transakce změnila některé sloupce), druhá relace tento řádek přeskočí a zpracuje další řádek, který odpovídá WHERE podmínky.

Poznámka:Toto chování se liší v úrovních izolace opakovatelného čtení. V takovém případě druhá relace vyvolá chybu:nebylo možné serializovat přístup kvůli souběžné aktualizaci a musíte opakovat celou transakci.

Náš dotaz může vypadat takto:

select id from user_reservation_table
where usedyesno = false
order by id
limit 1
for update ;

a poté:

  Update .... where id = (id returned by SELECT ... FOR UPDATE)



Osobně dávám přednost testování scénáře zamykání pomocí obyčejných, starých konzolových klientů (psql pro postgree, mysql nebo SQLPlus pro oracle)

Takže otestujte náš dotaz v psql:

session1 #select * from user_reservation_table order by id;
 id | usedyesno | userid | uservalue
----+-----------+--------+-----------
  1 | t         |      2 |         1
  2 | f         |      0 |         2
  3 | f         |      0 |         3
  4 | f         |      0 |         4
  5 | f         |      0 |         5
(5 wierszy)


session1 #begin;
BEGIN
session1 #select id from user_reservation_table
postgres-# where usedyesno = false
postgres-# order by id
postgres-# limit 1
postgres-# for update ;
 id
----
  2
(1 wiersz)


session1 #update user_reservation_table set usedyesno = true
postgres-# where id = 2;
UPDATE 1
session1 #

Relace 1 uzamčena a aktualizována o řádek id=2

A nyní session2

session2 #begin;
BEGIN
session2 #select id from user_reservation_table
postgres-# where usedyesno = false
postgres-# order by id
postgres-# limit 1
postgres-# for update ;

Relace 2 se zablokuje při pokusu o zamknutí řádku id =2

OK, pojďme potvrdit relaci 1

session1 #commit;
COMMIT
session1 #

a podívejte se, co se stane v relaci 2:

postgres-# for update ;
 id
----
  3
(1 wiersz)

Bingo – relace 2 přeskočila řádek id =2 a vybrala (a uzamkla) řádek id =3


Takže náš poslední dotaz by mohl znít:

update user_reservation_table
set usedyesno = true
where id = (
   select id from user_reservation_table
   where usedyesno = false
   order by id
   limit 1
   for update
) RETURNING uservalue;

Nějaká rezervace – tento příklad je pouze pro váš testovací účel a jeho účelem je pomoci pochopit, jak funguje zamykání v postgre.
Tento dotaz ve skutečnosti serializuje přístup k tabulce a není škálovatelný a pravděpodobně bude fungovat špatné (pomalé) ve víceuživatelském prostředí.
Představte si, že se 10 relací současně pokouší získat další řádek z této tabulky – každá relace se zablokuje a bude čekat, dokud se předchozí relace nepotvrdí.
Takže nepoužívejte tento dotaz v produkčním kódu.
Opravdu chcete "najít a rezervovat další hodnotu z tabulky"? Proč ?
Pokud ano, musíte mít nějaké zařízení pro serializaci (jako je tento dotaz, nebo, možná snadněji implementovatelné, uzamčení celé tabulky), ale to bude překážka.




  1. Agregace dat mongodb vs mysql

  2. index dataframe.to_sql jako primární klíč v postgresql

  3. Problém „KDE A“ MySQL

  4. Proč nepoužít čas vytvoření záznamu jako primární klíč?