원하는 것은 트랜잭션 컨텍스트 내에서 SELECT ... FOR UPDATE 입니다. UPDATE를 실행하는 것처럼 SELECT FOR UPDATE는 선택된 행에 단독 잠금을 설정합니다. 또한 격리 수준이 명시 적으로 설정된 것과 상관없이 READ COMMITTED 격리 수준에서 암시 적으로 실행됩니다. SELECT ... FOR UPDATE는 동시성에 매우 좋지 않으므로 꼭 필요한 경우에만 사용해야합니다. 또한 사람들이 잘라서 붙여 넣을 때 코드베이스가 증가하는 경향이 있습니다.
다음은 FOR UPDATE 쿼리의 일부 동작을 보여주는 Sakila 데이터베이스의 세션 예입니다.
먼저, 명확하게하기 위해 트랜잭션 격리 수준을 REPEATABLE READ로 설정합니다. 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)
다른 세션에서이 행을 업데이트하십시오. Linda는 결혼하여 이름을 변경했습니다.
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
session1로 돌아가서, 우리가 반복해서 읽을 수 있었기 때문에 Linda는 여전히 LINDA WILLIAMS입니다.
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)
그러나 이제이 행에 대한 독점 액세스 권한을 원하므로 행에서 FOR UPDATE를 호출합니다. 이제이 트랜잭션 외부의 session2에서 업데이트 된 최신 버전의 행이 다시 나타납니다. 반복 가능한 읽기가 아니며 읽기가 완결되었습니다.
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)
session1에 설정된 잠금을 테스트 해 봅시다. session2는 행을 업데이트 할 수 없습니다.
session2> UPDATE customer SET last_name = 'SMITH' WHERE customer_id = 3;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
그러나 우리는 여전히 그것에서 선택할 수 있습니다
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)
그리고 외래 키 관계로 자식 테이블을 업데이트 할 수 있습니다
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;
또 다른 부작용은 교착 상태를 일으킬 가능성이 크게 증가한다는 것입니다.
특정한 경우에는 다음을 원할 것입니다.
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;
"다른 작업을 수행하십시오"가 불필요하고 실제로 행에 대한 정보를 유지할 필요가없는 경우, SELECT FOR UPDATE는 불필요하고 낭비이며 대신 업데이트를 실행할 수 있습니다.
UPDATE `items` SET `status`='working', `updated`=NOW() WHERE `status`='pending' LIMIT 1;
이것이 의미가 있기를 바랍니다.
items
WHEREstatus
= 'pending'LIMIT 1 FOR UPDATE;" 와 함께 나올 때 내 문제를 해결하지 못하는 것 같습니다 . 둘 다 같은 줄을 본 후 하나는 다른 하나를 잠급니다. 나는 어떻게 든 그것이 잠긴 행을 우회하고 보류중인 다음 항목으로 갈 수 있기를 바라고있었습니다.