[DB] 트랜잭션(Transaction)
트랜잭션(Transaction)은 데이터베이스에서 하나의 논리적 작업 단위로 처리되는 일련의 데이터베이스 연산을 뜻합니다. 트랜잭션은 여러 연산을 하나로 묶어서 실행하는 개념으로, 연산이 모두 성공할 때만 데이터베이스에 적용되며, 하나라도 실패할 경우 전체 연산이 취소됩니다. 이처럼 트랜잭션을 통해 데이터의 일관성과 무결성을 보장할 수 있습니다.
트랜잭션의 4가지 주요 속성 (ACID 원칙)
1. Atomicity (원자성): 트랜잭션에 포함된 작업들은 모두 성공하거나 모두 실패해야 합니다. 하나의 연산이라도 실패할 경우, 이미 실행된 다른 연산들도 취소(롤백)되어야 합니다.
2. Consistency (일관성): 트랜잭션이 실행되기 전과 후에 데이터베이스가 항상 일관된 상태를 유지해야 합니다. 이를 통해 데이터의 무결성을 보장할 수 있습니다.
3. Isolation (격리성): 동시에 여러 트랜잭션이 실행될 때 트랜잭션 간의 간섭이 발생하지 않도록 보장하는 속성입니다. 특정 트랜잭션이 완료될 때까지 다른 트랜잭션이 중간 상태에 접근하지 못하게 합니다.
4. Durability (지속성): 트랜잭션이 성공적으로 완료되면 그 결과는 영구적으로 저장되며 시스템 오류가 발생해도 결과가 유지됩니다.
트랜잭션의 동작 흐름
• 시작: 트랜잭션이 시작되며 연속적인 연산이 실행됩니다.
• 커밋(Commit): 모든 연산이 성공적으로 끝나면 COMMIT 명령을 실행해 트랜잭션을 확정합니다. 이때 데이터베이스는 변경 사항을 확정하여 다른 사용자와 공유합니다.
• 롤백(Rollback): 트랜잭션 내 연산 중 하나라도 실패하면 ROLLBACK 명령을 통해 모든 변경 사항을 취소하고 이전 상태로 되돌립니다.
트랜잭션이 중요한 이유
• 데이터 무결성 보장: 중간 연산이 실패했을 때 데이터가 잘못된 상태로 남지 않도록 보호합니다.
• 에러 처리 용이: 트랜잭션의 원자성과 격리성 덕분에 트랜잭션이 실패하면 단순히 롤백으로 되돌릴 수 있어, 에러 처리가 간편해집니다.
• 복잡한 작업 처리: 여러 단계로 이루어진 작업을 하나의 단위로 묶어 처리할 수 있어 개발과 유지보수가 쉬워집니다.
트랜잭션의 예제 코드
1. 데이터 일관성 예시 (은행 계좌 이체):
• A 계좌에서 100원을 인출하고, B 계좌에 100원을 입금하는 작업을 하나의 트랜잭션으로 처리하면 중간에 문제가 생겨도 데이터가 불완전하게 변하지 않습니다.
BEGIN TRANSACTION; -- 트랜잭션 시작
UPDATE accounts SET balance = balance - 100 WHERE account_id = 'A';
UPDATE accounts SET balance = balance + 100 WHERE account_id = 'B';
COMMIT; -- 성공적으로 모든 작업이 완료되었을 때 확정
2. 롤백 예시 (트랜잭션 중 실패 발생 시):
• 트랜잭션 도중에 오류가 발생하면 작업을 취소하고 롤백합니다.
BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 'A';
-- 두 번째 쿼리 오류 발생
UPDATE accounts SET balance = balance + 100 WHERE account_id = 'B';
ROLLBACK; -- 트랜잭션 취소, 모든 변경사항을 원상 복구
트랜잭션의 사용 시나리오
• 은행 계좌 거래: 입출금, 이체와 같은 작업에서 데이터 무결성을 유지해야 할 때.
• 재고 관리: 주문과 재고 변경이 동시에 이루어져야 할 때.
• 주문 처리 시스템: 고객의 주문이 접수될 때 주문 상태와 결제 상태를 함께 처리하여 완전한 주문이 이루어지도록 보장할 때.
트랜잭션은 데이터의 안전성과 무결성을 보장하는 필수적인 기능으로, 특히 복잡한 작업을 수행할 때 데이터의 신뢰성을 높여줍니다.
+ 트랜잭션의 주요 특징
1. 격리 레벨 (Isolation Level)
트랜잭션 격리 레벨은 여러 트랜잭션이 동시에 실행될 때 데이터 일관성을 어떻게 유지할지를 결정하는 설정입니다. 트랜잭션 격리 수준이 낮을수록 동시성이 높아지고, 높을수록 일관성이 강화됩니다. 각 격리 레벨에서 발생할 수 있는 현상도 다릅니다.
• Read Uncommitted: 트랜잭션이 커밋되지 않은 데이터를 다른 트랜잭션이 읽을 수 있습니다.
• 문제: Dirty Read(더티 리드)가 발생할 수 있어 데이터 신뢰성 문제가 생깁니다.
• Read Committed: 커밋된 데이터만 읽을 수 있으며, 커밋되지 않은 데이터는 다른 트랜잭션에서 접근 불가.
• 문제: Non-repeatable Read(비반복적 읽기)가 발생할 수 있습니다.
• Repeatable Read: 트랜잭션이 시작될 때 읽은 데이터를 트랜잭션이 끝날 때까지 일관되게 유지.
• 문제: Phantom Read(팬텀 리드) 가능성은 있지만 더티 리드와 비반복적 읽기는 방지.
• Serializable: 가장 높은 격리 수준으로, 트랜잭션을 순차적으로 실행해 완벽한 일관성을 유지.
• 문제 해결: 팬텀 리드, 더티 리드, 비반복적 읽기를 모두 방지하지만 성능 저하 가능성이 큽니다.
2. 잠금 방식 (Locking Mechanism)
트랜잭션의 동시성을 제어하기 위해 트랜잭션이 특정 데이터를 잠그는 방식입니다. 주요 방식에는 비관적 잠금과 낙관적 잠금이 있습니다.
• 비관적 잠금 (Pessimistic Lock): 충돌 가능성이 높은 환경에서 사용하며, 트랜잭션이 데이터에 접근할 때 잠금을 걸어 다른 트랜잭션의 접근을 막습니다.
• 낙관적 잠금 (Optimistic Lock): 충돌이 적을 것으로 가정하고 트랜잭션 작업을 수행하다가, 최종 커밋 시점에서 충돌 여부를 체크하여 충돌 시 롤백하는 방식입니다.