[mysql] SELECT… FOR UPDATE를 언제 사용합니까?

의 사용 사례를 이해하도록 도와주세요 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 UPDATE2가 삭제되지 rooms않습니다. 그 맞습니까?

질문 2 : 언제 하나를 사용해야 SERIALIZABLE트랜잭션 격리가 대 READ_COMMITTEDSELECT ... FOR UPDATE?

답변은 이식 가능해야합니다 (데이터베이스에 한정되지 않음). 가능하지 않은 경우 이유를 설명해주세요.



답변

룸과 태그 간의 일관성을 유지하고 룸이 삭제 된 후 반환되지 않도록하는 유일한 이동식 방법은로 잠그는 것입니다 SELECT FOR UPDATE.

그러나 일부 시스템에서 잠금은 동시성 제어의 부작용이며 FOR UPDATE명시 적으로 지정하지 않고도 동일한 결과를 얻을 수 있습니다.


이 문제를 해결하려면 스레드 1이이어야합니다. 그러면 스레드 1이 완료 될 때까지 스레드 SELECT id FROM rooms FOR UPDATE2가 삭제되지 rooms않습니다. 그 맞습니까?

이것은 데이터베이스 시스템이 사용하는 동시성 제어에 따라 다릅니다.

  • MyISAMin MySQL(및 다른 여러 오래된 시스템)은 쿼리 기간 동안 전체 테이블을 잠급니다.

  • 에서 SQL Server, SELECT쿼리, 그들은 검사 한 기록 / 페이지 / 테이블에 대한 공유 잠금을 게재 할 동안 DML(이후 독점적으로 승진 또는 공유 잠금으로 강등된다) 쿼리 장소 업데이트 잠금. 배타적 잠금은 공유 잠금과 호환되지 않으므로 SELECT또는 DELETE쿼리는 다른 세션이 커밋 될 때까지 잠 깁니다.

  • 사용하는 데이터베이스 MVCC(예 Oracle: PostgreSQL,, MySQLwith InnoDB)에서 DML쿼리는 레코드의 복사본을 생성하며 (하나 또는 다른 방식으로) 일반적으로 독자는 작성자를 차단하지 않으며 그 반대도 마찬가지입니다. 이러한 데이터베이스의 경우 a SELECT 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이 잠겨 있으므로 다른 트랜잭션이 어떤 식 으로든 수정할 수 없으므로 트랜잭션 격리 수준은 여기서 차이가 없습니다.

또한 트랜잭션 격리 수준은 잠금과 무관합니다. 다른 격리 수준을 설정해도 트랜잭션에 의해 잠긴 다른 트랜잭션에서 행을 잠그고 업데이트 할 수 없습니다.

트랜잭션 격리 수준 (다른 수준에서)이 보장하는 것은 트랜잭션이 진행되는 동안 데이터의 일관성입니다.


답변