의 사용 사례를 이해하도록 도와주세요 SELECT ... FOR UPDATE
.
질문 1 : 다음은 언제 SELECT ... FOR UPDATE
사용해야하는지에 대한 좋은 예 입니까?
주어진:
- 방 [id]
- 태그 [ID, 이름]
- room_tags [room_id, tag_id]
- room_id 및 tag_id는 외래 키입니다.
애플리케이션은 모든 룸과 해당 태그를 나열하려고하지만 태그가없는 룸과 제거 된 룸을 구분해야합니다. SELECT … FOR UPDATE를 사용하지 않으면 다음과 같은 일이 발생할 수 있습니다.
- 처음에는 :
- 방 포함
[id = 1]
- 태그에는
[id = 1, name = 'cats']
- room_tags 포함
[room_id = 1, tag_id = 1]
- 방 포함
- 스레드 1 :
SELECT id FROM rooms;
returns [id = 1]
- 스레드 2 :
DELETE FROM room_tags WHERE room_id = 1;
- 스레드 2 :
DELETE FROM rooms WHERE id = 1;
- 스레드 2 : [트랜잭션 커밋]
- 스레드 1 :
SELECT tags.name FROM room_tags, tags WHERE room_tags.tag_id = 1 AND tags.id = room_tags.tag_id;
- 빈 목록을 반환
이제 스레드 1은 방 1에 태그가 없다고 생각하지만 실제로 방은 제거되었습니다. 이 문제를 해결하려면 스레드 1이이어야합니다. 그러면 스레드 1이 완료 될 때까지 스레드 SELECT id FROM rooms FOR UPDATE
2가 삭제되지 rooms
않습니다. 그 맞습니까?
질문 2 : 언제 하나를 사용해야 SERIALIZABLE
트랜잭션 격리가 대 READ_COMMITTED
와 SELECT ... FOR UPDATE
?
답변은 이식 가능해야합니다 (데이터베이스에 한정되지 않음). 가능하지 않은 경우 이유를 설명해주세요.
답변
룸과 태그 간의 일관성을 유지하고 룸이 삭제 된 후 반환되지 않도록하는 유일한 이동식 방법은로 잠그는 것입니다 SELECT FOR UPDATE
.
그러나 일부 시스템에서 잠금은 동시성 제어의 부작용이며 FOR UPDATE
명시 적으로 지정하지 않고도 동일한 결과를 얻을 수 있습니다.
이 문제를 해결하려면 스레드 1이이어야합니다. 그러면 스레드 1이 완료 될 때까지 스레드
SELECT id FROM rooms FOR UPDATE
2가 삭제되지rooms
않습니다. 그 맞습니까?
이것은 데이터베이스 시스템이 사용하는 동시성 제어에 따라 다릅니다.
-
MyISAM
inMySQL
(및 다른 여러 오래된 시스템)은 쿼리 기간 동안 전체 테이블을 잠급니다. -
에서
SQL Server
,SELECT
쿼리, 그들은 검사 한 기록 / 페이지 / 테이블에 대한 공유 잠금을 게재 할 동안DML
(이후 독점적으로 승진 또는 공유 잠금으로 강등된다) 쿼리 장소 업데이트 잠금. 배타적 잠금은 공유 잠금과 호환되지 않으므로SELECT
또는DELETE
쿼리는 다른 세션이 커밋 될 때까지 잠 깁니다. -
사용하는 데이터베이스
MVCC
(예Oracle
:PostgreSQL
,,MySQL
withInnoDB
)에서DML
쿼리는 레코드의 복사본을 생성하며 (하나 또는 다른 방식으로) 일반적으로 독자는 작성자를 차단하지 않으며 그 반대도 마찬가지입니다. 이러한 데이터베이스의 경우 aSELECT FOR UPDATE
가 유용합니다. 마찬가지로 다른 세션이 커밋 될 때까지SELECT
또는DELETE
쿼리를 잠급니다SQL Server
.
때 하나를 사용해야
REPEATABLE_READ
대 트랜잭션 격리를READ_COMMITTED
가진SELECT ... FOR UPDATE
?
일반적으로 REPEATABLE READ
가상 행 (수정되지 않고 다른 트랜잭션에서 나타나거나 사라진 행)을 금지하지 않습니다.
-
년
Oracle
및 이전PostgreSQL
버전,REPEATABLE READ
실제로 동의어입니다SERIALIZABLE
. 기본적으로 이것은 트랜잭션이 시작된 후에 변경된 내용을 볼 수 없음을 의미합니다. 따라서이 설정에서 마지막Thread 1
쿼리는 방이 삭제 된 적이없는 것처럼 반환합니다 (원하는 것일 수도 있고 아닐 수도 있음). 삭제 한 후 방을 표시하지 않으려면 행을 잠 가야합니다.SELECT FOR UPDATE
-
에서
InnoDB
,REPEATABLE READ
그리고SERIALIZABLE
다른 사항은 다음과 같습니다 독자SERIALIZABLE
효과적으로 동시 방지들이 평가 기록에 대한 모드 설정 다음 키 잠금,DML
그들에이. 따라서SELECT FOR UPDATE
직렬화 가능 모드는 필요하지 않지만REPEATABLE READ
또는READ COMMITED
.
격리 모드에 대한 표준은 쿼리에서 특정 단점을 보지 않도록 규정하지만 방법 (잠금 사용 또는 사용)은 정의하지 않습니다 MVCC
.
“필요하지 않다”고 말할 때 ” SELECT FOR UPDATE
특정 데이터베이스 엔진 구현의 부작용 때문에”를 추가 했어야했습니다.
답변
짧은 답변 :
Q1 : 예.
Q2 : 어떤 것을 사용하든 상관 없습니다.
긴 대답 :
select ... for update
의미하는대로 A 는 특정 행을 선택하지만 현재 트랜잭션에 의해 이미 업데이트 된 것처럼 (또는 ID 업데이트가 수행 된 것처럼) 잠급니다. 이를 통해 현재 트랜잭션에서 다시 업데이트 한 다음 커밋 할 수 있습니다. 다른 트랜잭션이 이러한 행을 어떤 방식 으로든 수정할 수 없습니다.
그것을 보는 또 다른 방법은 다음 두 명령문이 원자 적으로 실행되는 것과 같습니다.
select * from my_table where my_condition;
update my_table set my_column = my_column where my_condition;
영향을받는 행 my_condition
이 잠겨 있으므로 다른 트랜잭션이 어떤 식 으로든 수정할 수 없으므로 트랜잭션 격리 수준은 여기서 차이가 없습니다.
또한 트랜잭션 격리 수준은 잠금과 무관합니다. 다른 격리 수준을 설정해도 트랜잭션에 의해 잠긴 다른 트랜잭션에서 행을 잠그고 업데이트 할 수 없습니다.
트랜잭션 격리 수준 (다른 수준에서)이 보장하는 것은 트랜잭션이 진행되는 동안 데이터의 일관성입니다.