아래 내용은 트랜잭션 2탄이다. 

다만 코드로 나타내보고 싶은에 일단 글로 남겨본다 구현의 문제는 좀 더 나중에 고민 


관련된 책은 계속 읽어 봐야 겠다 

지금 총 5권이 있는데 난이도로는 


초급

Head First SQL 


중급 이상

SQL Server(개발편)

SQL 전문가 가이드 

대용량 데이터베이스 솔루션 Vol1(엔코아)

웹 프로그래머를 위한 데이터베이스를 지탱하는 기술 


이렇게 다 봐야 할 것 같다 


지금 정리할 내용은 중첩 트랜잭션 즉 트랜잭션이 이중 For문 이상 처럼 중첩되서 시작될 때 과연 트랜잭션 개수나 명시적 혹은 묵시적 이나 Auto Commit이면 어떻게 되는 것인가에 대한 고민이다. 


일단 몇가지 주의?? 사항이 있다 


1. 트랜잭션 선언 방법 

명시적

묵시적(오라클 기본)

Auto(SQL Server 기본)


2. 트랜잭션의 관리주체

로컬

분산(or Global)


3 중첩 영역 (트랜잭션 허용범위?)

Code(.Net)

Stored Procedure(DBMS)


총 3가지 요소로 나누고 사용하는 방법을 생각해보자 

현재 내가 다루는 부분에서는 어플리케이션 계층 즉 Biz 영역을 가지고 있다 그래서 기본은 Biz에서 트랜잭션을 처리하는 것이 기본이다. 


이걸 가지고 가설(또는 주장)을 세우자 

가설 : app에 Biz영역이 있으면 어플리케이션 계층에서 트랜잭션 처리를 한다!

라는 가설을 세우고 그것에 대한 타당성 검증을 하자 


그러면 고려되는 부분이 Code에서 트랜잭션 처리를 해야 한다는 것이다. 이것에 대한 기술은 .Net의 TrasactionScope를 사용하거나 IDbTransaction을 사용해서 트랜잭션 단위를 묶어서 처리할 수 있다 

기술적으로는 문제가 없다 


그렇다면 Biz영역을 왜 만들었나에 대한 질문을 할 수 있는데 나의 답은

DAO(Database Access Object)를 통해 DB를 변경하기 전에 로직에 따라 데이터를 수정하고 원하는 타입(예를 들면 Table Value Parameter)와 같이 변경해서 사용하거나 필요에 의한 가공을 하고 여러 Table(Entity)애 접근 할 수 있으니 그건 업무에 따라 달라지는 부분을 묶어서 업무 로직을 트랜잭션으로 처리하기 위해서 라고 길게 말하고 DB에 반영하기 이전에 데이터나 모델의 수정을 한 곳에서 일괄적인 작업단위로 분리하기 위해서 라고 간단히 말 할 수 있다 


그러면 SP에서 트랜잭션이 걸리면 어떻게 할거냐?? 라는 반론이 생길 수 있는데 

이때 중첩 트랜잭션이 생기고 그것에 따라 트랜잭션 개수가 복수로 생길 수 있다 

즉 Code에서 Begin Tran을 하면 하나의 트랜잭션이지만 SP에서도 Begin Tran을 하면 2개이상의 트랜잭션이 생긴다는 것이다. 

이렇게 되면 2번의 Commit이나 Rollback에 의한 초기화가 되야 짝이 맞아진다 

Scope가 중요해지는 부분이 생기는데 이렇게 되면 Code나 SP에서 둘 다 관리포인트가 생기니 일련의 과정을 모두 확인해봐야 하는 번거로움이 생긴다 


이때 유효하게 사용할 수 있는 부분이 TM이다 즉 트랜잭션 매니저가 있어서 Commit과 Rollback을 제어해 줄 수 있고 관리를 위임할 수 있으면 개발자 입장에서는 편해진다. 

그러니 Code에서 트랜잭션 처리를 할거면 TM역할을 하는 TransactionScope나 IDbTransaction을 이용해서 Work 단위를 만들면 중첩된 부분을 커버해주지는 않더라도 한 곳에서 트랜잭션을 시작하고 끝낼 수 있다 


한 곳에서 트랜잭션을 시작하고 끝낸다는 것에 대한 고찰 

사실 답은 억지로 끌어낸 감이 없잖아 있지만 중첩트랜잭션이 필요한가?? 에 대한 경우가 일관되게 적용하기가 아직 경험이 없다 그렇게 되니 일단 한곳에서 집중하고 필요한 경우 SP에서 중첩되게 사용할 수 있다고 말하고 싶다 

트랜잭션은 성능과 연관될 수 있다 트랜잭션이 길어지면 선행 처리가 길어진다는 것이고 그러면 후행처리는 그만 큼 느려지니 타임아웃에 의한 실패나 시스템 전체에 Sleep이 걸린 것 처럼 처리속도에 영향을 줄 수 있다 

그렇기 때문에 트랜잭션은 짧게 가져가되 성능감소는 최소화 하고 처리하는 Data는 무결성을 지켜주는 그런 식의 보완을 개발자가 제어해줘야 한다고 생각한다. 


간단히 의사 코드로 표현 해 보면 

TransactionScope Start 

{

Begin Tran()                // Transation Count +1 증가


Excute SP(insert Model)


Commit()


Excute SP(insert Model)

{

// 아래 내용은 SP의 내용

// insert into Table (Columns) values (Model)

Insert(Model) 

}


위와 같이 하면 Transaction Count가 1인 것이고 

아래와 같이 SP에 트랜잭션을 다시 걸면


TransactionScope Start 

{

Begin Tran()                // Transation Count +1 증가


Excute SP(insert Model)


Commit()


Excute SP(insert Model)

{

// 아래 내용은 SP의 내용

// Begin Tran            // Transaction Count +1 증가 

//     if(@@Transaction Count = 2)

//        insert into Table (Columns) values (Model)

// Commit Tran

Insert(Model) 

}


위 구문은 트랜잭션 카운트가 2가 되어서 Table에 Insert가 된다 
물론 위의 코드가 정확하다고 할 수 없다 Test Code를 작성할 때 Transaction Count를 사용할 때 생각지 못한 error 가 발생 했었기 때문에 
(@@는 Count를 반환하는 함수정도로 생각하면 될 것 같다 : http://forums.codeguru.com/showthread.php?493155-What-does-mean-in-MS-Sql-Server)
Test Case를 모두 Code로 해보려는 것은 실패했다 

일단 Code에서 시작한 Begin Tran이 SP의 Script 에서 @@TranCount를 만났을 때 호환이 안되는 것 처럼 보인다. 위의 SP가 실행되려면 상단에 Rollback Tran을 넣어주면 0으로 초기화가 되어서 Transaction Count가 2가 되지 않아 위의 SP는 Insert가 되지 않는다 (Rollback은.. 사실 강제로 SP에서 TranCount를 세기 위한 꼼수 같은 것이다.)
아마도 Code와 SP 모두 Begin Tran이 발생했을 때 그것을 받아들이는 DBMS에서 그 차이를 인지하지 못해서 문제가 발생될 수 있다고 해석된다. 

2개 이상의 중첩 트랜잭션의 필요여부 
꼭 그런것은 아니지만 로직을 짤 때 이중For문 이상 삼중 사중의 For은 피해야 하는 권장사항을 들은 적이 있다 즉 필요하면 써먹어야 겠지만 중첩 구조가 복잡한 것을 일부러 할 필요는 없다 그렇다면 비슷한 형태의 트랜잭션도 굳이 로직 사이사이에 필수로 라도 부분 Commit으로 처리해야하는 경우가 있는 것이 아니라면 Data의 수정을 All or Nothing 으로 하면 될 것으로 보인다 
그러면 부분적으로 SP에서 Begin Tran을 하는 경우는 부분 Commit이 있는 경우에 한정지어서 생각할 수 있다 만약 처리해야하는 부분이 많은데 부분 실패가 났을 때 완전 Rollback이 아니라 부분적으로 반영하고 재시도에서는 기존 Commit을 거치지 않는 그런 식으로 Retry인 경우에 이점이 있는 로직에서 처리할 수 있다 아직 그런 Case를 해본적이 없어서 가정일 뿐이다. 

위의 상황은 로컬 트랜잭션에 대한 과정이라는 것
분산트랜잭션을 경험해 보지 못해서인지 막연한 점이 있지만 하나 기준을 잡자면 어느 Resource 대상인지와 물리적인 대상이 변경되는지 2가지로 압축하고자 한다 위의 Insert는 특정 DB의 Table이다 그러니 하나의 DB만 수정하므로 분산된 Resource를 건드리는 것이 아니라 그리고 물리적으로 여러개의 DB를 건드리거나 통신적으로 (예를 들면 TCP / HTTP 를 사용해서 물리적인 Server가 달리지거나) 달라지는 것이 없고 하나의 Connection으로 단일 DB를 처리하므로 이런 case에는 단일 지역 트랜잭션 즉 로컬 트랜잭션으로 본다 
분산 트랜잭션을 구현하게 되면 여러개의 다른 Resource DB나 물리적으로 다른 Server 혹은 App에서 통신 구간이 변경되어 각각의 Data가 제대로 요청과 응답이 있었는지의 확인 과정을 거쳐야 해서 새로운 개념의 Scope의 Begin Tran이나 Commit or Rollback으로 묶어서 처리하는 부분이 있는 경우로 고려해 볼 수 있다. 

관련 내용 링크
https://ko.wikipedia.org/wiki/%EB%B6%84%EC%82%B0_%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98
https://technet.microsoft.com/ko-kr/library/ms191440(v=sql.105).aspx
http://krids.tistory.com/96

즉 분산된 트랜잭션을 쓰는 경우는 하나의 작업에서 다른 Server 접근 다른 DB 접근과 같이 상태가 변하는 경우로 간단히 볼 수 있다 
나중에 2PC(begin -> end -> prepare  -> commit)도 검증해보도록 하자 글로벌 트랜잭션이 이루어지는 과정이라고 한다 

MS Tech 쪽 설명을 보다보면 아래와 같은 내용이 나오는데

분산 트랜잭션은 리소스 관리자라고 하는 둘 이상의 서버에 분산됩니다. 트랜잭션 관리는 트랜잭션 관리자라고 하는 서버 구성 요소에 의해 리소스 관리자 간에 조정되어야 합니다. MS DTC(SQL Server 데이터베이스 엔진은 Microsoft Distributed Transaction Coordinator) 등의 트랜잭션 관리자 또는 분산 트랜잭션 처리용 Open Group XA 사양을 지원하는 기타 트랜잭션 관리자에 의해 조정되는 분산 트랜잭션에서 리소스 관리자 역할을 합니다. 자세한 내용은 MS DTC 설명서를 참조하십시오.  

트랜잭션 관리자라는 용어가 나오는데 아마도 위의 TM이라고 한 TransactionScope나 IDbTransaction 과 연관되어 있을 것으로 보인다
위키 백과에서는 

일반적으로 시스템은 트랜잭션 리소스(transaction resource)의 역할을 하고, 트랜잭션 매니저(transaction manager)는 이러한 리소스에 관련된 모든 동작에 대해 트랜잭션의 생성 및 관리를 담당한다

이 시스템이란 말은 데이터베이스로 대입해도 될 듯 하다 그렇다면 데이터베이스라는 리소스에 접근하는 부분에서 데이터베이스 엔진이라거나 MS DTC 이런 녀석들이 트랜잭션을 관리할 수 있고 Code에서는 위의 TransactionScope나 IDbTransaction 를 통해 관리자의 역할을 수행한다고 보인다. 

왜 마지막에 트랜잭션 매니저가 나왔냐 하면 위의 3가지 트랜잭션 선언방법 중 

명시적

묵시적(오라클 기본)

Auto(SQL Server 기본)

Auto Commit 이라는 것이 있다 이 녀석이 나에게 혼란을 주었다 


하나의 의사코드로 예를 들자면 

Begin Tran 

insert A

insert B

insert C

Commit Tran


insert A (commit)

insert B (commit)

insert C (commit)


위 2개의 방식에서 차이는 명시적 트랜잭션과 Auto Commit이다. 
아래의 경우에서 Auto Commit이 이루어 질때는 트랜잭션 개수가 3개인가?? 의문이 생겨버렸다. 
물론 하나의 작업으로 생각하면 3번의 트랜잭션이 있었다고 볼 수 있고 각각 한줄을 지날 때마다 영구적인 data의 변화를 줬기 때문에 트랜잭션의 영구성 원칙에 따라 트랜잭션 3번으로 보인다. 
그러면 트랜잭션으로 묶을 때와 아닐 때에 대해 트랜잭션에 대해 혼란이 가중된다. 뭐 일일이 트랜잭션 개수를 고려하는 것이 중요한 것은 아니겠지만 auto commit이 일어나는 것에 대해 어떻게 대처할 것인가에 대해 고려해줘야 한다.
이 때 정확한 Count는 알 수 없었지만 MS DTC가 되었든 DBMS가 되었든 단순 insert가 여러번 발생하면 그 하나에도 트랜잭션이 Begin ~ Commit이 일어났다고 봐야 할 것 같다 다만 사용자가 제어할 수 있는 트랜잭션이 아니기 때문에 Count를 확인하기 전에 트랜잭션이 끝나버렸다 
SQL Profiler로 확인 해 본 결과 TM이 아닌 각각의 BEGIN TRAN ~ COMMIT TRAN으로 잡혔기 때문에 트랜잭션 3개로 보는 게 옳은 것으로 보인다. 
TM이 관여한 트랜잭션도 아닌 것으로 로그가 남았다 명시적으로 BEGIN TRAN ~ COMMIT TRAN 을 사용하지 않았는데 해당 구문을 추적하는 이벤트에 남은 것으로 보아 명시적으로 선언하지 않으면 DBMS에서 DML 구문 하나가 끝날 때 Auto COMMIT TRAN을 만들어 주는 것으로 예상된다 

어쨌든 그렇다면 트랜잭션 개수는 카운팅되어야 하는 것으로 보인다. 

자 마지막 결론을 내린다. 
위의 일련의 과정은 결국 트랜잭션 처리는 어플리케이션에서 Biz 계층이 있다는 경우면 단순하게 DAO 영역에서 데이터베이스에 접근해서 CRUD를 하는 것이 아니라 필요한 로직처리가 있는 것이고 그 로직에서 Code에서 트랜잭션을 처리하기 때문에 기술적으로 TransactionScope나 IDbTransaction 을 통한 트랜잭션 매니저를 이용하게 되고 그 트랜잭션 매니저에서는 일단 하나의 트랜잭션 Count를 만들고 내부에서 호출한 SP에 BEGIN TRAN이 있으면 또 새로운 트랜잭션 Count가 생기는 것이다. 다만 이렇게 사용해야 하는 경우가 있다면 중첩된 For문을 쓰는 것과 같이 중첩된 트랜잭션이기 때문에 가급적 부분처리를 해야하는 것이 아니면 Code에서 일관되게 트랜잭션의 원자성과 격리성을 지켜주어서 All or Nothing 을 지켜준다
그러니 Biz를 어떻게 나누냐에 따라 트랜잭션을 고려한 개발을 하는 것이라고 할 수 있다는 것이다 
그리고 이것은 필수 사항은 아닌 것이다 왜냐하면 Code에서 하지 않으면 SP에서 BEGIN TRAN ~ COMMIT TRAN or ROLLBACK TRAN을 하는 경우이면 SP에게 트랜잭션 처리를 위임하게 되므로 DB에서 SP 내부처리로 할지 Code에서 Biz로 처리하는 것을 구분할 줄 알고 Biz에서 처리할 때는 일관된 DB Connection이 유지되어서 DB로의 접근을 가장 단순하게 해야 하는 이유가 생기는 것이다. 헷갈리지 말자 

만약 Biz 가 없고 정말 단순히 DB의 data를 변경하는 경우라면 SP에서 트랜잭션을 시작해서 BEGIN TRAN ~ COMMIT TRAN or ROLLBACK TRAN 로 처리 할 수도 있다는 것이다. 
그리고 트랜잭션 매니저의 내부처리에 대해서 궁금할 수도 있지만 단순히 각각의 SP를 큐에 모아놨다가 Commit이 일어날 때 큐에 모인 SP 전체를 실행해서 비우는 것처럼 은유적으로만 생각하고 깊게 생각하지 말자 트랜잭션 매니저는 단지 이용하는 수단일 뿐이다

트랜잭션 카운트 계산 법

Begin Tran 

insert A

insert B

insert C

Commit Tran

= 1개

insert A (commit)

insert B (commit)

insert C (commit)

= 3개 

TransactionScope.BeginTran
Excute SP
TransactionScope.Commit 
= 1개

TransactionScope.BeginTran
Excute SP A
SP.BeginTran
Insert data a
SP.Commit
TransactionScope.Commit 
= 2개

TransactionScope.BeginTran
Excute SP A
SP.BeginTran
Insert data a
SP.Commit
SP.BeginTran
Insert data b
SP.Commit
Excute SP A
SP.BeginTran
Insert data c
SP.Commit
Excute SP A
SP.BeginTran
Insert data d
SP.Commit
TransactionScope.Commit 
= 1 + (1 * 2) + (1 * 1) + (1 * 1)  = 5개 


+ Recent posts