588. 분산 트랜잭션 코디네이터 (DTC)와 애플리케이션 트랜잭션 연합
⚠️ 이 문서는 한 대의 데이터베이스(DB) 안에서 돈을 빼고 넣는 것은 완벽하게 통제할 수 있지만, 은행(Oracle)에서 돈을 빼서 증권사(MS-SQL)로 이체하는 것처럼 서로 다른 두 개의 이기종(Heterogeneous) DB가 엮인 거대한 트랜잭션을 처리할 때, 둘 중 하나라도 실패하면 둘 다 완벽히 롤백(Rollback)시켜 데이터 불일치의 재앙을 막기 위해 멱살을 잡고 통제하는 '마에스트로', 즉 분산 트랜잭션 코디네이터(DTC)와 2PC(2단계 커밋) 프로토콜을 다룹니다.
핵심 인사이트 (3줄 요약)
- 본질: 여러 개의 찢어진 DB나 시스템들을 묶어서 "모두 다 같이 성공(Commit)하거나, 아니면 다 같이 실패(Rollback)하라!"는 절대 원칙(ACID)을 분산 환경에서 억지로 관철하기 위한 중앙 통제 미들웨어다.
- 가치: 이 코디네이터가 없으면 A 은행 통장에선 돈이 100만 원 빠져나갔는데 B 증권사 통장에는 에러가 나서 안 들어오는 최악의 '돈 증발(고아 트랜잭션)' 사태가 발생한다.
- 기술 체계: XA 표준 스펙을 따르는 **DTC(Distributed Transaction Coordinator)**가 지휘봉을 잡고, 각 DB들에게 "너희들 준비됐냐?"라고 묻는 **1단계(Prepare)**와 "그럼 다 같이 쏴!"라고 명령하는 **2단계(Commit)**로 이루어진 2PC (Two-Phase Commit) 아키텍처로 돌아간다.
Ⅰ. 단일 트랜잭션의 평화와 분산(Distributed)의 지옥
내 몸속 장기들은 통제가 되지만, 남의 몸속 장기까지 내 맘대로 통제할 순 없다.
- 로컬 트랜잭션의 평온함:
- 오라클 DB 1대 안에
국민은행_계좌테이블과국민은행_펀드테이블이 같이 있다. - 계좌에서 돈을 빼고(UPDATE), 펀드 테이블에 돈을 넣는(INSERT) 2개의 쿼리를 묶어 1개의 트랜잭션으로 날린다.
- 둘 다 완벽하게 끝나면
COMMIT, 중간에 펀드 테이블에 락(Lock)이 걸려 에러가 나면ROLLBACK을 치면 된다. 1대의 DB 엔진은 이 롤백을 100% 보장해 준다.
- 오라클 DB 1대 안에
- 분산 트랜잭션(Distributed Transaction)의 분열:
- 이번엔
국민은행(Oracle)에서 돈을 빼서, 1,000km 떨어진키움증권(MS-SQL)으로 100만 원을 송금한다. - 자바(Java) 백엔드 서버가 오라클에
UPDATE 잔액 - 100을 치고COMMIT을 성공시켰다. - 그다음에 MS-SQL로
UPDATE 잔액 + 100을 치려고 네트워크를 타는 순간! 광랜이 포크레인에 끊겨서(네트워크 단절) MS-SQL에 쿼리가 도달하지 못했다. - 대참사: 국민은행에서 100만 원이 빠져나갔는데, 키움증권엔 돈이 안 들어왔다. 국민은행 오라클은 이미
COMMIT을 때려버려서 자력으로 롤백이 불가능하다. 100만 원이 우주 허공으로 증발해 버렸다.
- 이번엔
📢 섹션 요약 비유: 로컬 트랜잭션은 내가 양손에 빵(출금)과 우유(입금)를 쥐고 먹는 것입니다. 빵을 먹다 체하면 우유도 바로 뱉어버릴 수 있습니다(완벽한 통제). 분산 트랜잭션은 나는 서울에서 빵을 먹고 친구는 부산에서 우유를 마시는 겁니다. 내가 빵을 삼켰는데(Commit), 부산 친구가 우유를 흘렸습니다(에러). 친구가 실패했으니 내가 삼킨 빵을 다시 토해내야(Rollback) 하는데, 이미 뱃속으로 들어간 빵(완료된 로컬 커밋)은 혼자 힘으론 절대 토해낼 수 없는 최악의 데이터 불일치 상태에 빠집니다.
Ⅱ. 2PC (Two-Phase Commit): 코디네이터의 지배
아무도 혼자서 먼저 꿀꺽 삼키지 마라. 내 허락이 떨어질 때까지 입에 물고 대기하라.
- DTC (Transaction Coordinator)의 등판:
- 위 참사를 막기 위해 자바 앱(WAS) 쪽에 **DTC(분산 트랜잭션 관리자, 예: JTA/Atomikos)**라는 무시무시한 중앙 지휘관을 등판시킨다.
- 오라클과 MS-SQL은 이제부터 개별적으로
COMMIT을 때릴 권한을 박탈당하고, 무조건 이 DTC의 명령에 복종해야 한다.
- Phase 1: 준비 (Prepare) 단계 - 장전과 락(Lock):
- 자바 앱이 쿼리를 날린다. 하지만 끝에
COMMIT대신 DTC가 개입한다. - DTC가 오라클과 MS-SQL 양쪽에게 동시에 무전을 친다. "야, 너희 둘 다 커밋할 준비(Prepare) 완벽히 끝났냐? 디스크 락(Lock) 꽉 잡고 대기해!"
- 오라클: "출금 준비 완료! (Ready)" / MS-SQL: "입금 준비 완료! (Ready)"
- 만약 이때 MS-SQL이 네트워크가 끊겨 대답을 못 하거나 에러를 뿜으면? 1단계에서 엎어진 것이므로 DTC는 오라클에게 즉시 "작전 취소! 롤백해!"라고 명령하여 돈이 빠져나가는 걸 사전에 100% 차단한다.
- 자바 앱이 쿼리를 날린다. 하지만 끝에
- Phase 2: 실행 (Commit) 단계 - 동시 폭격:
- 양쪽 DB가 모두 "준비 완료(Ready)" 무전을 보내왔다!
- 그제야 DTC는 만족하며 양쪽에 최후의 명령을 하달한다. "좋아, 이제 두 놈 다 0.1초 만에 동시에 커밋(Commit) 때려!!!"
- 오라클과 MS-SQL은 꽉 잡고 있던 락을 풀며 데이터를 동시에 확정 짓고 장부를 마감한다. 완벽하고 우아한 데이터 정합성의 승리다.
📢 섹션 요약 비유: 영화에서 마피아 두목들이 폭탄이 든 가방을 교환하는 장면(2PC)입니다. 1단계(Prepare)에서 두 보스는 가방을 반쯤 열어 돈과 폭탄이 진짜인지 '동시에 눈으로 확인'만 하고, 손은 가방 손잡이를 꽉 잡고 놓지 않습니다(Lock). 둘 다 고개를 끄덕여 사인이 맞으면, 2단계(Commit)에서 "하나, 둘, 셋!" 하고 동시에 손을 놓아 가방을 완벽하게 맞바꿉니다. 만약 1단계에서 한쪽 가방에 폭탄이 없으면? 그냥 둘 다 손을 떼지 않고 거래를 취소(Rollback)해 버리면 그만입니다. 어느 한쪽만 덤터기를 쓰는 일이 원천 차단됩니다.
Ⅲ. 2PC의 몰락과 SAGA 패턴으로의 진화
강력한 자물쇠는 속도를 죽인다. 느려 터진 마이크로서비스(MSA)의 적폐.
- 치명적 병목: Lock의 장기화:
- 2PC는 철통보안이지만 치명적인 단점이 있다. 1단계(Prepare) 무전을 치고 2단계(Commit) 명령이 떨어질 때까지, 오라클과 MS-SQL은 해당 데이터(통장 잔고)에 락(Lock)을 걸고 아무것도 못 한 채 벌을 서고 있어야 한다.
- 만약 DTC 코디네이터가 1단계 무전을 친 직후 갑자기 전원선이 뽑혀 죽어버린다면? 양쪽 DB는 "언제 커밋하나요?"라며 영원히 락을 잡고 무한 대기(Deadlock)에 빠져 은행 전체가 하얗게 마비되어 버린다. (SPOF의 재앙)
- 클라우드와 MSA 시대의 2PC 퇴출:
- 옛날 거대한 통짜 서버 시절엔 2PC가 훌륭했지만, 요즘처럼 서버를 100개로 쪼개는 마이크로서비스(MSA) 시대에는 통신(API) 지연이 너무 길어 2PC를 쓰면 속도가 나락으로 떨어져 버린다. (가용성 박살)
- 대안: 사가(Saga) 패턴의 등장:
- "락(Lock)을 걸고 기다리지 마라! 쿨하게 그냥 각자 로컬 트랜잭션으로 바로바로 커밋(
COMMIT) 때리고 락을 풀어줘라!" - 대신 "만약 뒤쪽 증권사에서 에러가 터졌다는 무전이 날아오면, 은행 서버는 아까 빼줬던 100만 원을 다시 채워 넣는 '보상 트랜잭션(Compensation, UPDATE 잔액 + 100)' 쿼리를 추가로 쏴서 수동으로 원상 복구시켜라!"
- 2PC의 무식한 Lock 대기를 버리고, 이벤트 기반(Kafka)으로 에러를 치유(보상)하는 SAGA 패턴이 현대 MSA 분산 트랜잭션의 압도적 왕좌를 차지하게 되었다.
- "락(Lock)을 걸고 기다리지 마라! 쿨하게 그냥 각자 로컬 트랜잭션으로 바로바로 커밋(
📢 섹션 요약 비유: 2PC는 두 사람이 2인 3각으로 발을 묶고(Lock) 같이 달리는 것입니다. 한 명이 넘어지면 둘 다 자빠집니다. 느리고 답답합니다. SAGA 패턴은 발을 묶지 않고 각자 차를 타고 목적지로 쿨하게 달려갑니다(Lock 해제, 광속 처리). 만약 한 명이 차 사고가 나서 못 오면? 도착한 친구에게 "야, 나 못 가니까 너도 걍 돌아와라"라고 무전(보상 트랜잭션)을 쳐서 차를 돌려 다시 집으로 오게 만드는, 속도(성능)를 위해 순간의 불일치를 감수하는 현대적인 융통성입니다.