profile image

L o a d i n g . . .

728x90

이번 장에서는 백엔드 개발자라면 매우 중요한 트랜잭션에 대해 소개합니다. 트랜잭션의 기본 개념과 더불어 다양한 DBMS에서 어떻게 트랜잭션을 구현하는지 살펴볼 수 있는 장이므로, 시간이 나실 때 읽을 것을 추천합니다. 

 

ACID 

Atomicity 

원자성을 의미합니다. 트랜잭션 단위로 묶인 작업은 모두 성공하거나 모두 실패하는 트랜잭션의 속성입니다. MySQL은 트랜잭션의 원자성을 보장하기 위해 WAL(Write-Ahead Log)라는 로그성 파일을 활용합니다. 트랜잭션이 수행되는 과정에서 발생하는 데이터 변경을 로그로 WAL에 추가합니다(append). 만약 알 수 없는 이유로 시스템이 다운됐을 때, DBMS는 WAL를 참고해 반영되지 않은 트랜잭션의 작업을 디스크에 반영합니다. 트랜잭션이 WAL를 사용해 원자성을 어떻게 보장하는지, 그리고 WAL를 사용해 데이터베이스가 복구되는 과정이 궁금하시다면 다음 포스팅을 참고해 주세요. 

 

[Database] DBA급 개발자로 - #20 Database Recovery

이전 포스팅에서는 DBMS의 정책(steal, force policy)에 따라 데이터를 디스크에 어느 시점에 저장하는지와 WAL에 대해 간단히 살펴봤습니다. 이번 포스팅에서는 로그를 활용해서, 특히 WAL(Write Ahead Log)

code-run.tistory.com

Consistency 

일관성을 의미합니다. 데이터베이스에서 보장할 수 있는 일관성은 데이터베이스에 제한돼 있습니다. 예를 들어 데이터베이스는 외래 키 참조 무결성 등 데이터베이스 자체적으로(외부의 도움 없이) 식별할 수 있는 것만 일관성을 보장할 수 있습니다. 비즈니스 도메인 상의 일관성은 애플리케이션에서 보장해야 합니다. 금융 도메인의 예시를 들어보겠습니다. 특정 계좌에 있는 최소 금액을 10,000원이라는 규칙이 있다면, 해당 규칙을 지키기 위해서(일관성 보장) 데이터베이스뿐 아니라 애플리케이션에서 일관성을 보장하기 위한 로직이 필요합니다. 따라서 ACID의 C는 데이터베이스의 트랜잭션만으로 보장할 수 있는 특성이 아닙니다. 

Isolation 

트랜잭션이 동시에 실행하더라도 서로에게 영향을 주지 않고 독립적으로 실행된 것과 동일한 결과를 보장하는 트랜잭션의 특징입니다. 동시에 실행된 트랜잭션들이 동일한 데이터에 접근하는 경우 dirty read, dirty write, repeatable read, lost update 등 다양한 비정상적인 현상이 발생할 수 있습니다. Isolation level을 설정함으로써 동시에 실행되는 트랜잭션에서 발생할 수 있는 비정상적인 상황을 방지할 수 있습니다. 다만 너무 많은 비정상적인 상황을 방지하려다 보면 성능이 나빠질 수 있기 때문에 애플리케이션이 데이터를 조회하는 방식에 따라 적절한 isolation level을 설정하는 게 중요합니다. 

Durability 

트랜잭션이 커밋되면 디스크에 데이터가 저장됨을 보장합니다. 분산환경에서는 트랜잭션 커밋 시 복제 노드의 디스크에도 데이터가 저장되는 것을 보장함을 의미합니다. 

 

Isolation Levels 

다음으로는 트랜잭션의 isolation level에 대해 살펴보겠습니다. SQL 표준은 4개의 isolation level(read uncommitted, read committed, repeatable read, serializable)을 정의하고 각각의 isolation level에서 방지해야 하는 최소한의 비정상적인 상황을 정의합니다. 표준에서 각각의 isolation level은 "최소한 무엇을 방지해야 한다"만 정의하기 때문에 서로 다른 DBMS는 같은 isolation level이더라도 방지하는 비정상적인 상황이 다릅니다. Isolation level에 따라 MySQL과 PostgreSQL은 어떤 비정상적인 상황을 방지하는지 코드로 확인하고 싶다면 아래 포스팅을 참고해 주세요.

 

[Database] Isolation level에 따른 동시성 문제 분석

데이터베이스 트랜잭션은 설정된 isolation level에 따라 방지할 수 있거나 방지할 수 없는 동시성 문제가 존재합니다. ANSI SQL(미국 표준 협회의 SQL 표준)은 isolation level에 따라 방지해야 하는 최소한

code-run.tistory.com

 

SSI(Serializable Snapshot Isolation) 

이번 장에서는 가장 흥미로웠던 부분은 serializability isolation level를 구현하는 방법 중 하나인 SSI(Serializable Snapshot Isolation)이었습니다. PostgreSQL의 serializable isolation level에서 SSI를 사용하기 때문에 프로덕션 환경에서도 사용되는 기술입니다. 기존에 serializable isolation level을 구현하는 방법은 트랜잭션을 실제로 순차적으로 실행한다거나, 2 phase locking처럼 잠금을 활용해 발생할 수 있는 동시성 문제를 미연에 방지하는 방식입니다. 즉, 발생할 수 있는 동시성 문제를 미연에 방지하는 pessimistic concurrency control 형태입니다. SSI는 이와 다르게 optimistic concurrency control 형태입니다. 발생할 수 있는 동시성 문제를 미리 방지하지 않고 동시성 문제가 발생했을 때 대응하는 형태입니다. SSI는 기존의 snapshot isolation에 동시성 문제를 식별하는 로직이 추가된 형태로 볼 수 있습니다(실제로 PostgreSQL의 isolation level 중 repeatable read는 snapshot isolation 방식을 사용하고, serializable은 serializable snapshot isolation을 활용합니다). 원리를 짧게 요약하면 다음과 같습니다. 

  • Snapshot isolation과 동일하게 트랜잭션이 시작한 시점의 데이터만을 읽습니다. 다른 트랜잭션에 의해 중간에 데이터가 변경되더라도 해당 트랜잭션이 시작된 시점의 snapshot을 읽습니다. 
  • 트랜잭션이 커밋하는 시점에 해당 트랜잭션에서 참고한 데이터가 다른 트랜잭션에 의해 변경됐는지 확인합니다. 만약 변경됐다면 해당 트랜잭션을 abort 합니다. 

SSI와 기존 serializable isolation level을 구현한 방식(2PL, etc...)의 장단점은 운영체제에서 optimistic locking과 pessimistic locking의 장단점을 비교하는 것과 유사합니다. SSI는 writer에 의해 reader가 block 되지 않기 때문에 성능적으로 우수할 수 있습니다. 하지만 동시성 문제가 자주 발생하는 경우 트랜잭션이 자주 롤백될 수 있어 이러한 상황에서 성능이 기존 pessimistic concurrency control 방식보다 나쁠 수 있습니다. 반대로 기존 방식(2PL, etc...)은 writer에 의해 reader가 block 되어 성능이 나쁠 수 있지만 동시성 문제가 자주 발생하는 상황에서 SSI에 비해 트랜잭션 롤백의 빈도는 낮을 수 있습니다. 

 

 

 

728x90
복사했습니다!