Select 쿼리는 S락이 아니다. (X락과 S락의 차이)

soongjamm·2021년 6월 18일
13
post-thumbnail
post-custom-banner

이 글에서 사용한 dbms는 MySQL 8.x 버전이고 innoDB engine 기준입니다.
아~주 기본적인 레벨, 거의 시작점 수준의 이야기이니 감안해주세요.

이 글을 쓰게된 이유

저는 Real MySQL을 통해 DB를 공부하던 중 S-LockX-Lock에 대해 알게 되었습니다.
그리고 SELECT - FOR UPDATE 쿼리는 해당 레코드의 X-lock을 획득한다는 사실도 알게되었고,
이 사실을 이용해 직접 실습을 해보던 중 저의 뇌가 꼬이기 시작했습니다.

'1번 트랜잭션에서 A레코드의 X-Lock을 획득했으니 B트랜잭션에서는 A레코드를 읽을 수 없어야 하지.

1번 트랜잭션의 작업이 끝날때 까지 2번 트랜잭션의 SELECT는 대기상태가 되겠군...?'

'아니... 조회가 왜 돼?😯'

S-Lock은 Shared-Lock, 읽기 락과 동일한 의미이고
X-Lock은 Exclusive-Lock, 쓰기 락, 배타적 락과 동일한 의미입니다.

편의상 섞어쓸 수 있고, 이 이름들 또한 제가 헷갈렸던 부분이라 미리 정리했습니다.

SELECT와 S락에 대한 오해

1번 트랜잭션이 A레코드의 쓰기락을 얻었음에도 2번 트랜잭션은 아무 일 없이 A레코드를 조회하는데 성공했습니다. 왜일까요?
제가 SELECT와 S락에 대한 오해를 하고 있었기 때문입니다.

  • SELECT 한다는 것이 S락을 건다는 의미가 아니다.

  • SELECT, 즉 읽는 것(또는 조회)이란 어떤 '행위'일 뿐이지 '락'을 의미하진 않는다.

너무 당연한 말같지만... 저는 SELECT가 곧 S락이라고 무의식중에 생각하고 있었습니다.
그 배경은 S락과 X락의 차이를, 그리고 그 자체를 이해하지 못했기 때문입니다.
그래서 우선 위의 읽기는 행위일 뿐, 락이 아니다. 를 강조하고 싶고, 이어서 S락과 X락을 설명해보겠습니다.

S락과 X락의 차이

개념을 외우기만 했다면 쓰기락에 대해 쓰기락을 걸면 다른 트랜잭션이 읽지 못한다. 라고 말할 수도 있습니다. (제가 그랬습니다. ㅎㅎ) 하지만 이것은 틀린 표현이라 생각합니다.
위의 문장을 고쳐보겠습니다.

  • 쓰기락을 걸면 다른 트랜잭션에서 읽기락을 얻지 못한다.

아니, 그래서 S락은 뭐고 X락은 뭔가!

S락과 X락은 비슷합니다.
둘 다 레코드에 잠금을 걸고, 잠금이 걸린동안 다른 트랜잭션이 수정하지 못하도록 막는 일을 합니다.
내가 작업하는 동안 레코드의 내용이 계속 바뀌면 이 레코드를 신뢰할 수 없으니까 내 작업이 마치기 전까지는 아무도 건들지 마! 하고 잠금을 거는겁니다.

책의 내용에 대한 기억을 더듬어보며 정의해보자면

  • S락은 읽는 동안 수정이 발생하지 않게 잠그는 것이고
  • X락은 쓰는 동안 수정이 발생하지 않게 잠그는 것이다.

별 차이없어 보이지만, 제가 생각하는 이 둘의 결정적인 차이는 락 사이의 호환여부입니다.

  • S락 끼리는 호환되고, X락이 끼면 호환되지 않는다.

이게 무슨 말이냐면, 1번 트랜잭션이 어떤 레코드 A에 대해 S락을 얻었다고 가정합시다.

  • 2번 트랜잭션에서 레코드 A에 S락을 시도하면 성공합니다.
    왜냐하면 S락 끼리는 호환되기 때문입니다.
  • 그러나 A레코드에 X락을 시도하면 대기상태에 빠집니다. (예를들어 UPDATE 쿼리)
    왜냐하면 이미 1번 트랜잭션이 S락을 가진 상태기 때문에 호환되지 않는 X락을 동시에 걸수가 없습니다.
    1번 트랜잭션이 종료되면 대기하던 2번 트랜잭션이 그제서야 X락을 획득하게 됩니다.
  • 반대로 X락이 먼저 걸려있는 레코드에 S락을 시도하면, S락 시도도 대기상태에 빠집니다.

여기까지가 간략은 S락과 X락에 대한 설명이었고, 그렇다면 다시 본론으로 넘어가겠습니다.

어떻게 X락에 걸렸는데, SELECT가 된걸까?

X락과 S락은 호환되지 않기때문에 X락이 걸려있으면 S락 시도도 대기상태에 빠진다했는데, 왜 SELECT는 되는걸까요?

  • 다시 한번 강조하지만 SELECT와 S락은 구분되어야 합니다.
  • 그리고 MySQL InnoDB는 SELECT시 S락을 걸지 않고 조회합니다.

사실상 이 둘이 이 글에서 말하는 전부입니다.

X락을 그 레코드에 걸었든, 말든, MySQL InnoDB엔진은 SELECT시 S락을 걸지 않으니 조회가 가능한 것입니다.

물론 어떤 DBMS에서는 SELECT시에 S락을 획득한다고 합니다.
이러한 특징을 가진 DBMS에서는 X락이 걸린 레코드에 SELECT를 시도하면 대기가 발생할 것입니다.
(S락과 X락의 호환여부가 무엇을 의미하는지 이해하고, SELECT는 S락이 아님 을 이해하면 다른 DBMS에서 SELECT시 대기가 발생해도 이해할 수 있을거라 믿습니다.)

만약 MySQL InnoDB 엔진에서도 위의 상황처럼 X락이 걸린 레코드에 SELECT시 대기하는 상황을 보고싶다면,

  • X락을 먼저 얻기 위해 SELECT ~ FOR UPDATE 쿼리를 날리고
  • S락을 얻는 SELECT ~ LOCK IN SHARE MODE 를 사용하면 됩니다.
    LOCK IN SHARE MODE를 뺀 쿼리를 날린다면 그냥 조회가 될 것입니다.
    그러나 LOCK IN SHARE MODE는 S락을 걸려고 하기때문에, X락이 걸려있다면 대기상태가 됩니다.

  • 위에서 먼저 SELECT ~ FOR UPDATE로 category 테이블 전체에 X락을 걸었습니다.
  • 그러나 여전히 SELECT는 가능한 모습입니다.

  • 아래 트랜잭션에서 SELECT ~ LOCK IN SHARE MODE(S락 획득 시도)로 쿼리를 날리니까 대기상태에 빠졌습니다.

  • category 테이블 전체의 X락을 얻은 트랜잭션에서 롤백을 하니, 그재서야 다른 트랜잭션에서 SELECT ~ LOCK IN SHARE MODE;로 S락을 얻는 조회에 성공한 모습입니다.

글을 마무리하기 전에 다시한번 강조하고 마치겠습니다.

  • SELECT가 곧 S락을 의미하지 않는다.
  • S락과 X락을 이해하고 호환된다는게 무슨 의미인지 이해하자.

별거 아닌 주제를 길게 썼는데 저는

  • S락과 X락의 호환여부를 이해하는 것
  • S락과 SELECT를 구분하는 것

이 두가지가 DB를 이해하는데 많은 도움이 되었어서 정리해보았습니다.

profile
느리게~걷자~걷자~
post-custom-banner

3개의 댓글

comment-user-thumbnail
2022년 7월 28일

innodb에서 select를 사용할 경우, Consistent Nonlocking Reads로 동작해서 undo 로그에 있는 snapshot을 읽는 것으로 알고 있습니다. select가 락이 걸린 것을 읽을 수 없어야 하는데 innodb는 undo 로그까지 락을 걸지 않아서 select ... for update로 락이 걸린 것을 select를 통해서 읽을 수가 있습니다.

1개의 답글
comment-user-thumbnail
2024년 7월 23일

저를 이해 시키다니 대단하시네요

답글 달기