MySQL의 InnoDB 엔진은 SQL 표준에 정의된 4가지 트랜잭션 격리 수준(transaction isolation level)을 모두 제공한다. InnoDB 엔진의 트랜잭션 격리 수준 기본값은 REPEATABLE READ이다. MySQL client는 SET TRANSACTION 구문을 실행해서 격리 수준을 변경할 수 있고, server 자체에 옵션을 줘서 모든 client 연결에 대해 동일한 격리 수준을 적용하는 방법도 있다.
그러면 각각의 트랜잭션 격리 수준을 제공하기위해서 MySQL의 InnoDB 엔진이 어떤 방식을 사용하고있는지 가장 많이 사용되는 격리 수준 순으로 각각 알아보도록 하자.
앞서 언급했듯이 REPEATABLE READ 레벨의 경우 InnoDB 엔진에서 사용하는 디폴트 트랜잭션 격리 수준이고 특별한 성능 이슈가 없다면 잘 변경하지 않기때문에 가장 많이 사용된다. REPEATABLE READ를 만족시키기 위해서 MySQL InnoDB 엔진은 아래 두가지 방식을 사용한다.
SELECT ... FOR SHARE, SELECT ... FOR UPDATE 구문을 제공locking read 구문들과, UPDATE, DELETE이 수행될 때 실제 lock이 적용되는 방식은 SQL statement의 조건과, 대상 테이블의 컬럼에 index가 걸려있는지, 해당 index가 unique 한지에 따라 달라진다. 아래에 상황별로 정리를 해 보았다.
... WHERE pk=8... WHERE pk > 100... WHERE field = 3 or ... WHERE field > 4위에 언급된 개별적인 lock 들에 대한 더 자세한 설명은 MySQL InnoDB lock & deadlock 포스팅을 참고하면 된다.
트랜잭션 내부에서 non-locking read(기본 SELECT 구문) 실행할 때, 동시에 실행중인 다른 트랜잭션에서 데이터를 변경하더라도 특정 시점의 스냅샷(snapshot)을 이용하여 기존과 동일한 결과를 리턴할 수 있도록 해주는 기능.
설정된 트랜잭션 격리 수준에 따른 스냅샷이 생성되는 시점 비교
Consistent read 제약조건
트랜잭션 제약조건
위 두가지 조건이 결합하면 문제 상황이 발생한다.
INSERT INTO my_table (pk, value) VALUES (1, 'a') 실행 후 commit (Tx1 종료)DROP TABLE, ALTER TABLE이 실행되고 난 후에는 스냅샷이 더이상 유효하지 않다.InnoDB 엔진은 READ COMMITTED 레벨에서도 앞서 설명한 consistent read를 사용한다. 하지만 REPEATABLE READ일 때와는 다르게 매번 read opration 작업이 있을 때 마다 새로운 스냅샷을 생성해서 읽어오기 때문에 Phantom read가 발생할 수 있다.
REPEATABLE READ일 때 처럼 locking read를 할 수 있도록 동일하게 SELECT ... FOR SHARE, SELECT ... FOR UPDATE 구문이 제공되지만 적용되는 lock의 범위가 달라지는 것에 주의해야 한다.
READ COMMITTED 레벨로 설정된 경우 동일하게 locking read 구문 및, UPDATE, DELETE이 수행되더라도 REPEATABLE READ일 때보다 더 적은 범위에 대해서 lock이 적용된다.
READ UNCOMMITTED 레벨의 경우 lock을 사용하지 않는다. 이 레벨의 경우 트랜잭션을 사용하는 의미 자체가 거의 없으므로 잘 사용되지 않는다.
트랜잭션 격리 수준이 SERIALIZABLE로 설정되면 InnoDB는 자동으로 일반적인 SELECT 구문을 SELECT ... FOR SHARE으로 변경하여 실행한다. 그 외에는 REPEATABLE READ 레벨과 동일하다.
https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html
https://dev.mysql.com/doc/refman/8.0/en/innodb-consistent-read.html