안동민 개발노트 아이콘

안동민 개발노트

10장 : 트랜잭션

트랜잭션 제어

트랜잭션을 시작하고, 확정하고, 취소하는 명령어를 다룹니다. Oracle과 MySQL은 트랜잭션 시작 방식이 다르므로 주의가 필요합니다. 정확한 트랜잭션 제어를 모르면 데이터가 의도치 않게 확정되거나 유실될 수 있습니다.


COMMIT과 ROLLBACK

COMMIT은 트랜잭션의 모든 변경을 확정하고 이후의 읽기에서 볼 수 있는 상태로 만듭니다. 이미 시작된 다른 트랜잭션이 같은 값을 즉시 보느냐는 격리 수준과 스냅샷 시점에 따라 달라질 수 있습니다. ROLLBACK은 아직 커밋되지 않은 변경을 취소하여 트랜잭션 시작 전 상태로 되돌립니다. 커밋 결과의 실제 내구성은 로그 flush, 동기 커밋 설정, 스토리지 안정성의 영향을 받습니다.

기본 트랜잭션 제어 (Oracle)
-- Oracle은 DML 실행 시 자동으로 트랜잭션 시작 (수동 커밋 모드)
UPDATE accounts SET balance = balance - 100000 WHERE id = 1;
UPDATE accounts SET balance = balance + 100000 WHERE id = 2;

-- 성공하면 확정
COMMIT;

-- 문제가 있으면 COMMIT 대신 취소
-- ROLLBACK;
기본 트랜잭션 제어 (MySQL)
-- MySQL InnoDB는 기본 자동 커밋 (autocommit=1)
-- 명시적 트랜잭션:
START TRANSACTION;

UPDATE accounts SET balance = balance - 100000 WHERE id = 1;
UPDATE accounts SET balance = balance + 100000 WHERE id = 2;

COMMIT;
-- 문제가 있으면 COMMIT 대신
-- ROLLBACK;
명령어동작잠금비용
COMMIT변경 확정, 이후 공개보통 해제로그 기록/flush 설정 영향
ROLLBACK미커밋 변경 취소보통 해제Undo 적용, 변경량이 크면 부담

여기서 "공개"와 "잠금 해제"는 일반적인 경향입니다. 긴 스냅샷을 이미 잡은 트랜잭션은 커밋된 최신 값을 바로 보지 않을 수 있고, 일부 잠금이나 메타데이터 잠금은 DBMS 구현과 격리 수준에 따라 정리 시점이 다를 수 있습니다.


Oracle vs MySQL 자동 커밋 차이

항목OracleMySQL (InnoDB)
기본 경향수동 커밋, 도구 설정 확인 필요자동 커밋 (autocommit=1)
트랜잭션 시작DML 실행 시 자동START TRANSACTION, BEGIN, autocommit=0
COMMIT 하지 않으면세션 종료 시 보통 ROLLBACKautocommit=1이면 각 문장이 자동 COMMIT
DDL 실행 시묵시적 COMMIT 발생많은 DDL에서 묵시적 COMMIT 발생

Oracle에서 UPDATE 후 COMMIT을 안 하면 다른 세션에서 변경된 데이터를 볼 수 없고, 세션이 종료되면 보통 변경이 취소됩니다. MySQL은 기본적으로 각 SQL이 자동 커밋되므로, 여러 DML을 하나로 묶으려면 START TRANSACTION을 선언하거나 세션의 autocommit을 끄는 방식을 명확히 선택해야 합니다.

실무에서는 DBMS 기본값만 보지 말고 클라이언트 도구, 드라이버, 커넥션 풀의 autocommit 설정까지 함께 확인해야 합니다. 같은 DB라도 SQL 콘솔, 배치 프로그램, 웹 애플리케이션의 커밋 경계가 다를 수 있습니다.

MySQL 자동 커밋 제어
-- 현재 설정 확인
SELECT @@autocommit;  -- 1 = 자동 커밋 ON

-- 세션 단위로 자동 커밋 끄기
SET autocommit = 0;
-- → 이후 모든 DML은 명시적 COMMIT/ROLLBACK 필요
-- → Oracle과 유사한 동작

-- 다시 켜기
COMMIT;  -- 또는 ROLLBACK으로 현재 트랜잭션을 먼저 끝낸다
SET autocommit = 1;
-- MySQL에서는 autocommit을 0에서 1로 바꾸면 현재 트랜잭션이 커밋됨

-- START TRANSACTION은 autocommit 설정과 관계없이
-- 명시적 트랜잭션 시작
START TRANSACTION;
-- autocommit=1이어도 COMMIT/ROLLBACK까지 하나의 트랜잭션
-- 이미 진행 중인 트랜잭션이 있으면 새 트랜잭션 시작 전에 암묵 COMMIT될 수 있음

SAVEPOINT와 부분 롤백

SAVEPOINT는 트랜잭션 중간에 체크포인트를 설정하여, 전체가 아닌 특정 지점까지만 롤백할 수 있게 합니다.

ROLLBACK TO SAVEPOINT는 트랜잭션을 끝내지 않습니다. 지정 지점 이후의 변경만 되돌리고 트랜잭션은 계속 열린 상태이므로, 마지막에는 반드시 COMMIT 또는 전체 ROLLBACK으로 경계를 닫아야 합니다.

SAVEPOINT 사용 (Oracle)
-- 주문 처리 트랜잭션
INSERT INTO orders (id, user_id, total)
VALUES (orders_seq.NEXTVAL, 1, 50000);

SAVEPOINT after_order;

INSERT INTO order_items (id, order_id, product_id, quantity, price)
VALUES (items_seq.NEXTVAL, orders_seq.CURRVAL, 101, 2, 25000);

-- 재고 부족 발견!
ROLLBACK TO after_order;
-- → order_items INSERT만 취소, orders INSERT는 유지

-- 다른 상품으로 대체
INSERT INTO order_items (id, order_id, product_id, quantity, price)
VALUES (items_seq.NEXTVAL, orders_seq.CURRVAL, 102, 1, 50000);

COMMIT;

SAVEPOINT 규칙

SAVEPOINT는 부분 커밋이 아닙니다. SAVEPOINT 이전 변경도 최종 COMMIT 전까지는 확정되지 않습니다. 또한 ROLLBACK TO 이후 잠금이 어디까지 풀리는지는 DBMS와 작업 종류에 따라 다를 수 있으므로, SAVEPOINT를 긴 트랜잭션을 안전하게 길게 유지하는 도구로 오해하면 안 됩니다.

SAVEPOINT DBMS별 구문
-- Oracle
SAVEPOINT point_name;
ROLLBACK TO point_name;

-- MySQL
SAVEPOINT point_name;
ROLLBACK TO SAVEPOINT point_name;
RELEASE SAVEPOINT point_name;  -- 명시적 삭제

-- PostgreSQL
SAVEPOINT point_name;
ROLLBACK TO SAVEPOINT point_name;
RELEASE SAVEPOINT point_name;

-- SQL Server (트랜잭션 이름 방식)
SAVE TRANSACTION point_name;
ROLLBACK TRANSACTION point_name;
-- @@TRANCOUNT는 줄지 않으므로 최종 COMMIT/ROLLBACK 필요

DDL과 암묵적 커밋

Oracle과 MySQL은 많은 DDL(CREATE, ALTER, DROP) 실행 전후에 현재 트랜잭션을 자동으로 커밋합니다. 운영 중 DDL은 트랜잭션 경계를 바꿀 수 있으므로 DML과 섞지 않는 편이 안전합니다.

암묵적 커밋의 함정 (Oracle)
UPDATE accounts SET balance = balance - 100000 WHERE id = 1;
-- 아직 COMMIT 안 함

CREATE TABLE temp_log (msg VARCHAR2(200));
-- DDL 실행 → 암묵적 COMMIT 발생!
-- 위의 UPDATE가 자동으로 확정됨

ROLLBACK;
-- 이미 커밋되었으므로 롤백 불가!
PostgreSQL의 트랜잭셔널 DDL
-- PostgreSQL: 많은 DDL은 트랜잭션 안에서 롤백 가능
BEGIN;

CREATE TABLE new_table (id SERIAL, name TEXT);
INSERT INTO new_table (name) VALUES ('test');

-- 확인 후 문제 발견
ROLLBACK;
-- → CREATE TABLE도 취소됨

PostgreSQL도 모든 DDL이 트랜잭션 블록 안에서 가능한 것은 아닙니다. 예를 들어 CREATE DATABASE, VACUUM, CREATE INDEX CONCURRENTLY처럼 트랜잭션 블록 밖에서 실행해야 하는 명령이 있습니다.

반대로 Oracle과 MySQL에서 DDL의 묵시적 커밋은 "DDL만 커밋된다"가 아니라 그 전에 열려 있던 DML 트랜잭션의 경계까지 바꿀 수 있다는 점이 핵심입니다.


트랜잭션 제어와 잠금

트랜잭션이 끝나기 전까지(COMMIT 또는 ROLLBACK) 변경한 행에 대한 쓰기 잠금은 보통 유지됩니다. 읽기 잠금, gap lock, predicate lock, MVCC 스냅샷 유지 방식은 DBMS와 격리 수준에 따라 달라집니다.

트랜잭션 잠금 관련 실무 원칙
1. 트랜잭션은 가능한 짧게 유지
   → 잠금 시간을 최소화하여 동시성 확보

2. 사용자 입력을 기다리는 중에 트랜잭션을 열어두지 않기
   → "SELECT ... FOR UPDATE" 후 사용자 확인 대기 → 위험!

3. 대량 DML은 배치로 나누어 중간 COMMIT
   → 100만 건 UPDATE를 한 트랜잭션으로 → 언두/로그 폭증
   → 업무 원자성을 해치지 않는 범위에서 단위 분할

4. COMMIT 빈도와 데이터 정합성의 균형
   → 너무 잦은 COMMIT → 원자성 깨질 수 있음
   → 너무 드문 COMMIT → 잠금 + 리소스 부담

읽기 전용 트랜잭션과 읽기/쓰기 트랜잭션

읽기 전용 트랜잭션
-- Oracle
SET TRANSACTION READ ONLY;
-- → 일반 사용자 테이블 쓰기는 제한, DML 실행 시 에러
-- → 트랜잭션 시작 시점의 일관된 스냅샷 읽기 (Consistent Read)
-- → 임시 객체 등 예외는 DBMS 문서와 운영 규칙 확인

-- MySQL
START TRANSACTION READ ONLY;
-- → 일반 테이블 쓰기 방지와 일부 최적화 가능
-- → 임시 테이블 등 예외 동작은 버전과 엔진별로 확인

-- PostgreSQL
BEGIN TRANSACTION READ ONLY;

-- SQL Server
SET TRANSACTION ISOLATION LEVEL SNAPSHOT;
BEGIN TRANSACTION;
-- → 스냅샷 일관성은 제공하지만 읽기 전용 선언은 아님
-- → SNAPSHOT 격리 사용은 DB 옵션 설정이 필요할 수 있음
-- → 읽기 전용 보장은 권한, 라우팅, 애플리케이션 정책으로 분리
읽기 전용 트랜잭션의 이점
1. 일관된 조회: DBMS와 격리 수준에 따라 스냅샷 기준이 다름
   → Oracle READ ONLY는 트랜잭션 기준 스냅샷
   → PostgreSQL READ COMMITTED READ ONLY는 문장 기준 스냅샷
   → PostgreSQL에서 트랜잭션 기준 스냅샷이 필요하면 REPEATABLE READ 이상 검토

2. 성능 최적화 가능성: DBMS에 따라 쓰기 방지, 스냅샷, 라우팅 최적화에 활용

3. 보고서 생성 시 유용:
   Oracle: SET TRANSACTION READ ONLY;
   MySQL: START TRANSACTION READ ONLY;
   PostgreSQL: BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ READ ONLY;

트랜잭션 제어와 애플리케이션 패턴

실무에서 트랜잭션 제어를 잘못하면 데이터 정합성 문제나 성능 문제가 발생합니다.


DBMS별 트랜잭션 제어 비교 종합


자율 트랜잭션 (Autonomous Transaction)

Oracle에서는 현재 트랜잭션과 독립적인 별도 트랜잭션을 실행할 수 있습니다.

자율 트랜잭션은 감사 로그처럼 "메인 업무가 실패해도 남겨야 하는 기록"에 제한적으로 쓰는 기능입니다. 본 트랜잭션과 원자적으로 묶이지 않으므로, 주문·결제 같은 핵심 업무 상태를 자율 트랜잭션으로 쪼개면 정합성 문제가 생길 수 있습니다.

Oracle 자율 트랜잭션
CREATE OR REPLACE PROCEDURE log_event(p_msg VARCHAR2) IS
    PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
    INSERT INTO event_log (msg, log_time)
    VALUES (p_msg, SYSDATE);
    COMMIT;  -- 메인 트랜잭션과 독립적으로 커밋
END;

-- 메인 트랜잭션
BEGIN
    UPDATE accounts SET balance = balance - 100 WHERE id = 1;
    log_event('출금 시도');  -- 별도 트랜잭션으로 로그 기록
    
    -- 에러 발생!
    ROLLBACK;
    -- → accounts UPDATE는 롤백
    -- → event_log INSERT는 이미 커밋되어 유지됨!
END;
자율 트랜잭션 활용 사례
* 감사(Audit) 로그: 메인 트랜잭션 실패해도 로그는 남겨야 함
* 에러 로그: 예외 발생 시 에러 정보를 DB에 기록
* 보조 이력: 메인 업무 롤백과 분리해 남겨야 하는 이력

다른 DBMS에서의 대안
  MySQL: 별도 커넥션이나 외부 로그 저장소
  PostgreSQL: dblink, 별도 커넥션, 외부 로그 저장소
  단, 본 트랜잭션과 원자적으로 묶이지 않는다는 점을 설계에 반영

트랜잭션 제어 흐름 정리


정리

개념핵심 내용
COMMIT변경 확정, 로그 기록/flush 정책 영향
ROLLBACK미커밋 변경 취소, 변경량이 크면 비용 증가
SAVEPOINT트랜잭션 중간 체크포인트, 부분 롤백 가능
자동 커밋DBMS와 클라이언트 설정 확인 필요
암묵적 커밋Oracle/MySQL DDL에서 특히 주의
잠금 유지변경 행 잠금은 보통 COMMIT/ROLLBACK까지 유지
읽기 전용쓰기 방지와 스냅샷/라우팅 최적화에 활용 가능

트랜잭션 제어의 핵심은 짧고 명확한 트랜잭션을 유지하는 것입니다. 트랜잭션이 길어지면 잠금, 스냅샷, Undo/Redo, 복제 지연 비용이 커지고, 대량 변경 후 ROLLBACK은 매우 부담스러울 수 있습니다. COMMIT과 ROLLBACK의 경계를 명확히 하고, DDL에 의한 암묵적 커밋에 주의해야 합니다.

다음 절에서는 트랜잭션의 상태 전이직렬 가능성을 다루겠습니다.