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


119

의 사용 사례를 이해하도록 도와주세요 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?

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


2
어떤 RDBMS를 사용하고 있습니까?
Quassnoi

2
@Quassnoi, 질문 맨 아래에서 언급했듯이 휴대용 (데이터베이스 별이 아닌) 솔루션을 찾고 있습니다.
Gili

2
옵션 REPEATABLE_READREAD_COMMITTED휴대용 옵션이 있습니까? 내가 얻은 유일한 결과는 MSSQL 서버에 대한 것입니다
Billy ONeal 2013 년

3
@BillyONeal : 격리 모드는 허용하지 않는 단점을 보지 않도록 보장하지만 허용하는 단점에 대해서는 아무 말도하지 않습니다. 즉, READ COMMITTED모드 설정 은 실제로 다른 트랜잭션에 의해 커밋 된 레코드를 볼지 여부를 정의하지 않습니다. 이는 커밋되지 않은 레코드를 결코 볼 수 없다는 것을 확인합니다.
Quassnoi

3
select ... for update에는 rooms여전히 수 room_tags들이 별도의 테이블이기 때문에 삭제 될 수 있습니다. for update조항이에서 삭제를 방지 하는지 물어 보려고 했습니까 rooms?
Chris Saxon

답변:


84

룸과 태그 간의 일관성을 유지하고 룸이 삭제 된 후 반환되지 않도록하는 유일한 이동식 방법은로 잠그는 것입니다 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특정 데이터베이스 엔진 구현의 부작용 때문에"를 추가 했어야했습니다.


1
마지막 요점은 문제의 핵심입니다. "직렬화 가능 모드에서는 SELECT FOR UPDATE가 필요하지 않지만 REPEATABLE READ 또는 READ COMMITED에서는 필요합니다"라고 생각합니다.
Colin 't Hart

네가 옳아. 두 번째 질문은 언제 SERIALIZABLEREAD_COMMITTED함께 사용해야하는지 물어야 합니다 SELECT ... FOR UPDATE. 이 업데이트 된 질문을 반영하도록 답변을 업데이트 해 주시겠습니까?
Gili

1
@Gili : " SELECT FOR UPDATE직렬화 가능 모드 는 필요하지 않습니다 ", InnoDB. 다른 MVCC시스템에서는 두 가지가 동의어이며 SELECT FOR UPDATE.
Quassnoi

1
Colin의 게시물 이 귀하의 답변보다 내 특정 질문에 대한 답변이 더 낫다고 생각하지만 귀하가 제공 한 모든 참조에 감사드립니다. 나는 두 가지를 가장 잘 결합한 답변을 받아 들일 것입니다 (상단의 구체적인 답변, 아래의 참조 참조).
Gili

This depends on the concurrency control your database system is using: 머리카락을 쪼개는 것 같아요. 아래에 나열된 모든 사례 SELECT는 거래가 끝날 때까지 방이 삭제되지 않는다고 말합니다 . 그럼, 그 답은 단순히 Yes아래의 지원 참조에 있어야하지 않습니까?
Gili

33

짧은 답변 :

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이 잠겨 있으므로 다른 트랜잭션이 어떤 식 으로든 수정할 수 없으므로 트랜잭션 격리 수준은 여기서 차이가 없습니다.

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

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


1
What transaction isolation levels do guarantee [...] is the consistency of data once transactions are completed.격리 수준이 트랜잭션 중에 발생하는 일에 영향을 미치지 않는다는 잘못된 암시를 한다고 생각 합니다. 이 섹션을 수정하고 거래 중에 보거나 보지 않는 것에 미치는 영향에 대해 자세히 설명하는 것이 좋습니다.
Gili

1
귀하의 게시물은 Quassnoi 보다 내 특정 질문에 더 잘 답변 하지만 그가 제공 한 모든 참조에 감사드립니다. 나는 두 가지를 가장 잘 결합한 답변을 받아 들일 것입니다 (상단의 구체적인 답변, 아래의 참조 참조).
Gili

잠금 및 격리는 상호 교환 적으로 복잡합니다. 그래서 그것에 대한 지식을 얻을 수있는 책이 있습니까?
Chao
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.