Linked Server와 분산 트랜잭션, 그리고 트랜잭션 회피 전략
실무에서 여러 DB 서버 간 데이터를 처리하다 보면 Linked Server를 활용해야 할 일이 생깁니다.
이때 트랜잭션과 관련해 예상치 못한 오류를 마주하게 되는 경우가 종종 있습니다.
이번 글에서는 Linked Server에서 트랜잭션을 사용할 때 발생하는 문제와 그 해결책, 그리고 분산 트랜잭션의 개념과 대응 방법까지 함께 정리해보았습니다.
1. 문제 상황
아래와 같이 C# 애플리케이션에서 명시적으로 트랜잭션을 시작하고, 그 안에서 Linked Server에 대해 INSERT 또는 UPDATE를 실행한 상황이었습니다.
var connection = new SqlConnection(...);
connection.Open();
var tx = connection.BeginTransaction();
try
{
var cmd = new SqlCommand("sp_MyProc", connection, tx);
cmd.CommandType = CommandType.StoredProcedure;
cmd.ExecuteNonQuery();
tx.Commit();
}
catch
{
tx.Rollback();
}
위 코드를 실행하면 다음과 같은 오류가 발생합니다.
파트너 트랜잭션 관리자가 원격/네트워크 트랜잭션에 대한 지원을 할 수 없습니다.
분산 트랜잭션을 시작할 수 없으므로 요청한 작업을 수행할 수 없습니다.
이는 SQL Server에서 Linked Server에 쓰기 작업을 수행할 때 자동으로 분산 트랜잭션(MSDTC) 으로 승격되기 때문에 발생하는 문제입니다.
2. Linked Server와 트랜잭션
SQL Server에서 Linked Server를 이용한 SELECT 문은 문제가 발생하지 않습니다.
하지만 INSERT, UPDATE, DELETE와 같은 쓰기 작업을 수행하면 상황이 달라집니다.
로컬 트랜잭션이 시작된 상태에서 Linked Server에 쓰기를 시도하면, SQL Server는 이를 분산 트랜잭션으로 승격하려 시도합니다.
이 과정에서 대상 서버가 MSDTC(Microsoft Distributed Transaction Coordinator)를 허용하지 않거나, 네트워크나 보안 설정이 맞지 않다면 오류가 발생하게 됩니다.
3. 분산 트랜잭션이란?
분산 트랜잭션이란 여러 개의 데이터베이스나 서버에 걸친 작업을 하나의 트랜잭션처럼 묶어 처리하는 기술입니다.
예를 들어, 로컬 데이터베이스와 Linked Server의 테이블을 동시에 수정해야 하는 경우, 이 두 작업을 하나의 트랜잭션으로 묶기 위해 분산 트랜잭션이 사용됩니다.
SQL Server는 이를 위해 MSDTC 서비스를 활용하며, 2단계 커밋(2PC, Two-Phase Commit) 방식으로 작업의 일관성과 원자성을 보장합니다.
즉, 모든 대상에게 "준비 완료 여부"를 묻고, 전부 OK일 경우에만 COMMIT을 수행하며, 하나라도 실패하면 전체 작업을 ROLLBACK합니다.
분산 트랜잭션이 필요한 경우
- 여러 데이터베이스 또는 시스템에 동시에 데이터를 쓰는 경우
- 하나의 비즈니스 로직에서 Oracle, SQL Server, MQ 등 다양한 자원을 동시에 사용하는 경우
- 복잡한 시스템 통합 환경에서 원자성이 요구되는 경우
하지만 주의할 점
- MSDTC 설정이 양쪽 서버에 모두 필요합니다.
- 방화벽에서 포트 135 및 동적 RPC 포트를 열어야 합니다.
- 보안 정책상 운영 환경에서는 차단된 경우도 많습니다.
- 트랜잭션 처리 속도가 느려지고, 장애 발생 시 디버깅이 어렵습니다.
4. 해결 방법
방법 1: MSDTC 설정을 구성합니다
분산 트랜잭션을 사용하려는 경우, 로컬 SQL Server와 Linked Server 양쪽 모두에서 MSDTC 설정을 활성화해야 합니다.
설정 방법 (Windows 기준):
- Windows 실행창에서 dcomcnfg 실행
- 경로:
- Component Services > 컴퓨터 > 내 컴퓨터 > Distributed Transaction Coordinator > Local DTC (오른쪽 클릭) > 속성
- 보안 탭에서 다음 항목을 활성화합니다.
- Network DTC Access
- Allow Remote Clients
- Allow Inbound
- Allow Outbound
- No Authentication Required (테스트 환경 권장)
- MSDTC 서비스 재시작
net stop msdtc
net start msdtc
- 포트 135 (TCP) 및 MSDTC에서 사용하는 동적 포트를 방화벽에서 허용해야 합니다.
이 설정은 로컬 SQL Server뿐 아니라 Linked Server로 연결되는 모든 원격 서버에도 동일하게 적용해야 합니다.
방법 2: 트랜잭션을 제거합니다
문제의 원인이 트랜잭션인 경우, BeginTransaction() 호출을 생략하면 분산 트랜잭션이 발생하지 않으며, 오류 없이 실행될 수 있습니다.
var connection = new SqlConnection(...);
connection.Open();
var cmd = new SqlCommand("INSERT INTO [LinkedServer].[DB].[dbo].[Table] (...) VALUES (...)", connection);
cmd.ExecuteNonQuery();
이 방식은 간단하지만, 중간에 오류가 발생해도 복구할 수 없으므로 데이터 일관성이 중요하지 않은 단건 처리 작업에만 사용해야 합니다.
방법 3: 구조를 우회합니다
Linked Server를 직접 사용하지 않고, 로컬 서버에 임시 테이블을 만들고 데이터를 저장한 후,
원격 서버에서 Linked Server를 통해 데이터를 가져가는 구조로 우회할 수 있습니다.
- 로컬 서버: TransferQueue와 같은 임시 테이블에 데이터 적재
- 원격 서버: Linked Server를 통해 로컬 데이터를 조회하고 자체적으로 INSERT 실행
- 처리 후 상태 플래그(ProcessedFlag)를 통해 결과 관리
이 방식은 분산 트랜잭션 없이도 시스템 간 데이터를 안전하게 연동할 수 있는 안정적인 패턴입니다.
5. C#에서 트랜잭션을 안정적으로 사용하는 방법
C#에서 트랜잭션을 사용할 때는 SqlTransaction을 명시적으로 시작하고, 반드시 try-catch 구문으로 Rollback() 처리를 포함해야 합니다.
var tx = connection.BeginTransaction();
try
{
var cmd = new SqlCommand("sp_DoSomething", connection, tx);
cmd.CommandType = CommandType.StoredProcedure;
cmd.ExecuteNonQuery();
tx.Commit();
}
catch
{
tx.Rollback();
throw;
}
다만, 이 상태에서 Linked Server를 통해 데이터를 수정하는 경우 SQL Server가 자동으로 분산 트랜잭션으로 승격하게 됩니다.
따라서 분산 트랜잭션을 사용할 수 있는 환경이 아니라면, MSDTC 설정을 마쳤는지 반드시 확인해야 합니다.
6. 마무리
Linked Server는 매우 편리하지만, 트랜잭션과 함께 사용할 경우 분산 트랜잭션 이슈를 반드시 고려해야 합니다.
운영 환경에 따라 MSDTC를 설정하거나, 아예 구조를 바꾸는 방식이 더욱 현실적인 대응이 될 수 있습니다.