[Database]

[데이터 베이스] 트랜잭션 시리즈 #2 : Transaction이란? ACID란?

quokkalover 2023. 1. 1. 17:51

본 글에서는 트랜잭션이라는 단어가 의미하는 바가 무엇인지, 그리고 트랜잭션을 검색하면 항상 같이 따라오는 ACID라는 특성이 의미하는 바가 무엇인지 한번 다루어보도록 하겠다.

 

 

트랜잭션 정의 : all or nothing

트랜잭션이란 DBMS가 데이터베이스를 다룰 때 사용하는 논리적 작업 단위다. 여기서 말하는 논리적 작업 단위는 여러 작업들을 하나로 묶은 덩어리라고 볼 수 있다. 트랜잭션에서 이 작업 덩어리는 전체가 수행되거나 혹은 전혀 수행되지 않아야 한다. 이게 무슨 말인지 헷갈려도 괜찮다. 아래의 가장 흔한 은행 입출금 예시를 보면 쉽게 이해가 될 것이다.

A의 계좌에서 B의 계좌로 100원을 전송하는 트랜잭션을 실행하는 방법은 아래와 같다.

START TRANSACTION
A 계좌에서 100원을 인출하는 SQL UPDATE 문
B 계좌에 100원을 입금하는 SQL UPDATE 문
COMMIT
  • START TRANSACTION; 트랜잭션을 시작한다는 것을 의미
  • COMMIT; 지금 까지 작업한 내용을 DB에 영구적으로 저장하라는 의미. + 트랜잭션을 종료한다는 의미

위 예시에서 볼 수 있듯 DB에서 하나의 트랜잭션은 START TRANSACTION에서 시작해서 COMMIT 으로 끝나는 그 사이의 모든 작업을 의미한다. 위 예시에서는 A의 계좌에서 B계좌에 100원을 보내기 위해 A계좌에서 100원을 빼고, B계좌에서 100원을 입금하는 두 작업이 하나의 단위로 실행돼야하고, 둘 중 하나라도 실패하면 트랜잭션을 실행하지 않는 것이다. 당연하다. A 계좌에서 돈을 빼고 B계좌에 돈을 넣어주기 전에 에러가 난다면 A계좌에서만 돈이 빠지는 이상한 현상이 발생할 수 있기 때문이다. 따라서 트랜잭션을 생각할 때는 all or nothing을 꼭 기억하자. 전체가 수행되거나 또는 전체가 수행되지 않아야 한다. 트랜잭션 내에 정의된 여러 작업 중 일부만 수행하는 일은 발생해선 안된다. 따라서 서비스에서 의도치 않은 결과가 저장되는 것을 방지하기 위해서는 트랜잭션의 작업 단위를 잘 구성하는 것은 매우 중요하다.

참고로 트랜잭션은 주로 위 예시처럼 CREATE, UPDATE, DELETE와 같이 데이터를 삽입/수정/삭제할 때 주로 사용된다. 하지만 SELECT 와 같이 데이터를 읽을때도 데이터베이스는 트랜잭션으로 간주한다. 따라서 SELECT를 할 때도 읽고 있는 데이터가 중간에 수정되는 것을 방지 하기 위해 트랜잭션의 isolation level을 설정해 사용할 수 있다는 점도 알아두자.

 

추가로 이렇게 트랜잭션을 사용하는 이유는 DB 서버에 여러 요청이 동시에 올 수 있고, 각 요청이 DB의 상태를 변경하는 과정에서 데이터 부정합을 방지하기 위해 사용한다. 이렇게 데이터 부정합이 발생하지 않으려면 DB가 요청을 병렬로 처리하지 않고 한번에 하나만 처리하도록 하면 되는데, 요청이 많아질 경우 이러한 방식은 효율이 매우 떨어진다. 따라서 성능을 위해 어쩔 수 없이 병렬로 요청을 처리해야 하는데, 이때 데이터 부정합을 방지하기 위해 트랜잭션을 사용하는 것이다.

 

 

트랜잭션의 Key operation: Commit and Rollback

트랜잭션이 성공적으로 마무리 되어 그 결과를 데이터베이스에 영구적으로 반영하기 위해 실제 디스크에 write하는 명령어를 COMMIT이라고 한다. 중간에 오류가 발생하는 등으로 인해 트랜잭션을 실행하기 전 상태로 원상복구 하는 명령어는 ROLLBACK 이라고 한다.

 

트랜잭션의 실행 과정을 요약하면, 트랜잭션이 실행되면서 중간중간 작업들은 COMMIT되기 전까지 디스크에 반영하지 않고 별도의 메모리 공간에 변경하다 문제없이 수행이 되고나면 COMMIT과 함께 메모리에 저장했던 내용을 실제로 디스크에 write하면서 영구적으로 저장한다고 생각하면 된다.

 

이해를 돕기 위해 트랜잭션의 상태 변화 과정, lifecycle을 나열해보면 아래와 같다.

(1) Activte States : 트랜잭션이 실행되고 있을 때의 상태를 의미한다.

(2) Partially Committed : 트랜잭션이 실행되면서 데이터의 변경사항을 디스크에 쓰기 전에 각자의 메모리 공간에만 변경해놓은 상태를 의미한다.

(3) Committed : Partially Committed상태에서 실제로 데이터를 디스크에 쓴 상태를 의미한다. 이렇게 데이터가 커밋된 이후에는 다시 데이터를 롤백할 수 없다.

(4) Failed : Activate State나 Partially Commited 상태에서 에러 등의 이유로 트랜잭션이 취소된 상태를 의미한다.

(5) Terminated State : 트랜잭션이 커밋돼서 영구적으로 결과를 반영하거나 실패해서 원상복구(롤백)한 뒤에 실질적인 트랜잭션의 종료 상태를 의미한다.

 

일반적인 트랜잭션 사용 패턴

트랜잭션의 일반적인 사용패턴은 아래와 같다.

(1) START TRANSACTION 명령어와 함께 transaction을 시작한다

(2) 트랜잭션내 정의 해둔 여러 작업들을 수행한다

(3-1) 모든 작업들이 문제없이 동작했다면 transaction을 COMMIT한다

(3-2) 만약 중간에 문제가 발생햇다면 transaction을 ROLLBACK한다.

 


트랜잭션의 특성 : ACID

앞서 트랜잭션이 무엇인지에 대해 간단하게 알아보았다. 이제는 데이터베이스에서 트랜잭션을 안정적으로 실행하기 위해 보장돼야 하는 특성들에 대해 알아보자. 예상했겠지만 많이들 알고 있는 acronym인 ACID에 대해 알아볼 것이다. ACIDAtomicity, Consistency, Isolation, Durability의 앞글자만 딴 줄임말로, 트랜잭션하면 꼭 알아두어야 할 특성이다. 사실 공부할때는 트랜잭션은 ACID 특성을 보장해야 한다고 정의되지만, 실제로는 ACID원칙이 완벽히 지켜지지 않는 경우도 많다. ACID 원칙을 엄격하게 지키기 위해서는 데이터베이스의 성능을 포기해야 하기 때문이다. 이에 대해서는 뒷 부분에서 더 자세히 다룰 예정이지만 그냥 그렇다는 정도만 알아두고 ACID에 대해 알아보자.

 

Atomicity (원자성)

Atomicity란 앞에서 말했던 트랜잭션의 all or nothing특성을 의미한다. 다시 말해 트랜잭션 내의 여러 작업들은 데이터베이스에 모두 반영되던지 아니면 전혀 반영되지 않아야 한다는 것이다. 트랜잭션을 실행하다가 어떤 작업이 일관성을 깨트리거나 실패한 경우에는 트랜잭션 전부를 취소해야 한다. 앞서 말했듯 DBMS 구현마다 다르지만 원자성을 보장하는 방식의 예를 하나 들어보면 트랜잭션이 COMMIT되기 전까지는 변경 내용을 디스크에 쓰지 않고 메모리 버퍼에만 저장해두었다가 중간에 실패하게 되면 디스크에 반영하지 않는 방식으로 원자성을 보장한다.

Consistency (일관성)

데이터의 일관성이란 모든 트랜잭션이 미리 설정된 constraint(제약조건)나 trigger에서 정한 규칙을 어기지 않고 데이터의 논리적 오류가 없는 일관적인 상태여야 함을 의미한다. 예를 들어 계좌의 balance가 0미만으로 떨어질 수 없다고 정의 해두었는데 특정 트랜잭션이 balance를 0미만으로 설정하는 등의 작업을 할 수 없다거나, balance는 numeric 타입으로 설정해두었는데 갑자기 문자열로 기록이 되면 안되는 등의 경우가 있을 수 있다. 간혹 Consistency른 분산 환경에서 동작하는 스토리지에서 각 노드별로 일관적인 데이터를 유지한다는 개념으로 이해하는 경우가 있는데, Consistent하다는 점에서는 비슷한 개념이지만 ACID에서의 C에 해당하는 의미는 아니다.

Isolation (격리성)

Isolation이란 트랜잭션을 수행할 때 한 트랜잭션이 실행하는 동안 다른 트랜잭션이 접근할 수 없음을 의미한다. 즉 모든 트랜잭션은 다른 트랜잭션으로부터 독립되어야 한다. 예를 들어 트랜잭션 1, 2, 3, 4가 거의 동시에 수행되더라도 1 -> 2 -> 3 -> 4 와 같이 수행한 것처럼 되게 만들어야 한다는 것이다. 이러한 개념을 Serializability라고 표현하는데, Isolation 특성은 다룰게 상당히 많아서 별도의 글에서 더 자세하게 다룰 예정이다.

Durability (지속성)

지속성은 하나의 트랜잭션이 성공적으로 수행되었다면 (COMMIT 되었다면) 영원히 기록되야 하는 특성을 말한다. 이는 트랜잭션이 성공적으로 수행되면 트랜잭션에 대한 로그를 남기는 식으로 보장되며, 중간에 시스템 문제가 발생했을때 데이터베이스 로그를 참고해서 성공했던 트랜잭션을 복구 할 수 있다. 그리고 여기서 영구적으로 저장된다는 것은 비휘발성 메모리인 HDD, SDD에 저장하는 것을 의미한다.

 

 

 

자 이렇게 트랜잭션이 무엇인지, 그리고 트랜잭션의 기본적인 특성이 무엇인지에 대해 알아보았다. 지금까지는 개괄적인 내용에 대해서 다루어본거고 사실 디테일하게 들어가면 끝이 없다. 예를 들어 Durability를 보장하기 위해 DBMS가 어떻게 동작하는지 등은 DBMS의 종류마다 다르고, Isolation을 보장하는 방식도 다 조금씩 다르다. 따라서 실제로 내가 속한 회사에서 어떤 DBMS를 쓰냐에 따라서도 공부해야할 내용이 달라질 수 있다. 필자는 이 모든 것들을 알지도 못하고, 앞으로도 다 알 수는 없다고 생각하기 때문에 성능 및 안정성을 개선하기 위해 DBMS의 문서를 읽고 직접 튜닝하는데 도움이 될 수 있는 개념들을 정리하고자 한다. 그 중에서도 Isolationconcurrency control에 대해 집중적으로 알아볼 예정이다. 따라서 다음 글에서는 Serializability가 의미하는 바가 무엇인지, 그리고 Recoverability가 무엇인지에 대해 알아보도록 하겠다.