Ja, du gør noget forkert.
Se et enkelt eksempel.
Session 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=#
Session 2 - på samme tid, men kun 10 ms senere
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;
Session 2 hænger ....... og venter på noget ....
tilbage i Session 1
postgres=# commit;
COMMIT
postgres=#
og igen Session 2
uservalue
-----------
1
(1 wiersz)
UPDATE 1
postgres=# commit;
COMMIT
postgres=#
Session 2 venter ikke længere og afslutter sin transaktion.
Og hvad er det endelige resultat?:
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)
To brugere tog samme værdi 1, men kun bruger 2 er registreret i tabellen
=======================REDIGER ===================================
I dette scenarie kan vi bruge SELECT .. FOR UPDATE og bruge en måde, hvorpå postgre revurderer forespørgslen i Read Committed Isolation Level-tilstand,
se dokumentation:http://www.postgresql.org/docs/9.2/static/transaction-iso.html
Kort sagt:
hvis en session låste rækken, og den anden session forsøger at låse den samme række, så vil den anden session "hænge" og vente på, at den første session forpligtes eller rulles tilbage. Når den første session begår transaktionen, så revurderer den anden session WHERE søgebetingelsen. Hvis søgebetingelsen ikke stemmer overens (fordi den første transaktion ændrede nogle kolonner), springer den anden session over den række og behandler en næste række, der matcher WHERE betingelser.
Bemærk:denne adfærd er anderledes i gentagelige læseisolationsniveauer. I så fald vil den anden session give fejl:kunne ikke serialisere adgang på grund af samtidig opdatering, og du skal prøve igen for hele transaktionen.
Vores forespørgsel kan muligvis se sådan ud:
select id from user_reservation_table
where usedyesno = false
order by id
limit 1
for update ;
og derefter:
Update .... where id = (id returned by SELECT ... FOR UPDATE)
Personligt foretrækker jeg at teste låsescenarier ved hjælp af almindelige, gamle konsolklienter (psql for postgree, mysql eller SQLPlus for oracle)
Så lad os teste vores forespørgsel i 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 #
Session 1 låste og opdaterede en række id=2
Og nu 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 ;
Session 2 hænger, mens du forsøger at låse række-id'et =2
OK, lad os begå session 1
session1 #commit;
COMMIT
session1 #
og se hvad der sker i session 2:
postgres-# for update ;
id
----
3
(1 wiersz)
Bingo - session 2 sprang over række-id =2 og valgte (og låste) række-id =3
Så vores sidste forespørgsel kan være:
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;
Nogle forbehold - dette eksempel er kun til dit testformål, og dets formål er at hjælpe med at forstå, hvordan låsning fungerer i postgre.
Faktisk vil denne forespørgsel serialisere adgang til tabellen, og den er ikke skalerbar og vil sandsynligvis fungere dårlig (langsom) i flerbrugermiljø.
Forestil dig, at 10 sessioner samtidig forsøger at få næste række fra denne tabel - hver session vil hænge og vente, indtil den forrige session vil commit.
Så brug ikke denne forespørgsel i produktionskode.
Vil du virkelig "finde og reservere næste værdi fra tabellen"? Hvorfor?
Hvis ja, skal du have en eller anden serialiseringsenhed (som denne forespørgsel, eller, måske nemmere at implementere, låse hele bordet), men dette vil være en flaskehals.