본문 바로가기
카테고리 없음

DB - 트랜잭션 ACID

by kik328288 2026. 5. 15.

트랜잭션 (ACID, 커밋, 로그)

데이터베이스가 "신뢰할 수 있는 저장소"인 이유는 트랜잭션(Transaction)과 ACID 속성이 받쳐 주기 때문이다. 송금 한 번이 중간에 끊겨도 돈이 사라지지 않고, 동시에 1만 명이 같은 좌석을 예약해도 한 사람만 성공한다. 이 모든 보장의 토대가 트랜잭션 메커니즘이다. 본 글은 트랜잭션의 정의·ACID 4대 속성·트랜잭션 상태 다이어그램·WAL(Write-Ahead Logging) 기반 복구 메커니즘을 세심하게 정리한다(출처: 위키백과 — ACID). 제가 학교 캡스톤에서 결제 처리 코드를 트랜잭션 없이 UPDATE balance ... ; INSERT INTO ledger ... 두 줄로 짰다가 한 줄만 성공한 상태에서 서버가 죽어 잔액과 장부가 어긋나는 사고를 겪은 후로는 "두 줄 이상의 SQL은 무조건 트랜잭션"이라는 사실을 손끝으로 받아들였다.

 

트랜잭션의 정의와 4대 ACID 속성

트랜잭션이란 데이터베이스 상태를 한 일관된 상태에서 다른 일관된 상태로 옮기는, 논리적으로 더 쪼갤 수 없는 단일 작업 단위를 가리킨다. 송금 한 건은 "A 계좌에서 1만 원 차감 + B 계좌에 1만 원 증액" 두 SQL로 구성되지만, 외부에서 보면 한 번에 끝나거나 한 번에 실패한 것처럼 보여야 한다. 이 모든 것이 트랜잭션의 4대 속성, 곧 ACID로 보장된다.

속성 의미 DBMS 강제 도구
원자성 (Atomicity) 트랜잭션 안의 모든 연산이 모두 성공하거나 모두 실패 UNDO 로그·롤백
일관성 (Consistency) 트랜잭션 종료 후 모든 무결성 제약이 만족 제약 조건·트리거
고립성 (Isolation) 동시 실행 트랜잭션끼리 서로 간섭하지 않음 락·MVCC·격리 수준
지속성 (Durability) 커밋된 결과는 시스템 장애에도 사라지지 않음 REDO 로그·WAL

여기서 원자성(atomicity)이란 트랜잭션이 "all-or-nothing"으로 동작해 중간 상태가 외부에 노출되지 않는 성질을 가리키며, 송금 도중 서버가 죽어도 차감만 되고 증액이 안 된 상태는 절대 만들어지지 않는다. DBMS는 트랜잭션 시작 시점부터 모든 변경 이전 값을 UNDO 로그에 기록해 두었다가, 실패하면 그 로그를 역재생해 원상복구한다.

-- 트랜잭션의 기본 골격
START TRANSACTION;
    UPDATE account SET balance = balance - 10000 WHERE id = 'A';
    UPDATE account SET balance = balance + 10000 WHERE id = 'B';
    INSERT INTO ledger (from_id, to_id, amount) VALUES ('A', 'B', 10000);
-- 모두 성공하면 커밋, 하나라도 실패하면 롤백
COMMIT;        -- 또는 ROLLBACK;

ACID 4대 속성은 시험에서 거의 매회 출제되며, 단순 암기보다는 "어떤 종류의 사고를 막아 주는가"를 짝지어 외워야 한다. 원자성은 부분 실행 사고를, 일관성은 무결성 위배 사고를, 고립성은 동시성 사고를, 지속성은 장애 후 데이터 손실 사고를 각각 막아 준다. 솔직히 제 경험상 학교 시험에서 "고립성과 일관성의 차이"를 정확히 구분 못 해 헷갈렸다가, "고립성은 트랜잭션 간 간섭 차단, 일관성은 한 트랜잭션 후 무결성"이라는 한 줄로 끊어서 외운 후로는 헷갈리지 않게 되었다.

트랜잭션 상태 다이어그램과 COMMIT·ROLLBACK

트랜잭션은 일생 동안 다섯 가지 상태를 거친다. 시험에서 자주 출제되는 상태 다이어그램이다.

상태 의미 다음 상태
활성 (Active) 트랜잭션이 시작되어 SQL 실행 중 부분 완료 또는 실패
부분 완료 (Partially Committed) 마지막 SQL이 끝났지만 디스크 반영 전 완료 또는 실패
실패 (Failed) 정상 실행이 불가능해진 상태 철회로 진행
철회 (Aborted) ROLLBACK이 완료되어 원상복구 끝남 종료
완료 (Committed) COMMIT으로 디스크 반영 완료 종료

여기서 부분 완료(partially committed)란 모든 SQL은 끝났지만 변경 사항이 아직 디스크의 영구 저장소에 반영되지 않은 메모리 버퍼 상태를 가리키며, 이 시점에 시스템이 죽으면 결과가 사라질 수 있어 "거의 끝난 상태일 뿐 끝난 상태는 아니다." DBMS는 부분 완료 → 완료로 넘어갈 때 WAL의 커밋 레코드를 디스크에 강제 동기화(fsync)해야만 비로소 "지속성이 보장된 완료"로 인정한다.

-- 명시적 롤백 — 비즈니스 로직에서 실패 처리
START TRANSACTION;
    UPDATE inventory SET stock = stock - 1 WHERE product_id = 100;
    -- 재고가 음수가 되면 비즈니스 규칙 위반
    SELECT stock FROM inventory WHERE product_id = 100;
    -- 결과가 음수면
ROLLBACK;       -- 위 UPDATE도 함께 취소됨

-- SAVEPOINT — 트랜잭션 중간 체크포인트
START TRANSACTION;
    INSERT INTO orders (user_id, total) VALUES (1, 50000);
    SAVEPOINT sp_orders;
    INSERT INTO order_items (order_id, product_id) VALUES (LAST_INSERT_ID(), 99);
    -- 상품 99가 재고 0이라면
    ROLLBACK TO SAVEPOINT sp_orders;     -- order_items만 취소
    INSERT INTO order_items (order_id, product_id) VALUES (LAST_INSERT_ID(), 100);
COMMIT;

여기서 SAVEPOINT란 한 트랜잭션 안에서 부분적인 롤백 지점을 표시해 두는 메커니즘을 가리키며, 큰 트랜잭션의 일부만 취소하고 나머지는 유지해야 할 때 유용하다. 솔직히 제 경험상 학교 캡스톤에서 처음 SAVEPOINT를 발견했을 때는 "왜 이런 게 필요할까" 싶었는데, 결제 + 포인트 적립 + 쿠폰 사용이 한 트랜잭션에 묶인 코드에서 쿠폰 사용만 실패할 때 결제와 포인트는 살려야 했던 사례를 만나고 나서야 그 가치를 손끝으로 받아들였다.

WAL과 지속성 — 시스템이 죽어도 살아남는 메커니즘

지속성(Durability)은 ACID 4대 속성 중에서 가장 구현이 까다롭다. "커밋된 결과는 어떤 장애에도 사라지지 않는다"는 약속을 디스크가 한 번에 한 블록만 쓸 수 있다는 현실 위에서 지켜야 하기 때문이다. 현대 DBMS는 WAL(Write-Ahead Logging, 선기록 로깅) 기법으로 이 문제를 해결한다(출처: PostgreSQL 공식 문서 — Reliability and WAL).

WAL의 핵심 원칙은 단순하다. 데이터 페이지를 디스크에 쓰기 전에 그 변경 사항을 로그에 먼저 기록해 두라는 것이다. 변경이 두 곳(데이터 파일 + 로그 파일)에 기록되면, 한쪽이 디스크에 도달하지 못해도 다른 쪽으로 복구할 수 있다.

단계 동작 보장
1. 변경 발생 메모리 버퍼에 새 값 기록
2. 로그 작성 WAL 버퍼에 변경 레코드 기록
3. 로그 디스크 동기화 COMMIT 시 WAL을 fsync로 디스크 반영 지속성 확정
4. 데이터 페이지 디스크 반영 백그라운드 체크포인트로 데이터 파일에 기록
5. 장애 발생 시 마지막 체크포인트 이후 WAL을 재생(REDO) 데이터 복구

3단계의 fsync 호출 시점이 트랜잭션의 "진짜 커밋 시점"이다. 그래서 트랜잭션이 빠르려면 WAL 디스크가 빨라야 하고, 클라우드 DB가 SSD·NVMe를 표준으로 채택한 결정적 이유가 바로 여기에 있다. 또한 장애가 나면 DBMS는 마지막 체크포인트(checkpoint) 이후의 WAL을 처음부터 재생해서 "커밋된 변경"은 다시 적용하고(REDO), "미커밋 변경"은 취소(UNDO)해 일관 상태로 복구한다.

-- PostgreSQL의 WAL 관련 설정
SHOW wal_level;             -- replica / logical / minimal
SHOW synchronous_commit;    -- on (기본) / off / local / remote_apply
SHOW checkpoint_timeout;    -- 보통 5min~30min

여기서 체크포인트(checkpoint)란 메모리에 머무는 더티 페이지(dirty page, 아직 디스크에 안 쓴 변경된 페이지)를 디스크의 데이터 파일에 일괄 반영하고 그 시점을 WAL에 표시해 두는 작업을 가리키며, 체크포인트가 자주 일어나면 복구가 빨라지지만 일상 성능이 떨어지는 트레이드오프가 있다. 솔직히 이건 예상 밖이었는데, 학교에서는 "DB는 그냥 쓰면 디스크에 기록된다"고 막연히 생각했다가 PostgreSQL 운영 설정을 처음 들여다보고 fsync·체크포인트·WAL 세 가지가 트랜잭션 성능의 90%를 결정한다는 사실을 알고 난 후로는 "지속성은 디스크 동기화 한 줄에 달려 있다"는 점을 손끝으로 받아들였다.

마지막으로 시험 답안에서 자주 쓰이는 정형 표현을 정리하면, "트랜잭션은 데이터베이스 상태를 한 일관된 상태에서 다른 일관된 상태로 옮기는 논리적 단일 작업 단위이며, 원자성·일관성·고립성·지속성(ACID) 4대 속성으로 신뢰성을 보장한다. 현대 DBMS는 WAL(Write-Ahead Logging) 기법으로 변경을 로그에 먼저 기록한 뒤 데이터 파일에 반영하며, 장애 시 REDO·UNDO로 복구해 지속성을 구현한다"는 두 문장이 표준 답안 표현으로 통한다. 다음 글에서는 ACID 중 고립성(Isolation)을 깊게 다루는 격리 수준과 동시성 제어를 이어 다룬다.


메타 디스크립션: 트랜잭션의 정의와 ACID 4대 속성(원자성·일관성·고립성·지속성), 트랜잭션 상태 다이어그램과 COMMIT/ROLLBACK/SAVEPOINT, WAL(Write-Ahead Logging) 기반 복구 메커니즘을 DB 입문자 관점에서 세심하게 정리합니다.


소개 및 문의 · 개인정보처리방침 · 면책조항

© 2026 블로그 이름