트랜잭션 관리는 백엔드 개발자에게 있어서 매우 중요한 일 중 하나이다. 트랜잭션이란 데이터를 다루는 일련의 수행 작업 단위를 의미한다. 예를 들어 A라는 사람이 B라는 사람에게 10만원을 빼앗는 작업을 수행한다고 하자. 이 때 A의 자산은 10만원이 증가해야 되고 B의 자산은 10만원이 감소한 상태로 작업이 완료돼야 한다. 만약, 해당 과정 수행 중에 문제가 생겼다면 아예 작업이 수행되기 전 초기 상태로 되돌아가야 한다. A의 자산은 10만원이 증가됐는데, B의 자산은 그대로인 경우가 존재하면 안된다.
위와 같이 트랜잭션은 데이터를 다루는 작업을 의미하기 때문에 매우 신중하게 관리해야 한다. 그렇기 때문에 트랜잭션은 ACID 성질을 보장해야 한다. ACID 성질이란 무엇일까?
원자성(Atomicity)
트랜잭션의 모든 연산들이 성공적으로 수행되거나, 완전히 실패하는 단일 단위로 처리되도록 보장하는 특성을 의미한다. 위에서 말한 예시를 생각하면 편하다. A가 B의 10만원을 빼앗아서 A는 10만원이 증가했는데, B는 10만원이 감소되지 않고 그대로 있다면 그 세계의 경제 시스템은 난리가 났을 것이다.
DB에서는 원자성을 지킬 수 있도록 커밋, 롤백을 제공하여 완전히 성공되거나 실패하도록 보장해준다.
또한, Redo, Undo 복구를 이용하여 원자성을 지킬 수도 있다. 이에 대한 간단한 설명을 아래에 언급할 예정이므로 여기선 넘어가도록 하겠다.
일관성(Consistency)
트랜잭션이 성공적으로 수행되면 언제나 일관된 데이터베이스 상태를 유지해야되는 특성을 의미한다. 말이 조금 어려운데, DB의 정합성과 무결성 제약 조건이 깨지지 않는 상태로 트랜잭션이 수행됨을 보장해야된단 의미이다.
단어 자체만 보면 지속성(durability)와 헷갈릴 수 있는데, 정의를 이해하면 아예 다른 특성임을 알 수 있다.
DB에서는 일관성을 지키기 위해 REDO를 제공한다.
redo log
A disk-based data structure used during crash recovery, to correct data written by incomplete transactions.
출처: MySQL 공식문서 - Consistency - InnoDB Crash Recovery - redo log
특정 이슈로 DB에 장애가 발생했거나, 일관성이 깨져서 엉망진창이 돼버린 경우 REDO 복구를 이용하여 DB를 백업해 복구할 수 있다. 이 REDO 복구는 일관성(Consistency)를 지키기 위해 존재할 뿐 아니라 지속성(Durability)을 지키기 위해서도 필요하다.
독립성(Isolation)
동시에 실행되는 트랜잭션이 서로에게 영향을 끼치지 않고 독립적으로 수행됨을 보장해야되는 특성을 의미한다.
A라는 트랜잭션이 update 작업을 수행하고 있다고 하자. B라는 트랜잭션은 A가 update 작업을 하고 있는 데이터를 select(조회)하는 작업을 수행한다. 이 경우, 적절한 격리를 시키지 않는다면 의도치 않은 데이터를 조회하게 되는 문제가 발생한다.
(ex. Dirty Read, Non-Repeatable Read)
DB에서는 독립성을 지키기 위해 Transaction Isolation Level (트랜잭션 격리레벨) 설정을 제공해주며, 이에는 UNDO 기능이 포함된다. UNDO는 트랜잭션 격리 레벨이 READ COMMITED 이상일 때부터 사용되며, 이를 통해 의도치 않은 상황을 방지할 수 있다.
트랜잭션 격리레벨에 대한 내용이 궁금하다면 아래 글을 참고하자.
https://kth990303.tistory.com/313
[Spring] @Transactional로 DB 동시성 문제를 방지하자
웹 데이터 애플리케이션을 만들 때, dao에서 sql문으로 db에 접근하고 service에서 dao 메서드들을 이용하여 하나의 트랜잭션을 관리한다. 그런데 만약 이 애플리케이션을 여러 명이서 동시에 사용한
kth990303.tistory.com
스프링에서는 이러한 문제를 방지하기 위해 Consistence Read를 제공하도록 Undo log를 활용한 MVCC 기능을 제공하여 트랜잭션 격리레벨을 설정할 수 있게 해준다. 위 글에 해당 내용이 언급되므로 궁금하다면 참고하도록 하자.
지속성(Durability)
트랜잭션을 성공적으로 수행 시, 그 결과가 보존되는 성질을 의미한다.
DB에 쿼리문을 날리면 디스크에 해당 변경사항들을 로그 파일로 저장한다.
또한, 로깅을 할 때에도 REDO, UNDO 개념은 출연하게 된다. 그 이유를 살펴보자.
지속성(Isolation)이 지켜지기 위해서는 UNDO 복구가 요구된다는 사실을 위에서 알 수 있다. 이러한 UNDO 복구는 DB 갱신이 되기 전에 UNDO 정보를 로그에 써주어야 가능하게 된다. 이렇게 UNDO 정보를 로그에 작성한 후 DB 갱신을 하는 규칙을 WAL(Write Ahead Logging) 규칙이라 한다. 이렇게 함으로써 Consistence Read를 제공할 수 있게 되고 Dirty Read가 발생하지 않을 수 있다.
일관성(Consistency)이 지켜지기 위해서는 REDO 복구가 요구된다는 사실을 위에서 알 수 있다. 이러한 REDO 복구는 최소한 트랜잭션이 종료되기 전, 커밋 시점에는 REDO 정보를 로그에 써주어야 가능하게 된다.
DB 데이터가 지속적으로 보관되기 위해서는 디스크에 로그 파일이 영구적으로 저장되는 것이 중요하다.
또한, 정합성과 무결성 제약 조건이 깨지지 않은 상태로 ACID 성질이 모두 만족해서 의도치 않은 장애 상황을 만나지 않도록 해야할 것이다.
이전에 @Transactional 관련 포스팅을 쓰면서 ACID 내용을 작성하겠다고 해놓고, 반년간 작성하지 않았다.
이제서야 마음의 짐을 좀 덜어내릴 수 있을 듯 하다 ㅎㅎ
틀린 부분이나 조금이라도 의문점이 있다면 코멘트 남겨주세요 :)