Những gì bạn muốn là CHỌN ... ĐỂ CẬP NHẬT từ trong bối cảnh giao dịch. CHỌN CẬP NHẬT đặt một khóa độc quyền trên các hàng đã chọn, giống như khi bạn đang thực hiện CẬP NHẬT. Nó cũng ngầm chạy ở mức cô lập READ CAMITED bất kể mức cô lập được đặt rõ ràng là gì. Chỉ cần lưu ý rằng CHỌN ... ĐỂ CẬP NHẬT rất tệ cho đồng thời và chỉ nên được sử dụng khi thực sự cần thiết. Nó cũng có xu hướng nhân lên trong một cơ sở mã khi mọi người cắt và dán.
Đây là một phiên ví dụ từ cơ sở dữ liệu Sakila thể hiện một số hành vi của các truy vấn FOR UPDATE.
Đầu tiên, để chúng ta rõ ràng, hãy đặt mức cô lập giao dịch thành ĐỌC LẠI. Điều này thường không cần thiết, vì nó là mức cô lập mặc định cho InnoDB:
session1> SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
session1> BEGIN;
session1> SELECT first_name, last_name FROM customer WHERE customer_id = 3;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| LINDA | WILLIAMS |
+------------+-----------+
1 row in set (0.00 sec)
Trong phiên khác, cập nhật hàng này. Linda kết hôn và đổi tên:
session2> UPDATE customer SET last_name = 'BROWN' WHERE customer_id = 3;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
Quay trở lại với phiên 1, vì chúng tôi đã ở chế độ ĐỌC LẠI, Linda vẫn là LINDA WILLIAM:
session1> SELECT first_name, last_name FROM customer WHERE customer_id = 3;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| LINDA | WILLIAMS |
+------------+-----------+
1 row in set (0.00 sec)
Nhưng bây giờ, chúng tôi muốn độc quyền truy cập vào hàng này, vì vậy chúng tôi gọi CHO CẬP NHẬT trên hàng. Lưu ý rằng bây giờ chúng tôi nhận được phiên bản mới nhất của hàng trở lại, đã được cập nhật trong phiên 2 bên ngoài giao dịch này. Đó không phải là ĐỌC LẠI, đó là ĐỌC CAM KẾT
session1> SELECT first_name, last_name FROM customer WHERE customer_id = 3 FOR UPDATE;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| LINDA | BROWN |
+------------+-----------+
1 row in set (0.00 sec)
Hãy kiểm tra khóa được đặt trong phiên1. Lưu ý rằng session2 không thể cập nhật hàng.
session2> UPDATE customer SET last_name = 'SMITH' WHERE customer_id = 3;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
Nhưng chúng ta vẫn có thể chọn từ nó
session2> SELECT c.customer_id, c.first_name, c.last_name, a.address_id, a.address FROM customer c JOIN address a USING (address_id) WHERE c.customer_id = 3;
+-------------+------------+-----------+------------+-------------------+
| customer_id | first_name | last_name | address_id | address |
+-------------+------------+-----------+------------+-------------------+
| 3 | LINDA | BROWN | 7 | 692 Joliet Street |
+-------------+------------+-----------+------------+-------------------+
1 row in set (0.00 sec)
Và chúng ta vẫn có thể cập nhật một bảng con có mối quan hệ khóa ngoại
session2> UPDATE address SET address = '5 Main Street' WHERE address_id = 7;
Query OK, 1 row affected (0.05 sec)
Rows matched: 1 Changed: 1 Warnings: 0
session1> COMMIT;
Một tác dụng phụ khác là bạn tăng đáng kể xác suất gây ra bế tắc.
Trong trường hợp cụ thể của bạn, bạn có thể muốn:
BEGIN;
SELECT id FROM `items` WHERE `status`='pending' LIMIT 1 FOR UPDATE;
-- do some other stuff
UPDATE `items` SET `status`='working', `updated`=NOW() WHERE `id`=<selected id>;
COMMIT;
Nếu phần "làm một số thứ khác" là không cần thiết và bạn thực sự không cần phải giữ thông tin về hàng đó, thì CHỌN CẬP NHẬT là không cần thiết và lãng phí và thay vào đó bạn chỉ có thể chạy cập nhật:
UPDATE `items` SET `status`='working', `updated`=NOW() WHERE `status`='pending' LIMIT 1;
Hy vọng điều này làm cho một số ý nghĩa.
items
WHEREstatus
= 'đang chờ xử lý' GIỚI HẠN 1 ĐỂ CẬP NHẬT;" và cả hai đều nhìn thấy cùng một hàng, sau đó một cái sẽ khóa cái kia. Tôi đã hy vọng bằng cách nào đó nó sẽ có thể vượt qua hàng bị khóa và goto mục tiếp theo đang chờ xử lý ..