[DB] 트랜잭션(1) - ACID와 격리 수준(isolation level)
[DB] 트랜잭션(2) - repeatable read 격리 수준에서의 phantom read 현상
[DB] 트랜잭션(3) - 격리 수준과 동시성 문제, 그 해결
트랜잭션과 격리 수준에 대해서는 여러 책과 블로그 등에서 상세히 다루고 있으므로 개념을 살펴보진 않으려 한다. 간단하게만 짚고 넘어가면 다음과 같다.
트랜잭션
- 작업의 완전성을 보장해준다. 즉, 논리적인 작업 셋이 모두 성공(commit)하거나 실패(abort, rollback)하도록 보장한다.
- '데이터 중심 애플리케이션 설계' 책에서는 트랜잭션이 제공하는 안전성 보장을 흔히 ACID로 알려져 있다고 말하지만, 그 개념은 데이터베이스에 따라 제각각이고 개념 또한 모호하다고 말한다.
ACID
- 원자성(Atomic)
- 더 작은 부분으로 나눌 수 없는 단위
- 격리성(isolation)에서 다루는 여러 프로세스가 동시에 같은 데이터에 접근하려고 할 때 생기는 문제들과는 관련이 없음
- 하나의 원자적인 트랜잭션이 묶여서 완료되거나 실패함을 보장
- abortability가 원자성 보다 나은 단어일 수 있음
- 일관성(Consistency)
- 일관성의 아이디어는 항상 진실이어야 하는 데이터에 관한 어떤 선언(불변식(invariant))이 있다는 것(예를 들어 모든 계좌에 걸친 대변과 차변은 항상 맞아떨어져야 함)
- 하지만 일관성 아이디어는 애플리케이션의 불변식 개념에 의존하고, 이를 유지하도록 트랜잭션을 올바르게 정의하는 것은 애플리케이션의 책임
- 엄밀히 따지면 일관성은 데이터베이스만의 속성은 아님
- 격리성(Isolation)
- 여러 클라이언트가 단일 데이터에 접근하면 동시성 문제 발생하는데, 동시에 실행되는 트랜잭션은 모두 격리됨을 의미
- 여러 트랜잭션이 동시에 실행되었더라도, 순차적으로 하나씩 실행했을 때와 결과가 동일하게 만드는 아이디어
- 지속성(Durability)
- 하드웨어나 네트워크 등의 결함에도 트랜잭션에서 기록한 데이터는 손실되지 않음을 의미
- 디스크에 저장된 데이터가 오염되었을 때 복구할 수 있게 하는 write-ahead log(MySQL에서의 undo log) 등의 수단을 동반
- 무손실의 절대적 보장을 의미하는 것은 아님(SSD가 전원이 끊어지면 온도에 따라 몇 주내에 손실이 생길 수도 있음)
위에서 설명한 트랜잭션을 지원하는 데이터베이스의 속성 중에서 여러 클라이언트가 동시에 같은 데이터에 접근하려고 하는 상황인 격리성에 대해서 실제 테스트를 통해서 알아보고자 한다.
격리성
- 격리레벨은 MySQL에서 크게 4가지로 나뉜다. read uncommitted, read committed, repeatable read, serializable
- 데이터베이스마다 격리 수준을 정의하는 방식은 모두 다르다. 일반적으로는 동시성 문제를 해결하기 위해 트랜잭션 격리를 제공한다고 말하고, 모든 데이터를 직렬적으로 처리하는 직렬성은 성능 상의 이슈가 있으므로 어떤 동시성 이슈로부터는 보호해주지만 모든 이슈로부터는 보호해주지는 않는 '완화된 격리 수준'을 통해 이를 해결하려고 한다.
- 오라클의 serializable 격리 수준은 MySQL의 repeatable read 격리 수준에 대응되는 등 격리 수준을 정의하는 방식은 다르고, 각 격리 수준의 구현과 해결하고자 하는 동시성 수준도 모두 달라서 애매모호한 부분이 존재한다.
우선은 MySQL의 격리 수준을 통해 격리 수준에 따른 동시성 이슈가 무엇이 있고, 이를 다른 격리 수준에서 어떻게 해결했는지 살펴보고자 한다.
read uncommitted
- 다른 트랜잭션에서 커밋되지 않은 데이터를 읽게 될 수 있다. 하지만 위에서 A가 변경한 데이터를 B가 읽게 되는 순간에, A의 데이터가 실제 영구적으로 저장되는 데 성공(commit)할지, 실패(rollback)할지 알 수 없다. 데이터의 불일치가 발생할 수 있다. dirty read 문제가 발생한다.
read committed
- A 트랜잭션에서 commit 되기 전의 변경 데이터는 B 트랜잭션에서 undo log의 변경 전 데이터를 읽어온다. 이를 통해 dirty read는 발생하지 않는다.
- 하지만 commit 이후에는 변경 후의 데이터를 읽어와 non-repeatable read 문제가 발생한다.
repeatable read
- 트랜잭션마다 스냅샷 격리를 통해 transaction id에 따라 읽어오는 데이터를 구분한다. B 트랜잭션에서 한 번 읽었던 데이터는 트랜잭션이 종료되기 전이라면 A 트랜잭션에서 변경 완료(commit)되었다고 하더라도 A transaction id(10) 보다 최신의 B transaction id(12)가 있다면 undo log에서 변경 전 데이터를 읽는다. 이를 통해 non-repeatable read 문제를 해결한다.
- Phantom Read 현상이 있을 수 있다. 이에 대해서는 다음 글에서 자세히 설명한다.
serializable
- 가장 단순하고 엄격한 격리 수준으로, read/write lock 모두를 획득하여 처리 성능이 다른 격리 수준보다 떨어진다.
[참고]
'database' 카테고리의 다른 글
[DB] 데이터베이스와 색인(hash, LSM tree) (2) | 2023.06.08 |
---|---|
[DB] 트랜잭션(3) - 격리 수준과 동시성 문제, 그 해결 (0) | 2023.05.09 |
[DB] 트랜잭션(2) - repeatable read 격리 수준에서의 phantom read 현상 (0) | 2023.05.08 |
HikariCP - max lifetime과 DB wait_timeout의 관계 (0) | 2023.05.03 |
HikariCP - maximum pool size와 DB max_connections의 관계 (5) | 2023.05.03 |