Có, bạn đang làm sai điều gì đó.
Hãy xem ví dụ đơn giản.
Phần 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=#
Phiên 2 - cùng lúc, nhưng chỉ 10 mili giây sau
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;
Phiên 2 bị treo ....... và đang đợi điều gì đó ....
quay lại Phiên 1
postgres=# commit;
COMMIT
postgres=#
và một lần nữa Phiên 2
uservalue
-----------
1
(1 wiersz)
UPDATE 1
postgres=# commit;
COMMIT
postgres=#
Phiên 2 không phải chờ đợi nữa và kết thúc giao dịch của nó.
Và kết quả cuối cùng là gì ?:
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)
Hai người dùng có cùng giá trị 1, nhưng chỉ người dùng 2 được đăng ký trong bảng
======================CHỈNH SỬA ==================================
Trong trường hợp này, chúng ta có thể sử dụng SELECT .. FOR UPDATE và sử dụng một cách mà postgre đánh giá lại truy vấn trong chế độ Read Cam kết Mức cô lập,
xem tài liệu:http://www.postgresql.org/docs/9.2/static/transaction-iso.html
Tóm lại:
nếu một phiên đã khóa hàng và phiên khác đang cố gắng khóa cùng một hàng, thì phiên thứ hai sẽ "treo" và sẽ đợi phiên đầu tiên cam kết hoặc khôi phục. thực hiện giao dịch, sau đó phiên thứ hai sẽ đánh giá lại điều kiện tìm kiếm WHERE. Nếu điều kiện tìm kiếm không khớp (vì giao dịch đầu tiên đã thay đổi một số cột), thì phiên thứ hai sẽ bỏ qua hàng đó và sẽ xử lý một hàng tiếp theo khớp với WHERE điều kiện.
Lưu ý:hành vi này khác với các Mức cách ly đọc lặp lại. Trong trường hợp đó, phiên thứ hai sẽ gặp lỗi:không thể tuần tự hóa quyền truy cập do cập nhật đồng thời và bạn phải thử lại toàn bộ giao dịch.
Truy vấn của chúng tôi có thể trông giống như:
select id from user_reservation_table
where usedyesno = false
order by id
limit 1
for update ;
và sau đó:
Update .... where id = (id returned by SELECT ... FOR UPDATE)
Cá nhân tôi
Cá nhân tôi thích kiểm tra các tình huống khóa bằng cách sử dụng máy khách bảng điều khiển cũ, thuần túy (psql cho postgree, mysql hoặc SQLPlus cho oracle)
Vì vậy, hãy kiểm tra truy vấn của chúng tôi trong 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 #
Phiên 1 đã khóa và cập nhật một hàng id =2
Và bây giờ là phiên 2
session2 #begin;
BEGIN
session2 #select id from user_reservation_table
postgres-# where usedyesno = false
postgres-# order by id
postgres-# limit 1
postgres-# for update ;
Phiên 2 bị treo khi cố gắng khóa hàng id =2
OK, hãy cam kết phiên 1
session1 #commit;
COMMIT
session1 #
và xem điều gì sẽ xảy ra trong phiên 2:
postgres-# for update ;
id
----
3
(1 wiersz)
Bingo - phiên 2 đã bỏ qua hàng id =2 và chọn (và khóa) hàng id =3
Vì vậy, truy vấn cuối cùng của chúng tôi có thể là:
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;
Một số đặt chỗ - ví dụ này chỉ dành cho mục đích thử nghiệm của bạn và mục đích là giúp hiểu cách khóa hoạt động trong postgre.
Trên thực tế, truy vấn này sẽ tuần tự hóa quyền truy cập vào bảng và không thể mở rộng và có thể sẽ hoạt động xấu (chậm) trong môi trường nhiều người dùng.
Hãy tưởng tượng rằng 10 phiên đang cố gắng liên tục để lấy hàng tiếp theo từ bảng này- mỗi phiên sẽ bị treo và sẽ đợi cho đến khi phiên trước đó sẽ cam kết.
Vì vậy, không sử dụng truy vấn này trong mã sản xuất.
Bạn có thực sự muốn "tìm và đặt trước giá trị tiếp theo từ bảng" không? Tại sao?
Nếu có, bạn phải có một số thiết bị tuần tự hóa (như truy vấn này, hoặc, có thể dễ thực hiện hơn, khóa toàn bộ bảng), nhưng đây sẽ là một nút thắt cổ chai.