[MySQL] Shared lock 과 Exclusive lock

acho·2024년 10월 29일

Shared lock 과 Exclusive lock

MySQL의 Shared lock(이하 공유 락)과 Exclusive lock(이하 배타 락)에 대해 알아보겠습니다.
우선 MySQL 공식문서에는 이렇게 설명되어있습니다:


공유 락과 배타적 락

InnoDB는 표준 행 수준 잠금을 구현하며, 공유(S) 락과 배타적(X) 락 두 가지 유형이 있습니다.

공유(S) 락은 해당 락을 보유한 트랜잭션이 행을 읽을 수 있게 허용합니다.
배타적(X) 락은 해당 락을 보유한 트랜잭션이 행을 수정하거나 삭제할 수 있게 허용합니다.


여기서는 '어떤 작업을 허용하는지'의 관점으로 설명하고 있습니다.
보통 락은 '어떤 작업을 제한하기 위해 사용하는 것'이라고 생각되기 때문에 이 부분에서 많은 혼란이 생기는 것 같습니다.

공유 락과 배타적 락의 차이

공유 락과 배타적 락을 비교할 때, 가장 중점이 되는 차이점은 '여러 트랜잭션에서 락을 획득할 수 있는가' 입니다.
즉 A 행에 대해 어떤 트랜잭션에서 lock을 걸었을 때, 다른 트랜잭션에서 lock을 또 걸수 있는지의 여부입니다.
이름에서 보이듯이 공유 락은 이것이 가능하고, 배타적 락은 불가능합니다.

-- Transaction A
BEGIN;
SELECT * FROM user WHERE id = 1 FOR SHARE;

-- Transaction b

BEGIN;
SELECT * FROM user WHERE id = 1 FOR SHARE; //가능
  • 같은 행에 대해 여러 트랜잭션이 락을 걸 수 있습니다. A 트랜잭션이 끝날 때 까지 B 트랜잭션이 기다리지 않아도 됩니다. -> 병렬성을 확보할 수 있습니다.
  • 트랜잭션 A와 B가 모두 끝날 때까지 공유 락의 제한을 받게 됩니다.
  • 공유 락이 걸려있는 동안은 대상을 수정 / 삭제할 수 없습니다.

배타적 락은 같은 대상에 대해 오직 하나의 트랜잭션만 락을 걸 수 있는 락입니다.
이미 배타적 락이 걸려있는 경우 어떤 락이든 추가로 거는 것이 불가능합니다.
단지 그 뿐이기 때문에 락을 걸지 않은 조회는 가능합니다. 즉FOR SHAREFOR UPDATE 구문이 없는 SELECT 구문은 사용이 가능합니다.

공유 락은 읽기 작업을 허용한다

배타적 락은 일반적으로 생각하는 락의 의미에 가깝고, 공유 락은 읽기 작업 시 락을 걸면서도 병렬성을 유지하기 위해 고안된 락이다. 라고 생각하면 편한 것 같습니다.
이런 관점에서 보면 읽기 작업을 허용한다는 말의 뜻을 좀 더 잘 파악할 수 있습니다.

  • 공유 락은 (수정/삭제를 제한함으로써) 읽기 작업을 허용한다.

  • 공유 락은 읽기 작업에 특화된 락이다. 그렇기 때문에 여러 트랜잭션이 같은 대상에 대해 락을 걸도록 허용한다.

  • 배타적 락은 (오직 하나의 트랜잭션만 락을 걸 수 있도록 함으로써) 데이터의 수정 / 삭제를 허용한다.

예시

배타적 락

예를 들어 다음과 같은 작업을 해야 한다면:

-- 좌석의 status 조회
SELECT status FROM seats 
WHERE flight_id = 100 AND seat_no = 'A1' FOR UPDATE;

-- 빈 좌석이라면 예약
IF status = 'AVAILABLE' THEN
    -- 실제 예약시에는 UPDATE
    UPDATE seats 
    SET status = 'RESERVED' 
    WHERE flight_id = 100 AND seat_no = 'A1';
END IF;
  • seats 테이블에서 status를 먼저 읽고 'AVAILABLE'일 때만 예약을 해야 합니다.
  • 그런데 만약 다른 SELECTINSERT 사이에 다른 연결이 해당 좌석의 status를 바꾼다면, 잘못된 좌석에 예약을 할 수도 있게 됩니다.
    이를 막기 위해서는 SELECT하는 행에 대한 변경을 막아야 합니다.
  • 또 추후 이 행을 변경할 것이기 때문에, 다른 트랜잭션에서 이에 대한 락을 걸지 못하게 해야 합니다. 그렇기 때문에 배타적 락을 사용합니다.

공유 락

은행 계좌에서 잔액을 조회하는 경우에는 어떨까요? 값을 읽는 동안에 값이 수정되어서는 안됩니다. 사용자에게 정확하지 못한 값을 전달할 수 있기 때문입니다.
하지만 조회만 하는 것이기 때문에 다른 트랜잭션이 락을 걸어도 상관 없습니다. 그렇기 때문에 공유 락이 사용됩니다.

공유 락과 REPETABLE READ

REPETABLE READ는 트랜잭션의 격리 수준 중 하나로, Non-Repetable read 현상을 방지하기 위해 트랜잭션이 끝날 때까지 읽고 있는 행이 변경되지 않음을 보장하는 레벨입니다.
공유 락의 동작과 유사합니다. MySQL에서는 기본 격리 레벨이 REPETABLE READ입니다. 트랜잭션 내부에서 읽는 행에 대해서는 기본적으로 공유 락을 걸어 격리 레벨을 보장하기에 명시적으로 락을 걸지 않아도 됩니다.

reference
https://dev.mysql.com/doc/refman/8.4/en/innodb-locking.html
https://suhwan.dev/2019/06/09/transaction-isolation-level-and-lock/
https://medium.com/@mithleshfantezie/exclusive-lock-and-shared-lock-2d1e87b0ab74

0개의 댓글