현재 진행중인 교내 프로젝트에서 락을 걸어야하는 경우가 발생하여 DB로 사용중인 Mysql 8.X(Engine=InnoDB)의 Lock에 대해 알아보고자 합니다. 해당 글은 Mysql 8.0 공식 문서를 참고하여 작성된 글입니다. 자세한 내용은 하단의 Reference를 참고 하시면 됩니다!
공유(S) 잠금 예시)
트랜잭션 T1이 행 r에 대한 공유(S)잠금을 보유하고 있는을 때, 일부 개별 트랜잭션 T2의 행 r에 대한 잠금 요청 하는 경우
-> T2의 S 잠금 요청은 즉시 승인 됩니다. 결과적으로 T1과 T2는 모두 r에 대한 S 잠금을 보유하게 됩니다.
-> T2의 X 잠금 요청은 즉시 승인될 수 없습니다.
배타적(E) 잠금 예시)
트랜잭션 T1이 행 r에 대해 배타적(X) 잠금을 보유하고 있을 때, 다른 트랜잭션 T2가 행 r에 대해 두 가지 유형의 잠금(공유, 배타적)을 요청해도 즉시 승인할 수 없습니다.
대신 트랜잭션 T2는 트랜잭션 T1이 행 r에 대한 잠금을 해제할 때까지 기다려야 합니다.
예를 들어, LOCK TABLES ... WRITE 문은 지정된 테이블에 배타적 잠금(X 잠금)을 사용합니다. 여러 세분화 수준에서 잠그는 것을 실용적으로 만들기 위해 InnoDB는 Intention 잠금을 사용합니다.
- Intention 잠금은 테이블의 행에 대해 나중에 트랜잭션에 필요한 잠금 유형(공유 또는 배타적)을 나타내는 테이블 수준 잠금입니다. Intention 잠금에는 두 가지 유형이 있습니다:
- 의도 공유 잠금(IS)은 트랜잭션이 테이블의 개별 행에 공유 잠금(S)을 설정하려는 의도를 나타냅니다.
- 의도 독점 잠금(IX)은 트랜잭션이 테이블의 개별 행에 배타적 잠금(X)을 설정하려는 의도를 나타냅니다.
테이블 수준 잠금 유형 호환성은 다음 표에 요약되어 있습니다.

LOCK TABLES ... WRITE)을 제외하고는 아무것도 차단하지 않습니다. 즉, Intention 잠금의 주요 목적은 누군가 행을 잠그고 있거나 테이블의 행을 잠그려고 한다는 것을 표시하는 것입니다.예시)표에서 IX와 IX는 Compatible이기에, 하나의 테이블에 트랜잭션 T1이 IX를 가지고 있으면, 트랜잭션 T2는 IX를 가질 수 있습니다. 그러나 한 행에 대해 트랜잭션 T1이 X를 가지면 트랜잭션 T2는 X를 가질 수 없습니다.
Record Lock 예시) SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE; 는 다른 트랜잭션이 t.c1의 값이 10인 행을 삽입, 업데이트 또는 삭제하지 못하도록 합니다.
- 레코드 잠금은 테이블이 인덱스 없이 정의된 경우에도 항상 인덱스 레코드를 잠급니다. 이러한 경우 InnoDB는 숨겨진 클러스터된 인덱스를 생성하고 이 인덱스를 레코드 잠금에 사용합니다.
Gap Lock 예시), SELECT c1 FROM t WHERE c1 BETWEEN 10과 20 FOR UPDATE; 는 범위의 모든 기존 값 사이의 간격이 잠겨 있으므로 열에 이미 해당 값이 있든 없든 다른 트랜잭션이 t.c1 열에 15라는 값을 삽입하지 못하도록 합니다.
- Gap은 단일 인덱스 값, 여러 인덱스 값에 걸쳐 있을 수도 있고 비어 있을 수도 있습니다.
- Gap Lock은 성능과 동시성 사이의 절충안의 일부입니다.
- 서로 다른 트랜잭션이 갭에 충돌하는 잠금을 보유할 수 있습니다. 예를 들어 트랜잭션 A는 하나의 갭에 Share Gap Lock(Gap S-lock)을, 트랜잭션 B는 동일한 갭에 Exclusive Gap Lock(Gap X-lock)을 유지할 수 있습니다. 충돌하는 Gap Lock이 허용되는 이유는 하나의 레코드가 인덱스에서 제거될 경우, 다른 트랜잭션이 해당 레코드에 보유한 갭 잠금을 병합해야 하기 때문입니다.
- 즉, InnoDB의 Gap Lock 기능은 "순수 억제형"으로, 다른 트랜잭션이 Gap에 삽입되는 것을 방지하는 것이 유일한 목적입니다.
(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)
Insert Intention Locks 예시 1 ) 값이 4와 7인 인덱스 레코드가 있다고 가정해 보겠습니다. 각각 5와 6의 값을 삽입하려는 별도의 트랜잭션은 삽입된 행에 대한 독점 잠금을 얻기 전에 각각 삽입 의도 잠금으로 4와 7 사이의 간격을 잠그지만 행이 충돌하지 않으므로 서로를 차단하지 않습니다.
Insert Intention Locks 예시 2)다음 예시는 삽입된 레코드에 대한 Exclusive Lock을 얻기 전에 Insert Intention Lock을 취하는 트랜잭션을 보여줍니다. 클라이언트 A는 두 개의 인덱스 레코드(90과 102)가 포함된 테이블을 생성한 다음 ID가 100보다 큰 인덱스 레코드에 Exclusive Lock을 설정하는 트랜잭션을 시작합니다. 이 Exclusive Lock에는 102번 레코드 앞에 갭 잠금이 포함됩니다. 클라이언트 B는 갭에 레코드를 삽입하는 트랜잭션을 시작합니다. 트랜잭션은 Exclusive Lock을 얻기 위해 기다리는 동안 Insert Intention Lock을 취합니다.
// A
mysql> CREATE TABLE child (id int(11) NOT NULL, PRIMARY KEY(id)) ENGINE=InnoDB;
mysql> INSERT INTO child (id) values (90),(102);
mysql> START TRANSACTION;
mysql> SELECT * FROM child WHERE id > 100 FOR UPDATE;
+-----+
| id |
+-----+
| 102 |
+-----+
// B
mysql> START TRANSACTION;
mysql> INSERT INTO child (id) VALUES (101);
REPEATABLE READ 또는 SERIALIZABLE 트랜잭션 격리 수준을 지원하는 데 적합하지 않습니다. 다차원 데이터에는 절대적인 순서 개념이 없기 때문에 어떤 것이 "Next" Key 인지 명확하지 않기 때문입니다.