Transaction 격리 수준들을 살펴 보면, Repeatable Read 격리 수준에서 Phantom Read 문제가 발생한다.
그러나, MySQL에서 InnoDB 엔진을 사용할 경우, Repeatable Read 격리 수준에서 Phantom Read가 발생하지 않는다.
그 이유를 알아보자.
Phantom Read란 하나의 논리적인 작업 내에서 여러번 SELECT 문장 수행 시 특정 데이터가 보였다, 안보였다 하는 경우를 뜻한다.
1개
만 나온다.INSERT
한다.처음의 값과 count 수가 다르다.
이 현상을 Phantom Read라고 하며, Non-Reapeatable Read와 다음과 같은 차이가 있다.
None-Reapeatable Read : 특정 데이터가 바뀌어 보이는 것. ( UPADTE )
Phantom Read : 데이터의 갯수가 일치하지 않는 것. ( INSERT )
MySQL에서 Reapetable Read 격리 수준에서도 Phantom Read가 발생하지 않는 이유를 이해하기 위해 우선, InnoDB Lock을 이해해야 한다.
레코드 단위
로 Lock을 걸 수 있다.우수한 동시성
덕분에 MySQL 8.0 이상 부터는 거의 표준이 되었다.Record Lock은 레코드 그 자체에 Lock을 거는 것이 아닌
Index에 Lock
을 거는 방식이다.
해당 쿼리를 실행하면, t.c1=10인 행에 대해 UPDATE/INSERT/DELETE가 불가능하다.
SELECT c1 FROM THERE c1 = 10 FOR UPDATE
동일한 인덱스가 설정된 경우 해당 레코드가 모두 잠기기 때문에 Index를 설정하는 것이 매우 중요하다.
cf. 만약 Index가 설정되지 않은 Table
이라면, PK를 기준으로 자동으로 생성된 클러스터 인덱스
를 통해 Lock을 건다.
간격
을 잠그는 것을 의미한다.해당 쿼리를 실행하면, 10 ~ 20 사이의 간격이 잠겨있기 때문에
t.c1=15인 값을 INSERT 할 수 없다.
SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE
다음과 같은 고유 인덱스를 검색하는 일에는 Gap Lock이 사용되지 않는다.
SELECT * FROM child WHERE id = 100;
Next Key Lock은 Record Lock과 Index Record 이전 간격의 Gap Lock을 조합한 잠금이다.
InnoDB에서 Phantom READ가 발생하지 않는 이유가 바로 이 Next-Key Lock
때문이다.
예를 들어. 10, 11, 13, 20의 인덱스가 있을 때 다음과 같이 Lock이 수행된다.
(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)
가장 큰 인덱스 값 다음의 간격에 대해서 Lock을 수행한다.
InnoDB의 Default 격리 수준이 REAPITABLE READ이고,
이 경우 Next Key Lock을 사용하기 때문에 Phantom Read가 발생하지 않는다.