영우
잠금(LOCK)과 MVCC로 보는 InnoDB의 격리수준 본문
글을 작성하는 이유
Real MySQL 8.0의 InnoDB 격리수준에 대한 부분에서 각 격리수준을 설명하면서, MVCC에 대한 이야기를 주로다루고 LOCK에 대한 이야기는 거의 다루지 않아 설명이 매우 헷갈렸습니다.
예를 들면 SERIALIZABLE에서 읽기 작업도 공유잠금을 획득한다(183p) 라고 나와있는데 공유잠금에 대한 설명이 없어 이해하기 어려웠습니다.
잠금과 MVCC를 모두 이해하면 더 쉽게 InnoDB의 격리수준에 대해 이해할 수 있을것같아 글을 작성하게 되었습니다.
잠금이란?
잠금은 동시성을 제어하기 위한 기능입니다. DB에서 여러 커넥션들이 동시에 하나의 데이터를 업데이트 하고자 하면 일관성 없는 결과가 나오게 됩니다.
이런 동시 엑세스를 막기 위해, 한번에 하나의 커넥션만 데이터에 접근할 수 있도록 보장하는 기술이 잠금입니다.
잠금에는 두가지 주요 유형이 있습니다.
- 배타잠금(X-Lock)
- 배타잠금은 일반적으로 레코드에 데이터를 쓸 목적으로, 단일 엔티티가 배타적으로 보유합니다. 잠금이 필요한 다른 엔티티를 모두 차단하게 됩니다.
- 공유잠금(S-Lock)
- 공유잠금은 일반적으로 레코드를 읽을 목적으로, 여러 엔티티가 함께 보유할 수 있습니다. 모든 보유자가 공유잠금을 해제할 때까지 배타잠금을 획득할 수 없어, 공유잠금을 가지고 있는동안 레코드가 변경되지 않는다고 생각합니다.
MVCC란?
MVCC는(Multiversion concurrency control)은 타임스탬프 기법을 활용한 비잠금 동시성 제어방법 입니다.
잠금을 통한 동시성제어는 쓰기작업이 끝날때까지 읽기작업이 기다려야한다는 단점이 있습니다. 이와 다르게 MVCC의 아이디어는 데이터를 업데이트할 경우에 원래의 데이터를 새 데이터로 덮어쓰지 않고, 최신버전의 데이터항목을 생성하는것입니다.
이를 통해 특정시점에 대한 일관된 데이터를 제공하고, MVCC의 읽기 트랜잭션은 데이터의 트랜잭션 ID를 사용해 읽을 데이터의 버전을 결정하게 됩니다. 이를 통해 잠금없이 동시에 읽고 쓸 수 있습니다.
격리수준이란?
isolation(격리)는 트랜잭션 무결성이 다른 사용자에게 보이는 방식을 결정합니다.
- 무결성(integrity) : 데이터의 정확성과 일관성을 유지하고 보증하는것
격리수준이 낮을 수록 트랜잭션 간 격리가 적어져 데이터 부정합이 많고 성능이 향상됩니다. 반대로 격리수준이 높을수록 데이터 부정합이 적고 성능이 저하됩니다.
InnoDB에서 트랜잭션 격리수준은 앞서 언급한 MVCC를 어떻게 적용할것인가, LOCK을 어떻게 걸것인가에 따라 달라지게 됩니다.
READ UNCOMMITTED
- 데이터 읽기가 잠금없이 수행됩니다.
- 타 트랜잭션의 커밋여부와 상관없이 데이터를 읽습니다.
- 타 트랜잭션의 데이터 변경 후 해당 데이터를 읽고, 타 트랜잭션의 롤백 후 데이터를 읽는 것을 생각하면 데이터가 달라지는것을 알 수 있습니다. 이를 dirty read라 합니다.
- 헷갈렸던 부분
- 질문
- READ UNCOMMITTED에는 MVCC를 사용하지 않는건가?
- 나름의 해답
- 각 트랜잭션이 롤백을 위한 언두로그를 가지고 있을것 같습니다. 하지만 멀티버전이라고 보기는 어렵고, 목적도 동시성 제어가 아닌 단순롤백용입니다. 그러므로 MVCC를 사용하지 않는다고 생각합니다.
- 질문
SERIALIZABLE
- 데이터 읽기가 공유잠금을 획득해야 수행됩니다.
- 가장 단순한 격리수준이자 동시에 가장 엄격한 격리수준입니다.
- 읽기에서 잠금을 요구함으로써 동시성문제를 해결하고, 성능이 떨어집니다.
- 책에서 잘못된 내용이라고 생각하는 부분
- 문장
- 한 트랜잭션에서 읽고 쓰는 레코드를 다른 트랜잭션에서 절대 접근할 수 없음(Real MySQL 8.0 183p)
- 이유
- autocommit이 꺼져있을때 select문은 공유잠금을 획득하기 때문에 다른 트랜잭션과 동시에 읽을수는있다.
- 근거
- but InnoDB implicitly converts all plain SELECT statements to SELECT ... FOR SHARE if autocommit is disabled(MYSQL 공식문서)
- 문장
- 공식문서를 보며 헷갈렸던 부분
- 문장
- If autocommit is enabled, the SELECT is its own transaction. It therefore is known to be read only and can be serialized if performed as a consistent (nonlocking) read and need not block for other transactions.(MYSQL 공식문서)
- “autocommit이 활성화된 경우 SELECT는 자체 트랜잭션입니다. 따라서 SELECT는 읽기 전용으로 알려져 있으며 일관된(비잠금) 읽기로 수행될 경우 직렬화될 수 있으며 다른 트랜잭션에 대해서는 차단할 필요가 없습니다.
- 헷갈린 부분
- 잠금없는 일관된 읽기로 수행한다는 것이, 데이터의 배타잠금과 상관없이 접근할 수 있는건가? 잠금이 없으니까?
- 나름의 해답
- 잠금없는 일관된 읽기로 수행하는 것은 MVCC를 통한 접근이지 배타잠금과는 상관없는 이야기였습니다.
- 잠금없는 일관된 읽기에 꽂혀서 헷갈렸고, 잠금없는 일관된 읽기 라는 용어는 MVCC의 가장 중요한 목적입니다.(심지어 공식문서에 따로 페이지가 있습니다! (MySQL 공식문서)
결론
- 가장 중요한 깨달음은 MVCC의 목적은 잠금없는 일관된 읽기 라는 점입니다. 무결성이 떨어지는 격리수준인 READ UNCOMMITED를 제외하고 READ COMMITTED과 REPEATABLE READ는 MVCC를 통해 잠금없는 일관된 읽기를 할 수 있습니다. 그리고 이러한 특성을 이해한 후, 가장 높은 격리수준인 SERIALIZABLE은 읽기도 잠금을 필요로 해 성능이 떨어질 수 밖에 없는것을 쉽게 받아들일 수 있었습니다.
- 책에서 잘못된 내용이라고 생각하는 부분을 찾으며, 책의 내용이 완전히 들어맞지 않을 수 있다는 생각이 들었습니다. 그렇기 때문에 중요한것은 글의 맥락을 고려한 저자가 말하고자 하는 내용이지 내용을 그 자체가 아니라는 깨달음을 얻었습니다. 단순 암기를 하기 보다는 어떤 맥락에서 어떠한 의도로 이야기를 했는지에 집중해 읽어야 한다는 것을 알 수 있었습니다.
더 공부할 부분
- 2PL(2Phase Locking)
- 의도잠금(Intent Lock)
- 데이터 직렬성
- REPEATABLE READ, READ COMMITTED에 대한 이야기
참고자료
REAL MySQL 8.0 5장 트랜잭션과 잠금
https://en.wikipedia.org/wiki/Record_locking
https://dev.mysql.com/doc/refman/8.0/en/innodb-multi-versioning.html
https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html
'CS > 데이터베이스' 카테고리의 다른 글
클러스터링 인덱스의 사실과 오해 (0) | 2024.02.15 |
---|---|
서브쿼리란 뭘까? (1) | 2024.02.08 |
MySQL EXPLAIN 명령어는 뭘까? (0) | 2024.02.01 |
MySQL에 대용량 데이터를 삽입해 인덱스를 사용해보기 (0) | 2024.01.19 |
InnoDB 버퍼풀의 LRU 구현해보기 ! (0) | 2024.01.04 |