Database/mysql(InnoDB)

Transaction의 Isolation Level

Tony Lim 2021. 10. 18. 10:14
728x90

https://suhwan.dev/2019/06/09/transaction-isolation-level-and-lock/

 

Lock으로 이해하는 Transaction의 Isolation Level

개요 내게 transaction의 isolation level은 개발할 때 항상 큰 찝찝함을 남기게 하는 요소였다. row를 읽기만 할 때는 REPEATABLE READ로, row를 삽입 / 수정 / 삭제할 때는 SERIALIZABLE로 isolation level을 지정했지

suhwan.dev

 

여기에 예시도 많음

 

Row level lock

가장 기본적인 lock은 테이블의 row마다 걸리는 row-level lock이다.

shared lock(S lock) = read대한 lock 

Exclusive lock(X lock) = write 에 대한 lock 

여러 transaction 이 동시에 한 row에 S lock을 걸 수 있다. 즉 여러 transaction 이 동시에 한 row를 읽을 수 있다.

S lock이 걸려있는 row에 다른 transaction이 X lock 을 걸 수 없다. 즉 다른 transaction이 읽고 있는 row를 수정하거나 삭제 할 수 없다.

X lock이 걸려있는 row에는 다른 transaction이 S lock 과 X lock 둘 다 걸 수 없다. 즉 다른 transaction 이 수정 및 삭제하는 경우 row는 읽기, 수정 , 삭제가 전부 불가능 하다.

 

Record lock = DB의 index record에 걸리는 lock이다.

Gap lock = DB index record의 gap 에 걸리는 lock = 여기서 gap은 index중 db에 실제 record가 없는 부분을 의미 

record lock이 이미 존재하는 row가 변경되지 않도록 보호, gap lock은 조건에 해당하는 새로운 row가 추가되는 것을 방지 하기 위함

lock은 transaction이 commit , rollback 이 되면 unlock이 된다.

 

Transaction Isolation level

Consistent Read = read operation 을 수행할 때 현재 db의 값이 아닌 특정 시점의 db snapshot을 읽어오는 것, 물론 이 snapshot은 commit 된 변화만이 적용된 상태를 의미한다.

InnoDB엔진은 각 쿼리를 실행 할 때마다 실행한 쿼리의 log를 차곡차곡 저장한다. 그리고 나중에 consistent read를 할 때 이 log를 통해 특정 시점의 DB snapshot을 복구하여 가져온다. 이 방식은 복구하는 비용이 발생하긴 하지만, lock을 활용하는 방식보다 높은 동시성을 얻을 수 있다.

 

Read Uncommitted

read committed transaction 과 기본적으로 비슷하다. 하지만 read operation 을 실행할 때 아직 commit 되지 않은 데이터를 읽어 올 수 있다. 즉 dirty read가 발생한다.

이게 가능한 이유는 InnoDB 엔진은 일단 실행한 모든 쿼리를 DB에 적용한다. 그것들이 commit 되지 않았어도 적용한다. 즉 특별히 log를 보고 특정 시점의 snapshot을 복구하는 consistent read를 하지않고 그냥 해당 시점의 DB를 읽으면 dirty read가 된다.

 

 

Read Committed

commit 된 데이터만 보이는 수준의 isolation을 보장하는 level 이다.

repeatable read transaction 이 첫 read operation 을 기준으로 consistent read를 수행하는 반면, read commited transaction 은 read operation 마다 db snapshot을 다시 뜬다. 그렇기 때문에 다른 transaction이 commit한 다음에 다시 read operation을 수행하면 , repetable read 와는 다르게 read committed transaction으 해당 변화를 볼 수 있다.

뭣하러 매번 snapshot을 뜨냐 commit 되었다면 걍 db 읽어오면 되는 것이 아니냐? 라고 생각했지만 실제 DB에는 아직 commit되지 않은 쿼리도 적용이 된 상태다. 따라서 진짜로 commit된 데이터만을 읽어오기 위해서는 아직 commit 되지 않은 쿼리를 복구하는 과정이 필요하다. 

read committed transaction 은 record lock만 사용하고 gap lock은 사용하지 않는다. 따라서 phantom read가 일어날 수 있다.

 

Repeatable Read

반복해서 read operation 을 수행하더라도 읽어 들이는 값이 변화하지 않는 정도의 isolation을 보장하는 level 이다.

해당 Transaction은 처음으로 read operation 을 수행한 시간을 기록한다. 그 이후에는 모든 read operation 마다 해당 시점을 기준으로 consistent read를 수행한다. 고로 transaction 도중 다른 transaction이 commit 되더라도 새로이 commit 된 데이터는 보이지 않는다. 첫 read시의 snap shot을 보기 때문이다.

일반적인 non-locking ,read operation 외에 lock을 사용하는 select 나 update, delete 쿼리를 실행 할 떄, repeatable read transaction은 gap lock을 활용한다. 즉 내가 조작을 가하려고 하는 row의 후보군을 다른 transaction이 건들지 못하도록 한다. 

 

gap lock을 걸지 않으면 phantom read가 일어난다. update하려는 row의 후보군에 gap lock이 걸려야지 중간에 엉뚱한 것을 읽거나 , 없다고 착각하지 않는다.

 

Serializable

serializable transaction은 기본적으로 repeatable read 와 동일하다. 대신 select 쿼리가 전부 select .. for share로 자동 변경이된다.

select 할떄 S lock을 건다는 소리이다. 이렇게 엄격해지면 deadlock이 발생하기 쉬워진다.

 

 

 

 

 

 

728x90