14.2.3 InnoDB 잠금 모드
InnoDB
는 2 개의 잠금 유형 ( 공유 ( S
) 잠금 으로 배타적 ( X
) 잠금 )이 표준 행 수준 잠금이 구현됩니다. 기록 간격 및 넥스트 키의 각 잠금 유형에 대해서는 섹션 14.2.6 "InnoDB 레코드 격차 및 넥스트 키 잠금" 을 참조하십시오.
공유 (
S
) 잠금 은 잠금을 트랜잭션에서 행 읽기가 허용됩니다.배타적 (
X
) 잠금 은 잠금을 트랜잭션에 의해 행을 갱신 또는 삭제가 허용됩니다.
트랜잭션 T1
이 행 r
에 대한 공유 ( S
) 잠금을 보유하고있는 경우 다른 트랜잭션 T2
에서 행 r
에 대한 잠금 요청은 다음과 같이 처리됩니다.
T2
에 따르면S
락에 대한 요청은 즉시 부여 할 수 있습니다. 그 결과,T1
과T2
모두가r
에서S
잠금을 유지합니다.T2
에 따르면X
락에 대한 요청은 즉시 부여 할 수 없습니다.
트랜잭션 T1
이 행 r
에서 독점 ( X
) 잠금을 보유하고있는 경우는 r
에서 어떤 유형의 잠금에 대한 일부 개별 트랜잭션 T2
의 요청은 즉시 부여 할 수 없습니다. 대신에 트랜잭션 T2
는 행 r
에서 트랜잭션 T1
의 잠금이 해제 될 때까지 대기해야합니다.
인텐션 잠금
또한 InnoDB
에서는 레코드 잠금 및 테이블 전체 잠금이 공존하는 것을 허용하는 다중 입상 잠금을 지원하고 있습니다. 다중 입상 레벨의 잠금을 실천하기 위해, 인텐션 락 이라는 추가 잠금 유형이 사용됩니다. 인텐션 락은 나중에 트랜잭션이 해당 테이블의 행에 필요한 잠금 유형 (공유 또는 배타)을 나타내는 InnoDB
테이블 잠금입니다. 트랜잭션 T
가 테이블 t
에 지정된 유형의 잠금을 요청했다고 가정하면, InnoDB
에서 사용되는 인텐션 락이는 다음 두 가지 유형이 있습니다.
인텐션 공유 (
IS
) : 트랜잭션T
는 의도적으로 테이블t
의 각 행에S
잠금을 설정합니다.인텐션 배타적 (
IX
) : 트랜잭션T
는 의도적으로 이러한 행에X
잠금을 설정합니다.
예를 들어, SELECT ... LOCK IN SHARE MODE
는 IS
잠금을 설정하고 SELECT ... FOR UPDATE
는 IX
잠금을 설정합니다.
인텐션 락의 단계는 다음과 같습니다.
트랜잭션이 테이블
t
있는 행의S
잠금을 얻으려면 먼저t
의IS
또는 그것보다 강한 락을 취득해야합니다.트랜잭션이 행의
X
잠금을 얻으려면 먼저t
의IX
잠금을 획득해야합니다.
이러한 규칙을 요약 할 때는 다음 잠금 유형 호환성 매트릭스를 사용하면 편리합니다.
X | IX | S | IS | |
---|---|---|---|---|
X | 경쟁 | 경쟁 | 경쟁 | 경쟁 |
IX | 경쟁 | 호환 | 경쟁 | 호환 |
S | 경쟁 | 경쟁 | 호환 | 호환 |
IS | 경쟁 | 호환 | 호환 | 호환 |
잠금 기존 잠금과 호환되는 경우 요청자의 트랜잭션에 잠금이 부여되지만 기존의 락과 충돌하는 경우 잠금이 부여되지 않습니다. 트랜잭션은 충돌하는 기존의 잠금이 해제 될 때까지 기다립니다. 잠금 요청이 기존의 락과 충돌, 교착 상태 가 발생하기 때문에 부여 할 수없는 경우 오류가 발생합니다.
따라서, 인텐션 락은 전체 테이블 요청 ( LOCK TABLES ... WRITE
등) 이외는 아무것도 차단되지 않습니다. IX
및 IS
락의 주요 목적은 누군가가 행을 잠글 수 있으며 테이블의 행을 잠그려고하고 있다는 것을 보여주는 것입니다.
교착 상태의 예
다음의 예문은 락 요청에 의해 교착 상태가 발생했을 때 어떻게 오류가 발생 하는지를 보여줍니다. 이 예는 A와 B 두 클라이언트가 등장합니다.
먼저 클라이언트 A가 행을 하나 포함하는 테이블을 작성하고 트랜잭션을 시작합니다. 트랜잭션 내에서 A는 공유 모드에서 선택한 행에서 S
잠금을 획득합니다.
mysql>CREATE TABLE t (i INT) ENGINE = InnoDB;
Query OK, 0 rows affected (1.07 sec) mysql>INSERT INTO t (i) VALUES(1);
Query OK, 1 row affected (0.09 sec) mysql>START TRANSACTION;
Query OK, 0 rows affected (0.00 sec) mysql>SELECT * FROM t WHERE i = 1 LOCK IN SHARE MODE;
+------+ | i | +------+ | 1 | +------+ 1 row in set (0.10 sec)
그런 다음 클라이언트 B가 트랜잭션을 시작하고 테이블에서 행을 삭제하려고합니다.
mysql>START TRANSACTION;
Query OK, 0 rows affected (0.00 sec) mysql>DELETE FROM t WHERE i = 1;
삭제 작업을하려면 X
잠금이 필요합니다. 클라이언트 A가 보유하고있는 S
락과 호환이 없기 때문에 잠금을 부여 할 수 없습니다. 따라서 요청은 행 잠금 요청의 대기 클라이언트 B는 차단됩니다.
마지막으로, 클라이언트 A도 테이블에서 행을 삭제하려고합니다.
mysql> DELETE FROM t WHERE i = 1;
ERROR 1213 (40001) : Deadlock found when trying to get lock;
try restarting transaction
클라이언트 A가 열을 제거하기 위해 X
잠금이 필요하기 때문에, 데드락이 발생합니다. 그러나 클라이언트 B는 이미 X
락에 대한 요청이있어, 클라이언트 A가 S
잠금을 해제 할 때까지 대기하고 있기 때문에 잠금 요청을 부여 할 수 없습니다. B에 따르면 X
락에 대한 이전 요청으로 인해 A가 보유하고있는 S
잠금을 X
잠금으로 업그레이드 할 수 없습니다. 그 결과, InnoDB
는 클라이언트 중 하나에 오류가 발생하여 잠금을 해제합니다. 클라이언트는 다음 오류를 반환합니다.
ERROR 1213 (40001) : Deadlock found when trying to get lock; try restarting transaction
이때 다른 클라이언트에 대한 잠금 요청을 부여 할 수있게 테이블에서 행이 삭제됩니다.
InnoDB Monitor 출력 LATEST DETECTED DEADLOCK
절에는 "TOO DEEP OR LONG SEARCH IN THE LOCK TABLE WAITS-FOR GRAPH, WE WILL ROLL BACK FOLLOWING TRANSACTION"라는 메시지가 포함됩니다. 이것은 대기 목록에서 트랜잭션 수가 200 제한에 도달했음을 나타냅니다. 이 제한은 LOCK_MAX_DEPTH_IN_DEADLOCK_CHECK
에서 정의됩니다. 200 개의 트랜잭션을 넘는 대기 목록은 교착 상태로 처리 대기 목록을 확인하려는 트랜잭션은 롤백됩니다.
잠금 스레드가 대기 목록에서 트랜잭션이 소유 한 1,000,000 개 이상의 잠금을 참조 할 필요가있는 경우에도 같은 오류가 발생할 수 있습니다. 1,000,000 개의 잠금 제한은 LOCK_MAX_N_STEPS_IN_DEADLOCK_CHECK
에서 정의됩니다.