분산 트랜잭션

tbJDBC에서 2개 이상의 네트워크 상의 시스템 간의 트랜잭션을 제공하는 Tibero DB 분산 트랜잭션 기능을 설명합니다.

분산 트랜잭션은 보통 전역 트랜잭션이라고도 하는데, 통합으로 관리되는 하나 이상의 트랜잭션 집합을 말합니다. 분산 트랜잭션에 참여하는 트랜잭션은 동일한 데이터베이스에 존재할 수도 있고, 다른 데이터베이스에 존재할 수도 있습니다. 이러한 각각의 트랜잭션을 트랜잭션 브랜치(Transaction Branch)라고 합니다.

분산 트랜잭션은 JDBC 2.0 표준의 확장 API로서 접속 풀링 기능을 기반으로 제공되거나 X/OpenDTP(Distributed Transaction Processing) 규약의 XA 표준으로 제공되기도 합니다.

분산 트랜잭션 특징

  • 분산 트랜잭션은 개별 트랜잭션을 관리하기 위해 외부의 트랜잭션 관리자를 이용합니다. 트랜잭션 관리자는 Java Transaction API 표준을 구현한 소프트웨어 요소로서 여러 벤더(vendor)에서 XA 호환 JTA 모듈을 제공합니다.

  • XA 기능은 애플리케이션 프로그램과는 별도로 구분되며, 대부분 애플리케이션 프로그램 서버(Application Server)와 같은 Middle-tier에서 사용됩니다.

  • 리소스 매니저(Resource Manager)는 데이터나 다른 종류의 리소스를 지칭하는 용어로 본 장에서는 데이터베이스를 가리킵니다.

구성요소

아래는 XA 구성요소별 기능 설명입니다.

XADataSource

XADataSource는 Connection Pool DataSource나 다른 DataSource와 비슷한 개념과 기능을 가집니다. 분산 트랜잭션에서 사용되는 각각의 리소스 매니저(데이터베이스)에는 하나의 XADataSource 객체가 존재하고, 이 XADataSource가 XAConnection을 생성합니다.

XAConnection

XAConnection은 Pooled Connection의 확장이며, 개념이나 기능면에서는 유사합니다. XAConnection은 물리적인 데이터베이스 연결에 대한 임시 핸들이 되고, 하나의 XA Connection은 하나의 데이터베이스 세션에 해당합니다.

XAResource

XAResource는 분산 트랜잭션의 각 트랜잭션 브랜치를 조합하기 위해 트랜잭션 관리자에 의해 사용됩니다. 각각의 XAConnection으로부터 하나의 XAResource 객체를 얻어올 수 있고, 1:1 관계를 가집니다. 따라서 하나의 XAResource 객체는 하나의 데이터베이스 세션에 해당됩니다. 하나의 XAResource 객체는 실행 중인 오직 하나의 트랜잭션 브랜치만 있을 수 있습니다. 그러나 실행 중인 트랜잭션과는 별개로 정지된 트랜잭션도 있을 수 있습니다. 각각의 XAResource 객체는 해당 세션에서 수행되며 시작, 종료, 준비,커밋, 롤백 등의 기능이 있습니다.

XID

각각의 트랜잭션 브랜치를 구분하기 위해 XID(트랜잭션 ID)를 사용하고, 하나의 XID는 트랜잭션 브랜치 ID와 분산 트랜잭션 ID로 구성됩니다.


전역 트랜잭션과 지역 트랜잭션 간의 변환

JDBC 3.0 표준에서는 전역 트랜잭션과 지역 트랜잭션 사이에서 커넥션을 공유할 수 있고, 각각으로 변환 할 수 있습니다. 일반적으로 커넥션은 아래의 세 가지 모드 중에 반드시 하나를 갖습니다.

모드
설명

NO_TXN

  • 현재 커넥션을 사용하는 활성화된 트랜잭션이 없는 모드

LOCAL_TXN

  • auto-commit 모드를 비활성화시킨 상태로 현재 커넥션을 사용하는 활성화된트랜잭션이 있는 모드

  • 커넥션 모드에 따라 prepare(), commit(), rollback(), forget(), end() 메소드를 호출할 수 없고, XAException이 발생

GLOBAL_TXN

  • 현재 커넥션을 사용하는 활성화된 트랜잭션이 있는 모드

  • 커넥션 모드에 따라 commit(), rollback(), setAutoCommit(), setSavepoint() 메소드를 호출할 수 없고, SQLException이 발생

각 커넥션은 실행 상태에 따라 아래와 같이 세 가지 모드 사이에서 자동으로 바뀝니다. 단, 커넥션이 초기화 될 때에는 항상 NO_TXN 모드로 동작합니다.

현재 모드
NO_TXN으로 변환
LOCAL_TXN으로 변환
GLOBAL_TXN으로 변환

NO_TXN

-

auto-commit 모드를 비활성 화시키고 DML 문을 수행 시

XAConnection에서 얻은 XAResource에 end() 메소드를 호출 시

LOCAL_TXN

DDL 문이 수행되거나 com mit() 또는 rollback() 메소드를 호출 시

-

불가능

GLOBAL_TXN

XAConnection에서 얻은 XAResource에 end() 메소드를 호출 시

불가능

-


Tibero XA 패키지

Tibero에서는 XA 표준에 따른 분산 트랜잭션 패키지인 com.tmax.tibero.jdbc.ext 내부에 아래와 같은 클래스를 지원합니다.

  • TbXAConnection

  • TbXADataSource

  • TbXAException

  • TbXAResource

  • TbXid


XA 인터페이스의 구성요소

본 절에서는 JDBC 2.0 표준 패키지에 정의된 XA 인터페이스의 구성요소를 설명합니다.

XADataSource 인터페이스

javax.sql.XADataSource에 정의된 인터페이스는 아래와 같습니다.

public interface XADataSource
{
    XAConnection getXAConnection() throws SQLException;
    XAConnection getXAConnection(String user, String password) throws SQLException;
    ...
}

tbJDBC에서는 com.tmax.tibero.jdbc.ext.TbXADataSource 클래스로 제공됩니니다. 또한 TbConnectionPool DataSource를 확장하여 일반적인 DataSource에서 사용할 수 있는 커넥션의 특성을 사용할 수도 있습니다.

XAConnection 인터페이스

javax.sql.XAConnection에 정의된 인터페이스는 아래와 같습니다.

public interface XAConnection extends PooledConnection
{
  javax.transaction.xa.XAResource getXAResource() throws SQLException;
}

tbJDBC에서는 com.tmax.tibero.jdbc.ext.TbXAConnection 클래스로 제공됩니다.

XAResource 인터페이스

javax.transaction.xa.XAResource에 정의된 인터페이스는 아래와 같습니다.

public interface XAResource
{
    void commit(Xid xid, boolean onePhase) throws XAException; 
    void end(Xid xid, int flags) throws XAException;
    void forget(Xid xid) throws XAException; 
    int prepare(Xid xid) throws XAException; 
    int rollback(Xid xid) throws XAException;
    void start(Xid xid, int flags) throws XAException; 
    boolean isSameRM(XAResource xares) throws XAException;
}

tbJDBC에서는 com.tmax.tibero.jdbc.ext.TbXAResource 클래스로 제공됩니다.

XAResource 인터페이스에서 설정할 수 있는 메소드는 아래와 같습니다.

메소드
설명

Start

XID와 관련된 트랜잭션의 특정 브랜치를 시작하거나 이미 존재하는 트랜잭션을 재시작 또는 변경 사항을 조인

End

XID와 관련된 트랜잭션의 특정 브랜치에 대한 종료 상태(정상 또는 실패)를 알리 거나 이미 존재하는 트랜잭션을 멈춤

Prepare

현재의 트랜잭션 브랜치에서 수행된 변경사항을 적용하기 위해 준비

Commit

현재의 트랜잭션 브랜치의 변화를 반영

Rollback

현재의 트랜잭션 브랜치의 변화를 롤백시킴

Forget

리소스 매니저가 지정한 트랜잭션 브랜치를 무시하도록 함

Recover

현재 준비가 완료되었거나 수행이 끝난 트랜잭션 브랜치의 목록을 반환

isSameRM

두 개의 XA 리소스 객체가 동일한 리소스 매니저에 속해 있는지의 여부를 반환

Start

XID와 관련된 트랜잭션의 특정 브랜치를 시작하거나 이미 존재하는 트랜잭션을 재시작 또는 변경 사항을 조인시키기 위해 사용하는 메소드입니다.

  • 문법

void start(Xid xid, int flags)

  • 파라미터

파라미터
설명

xid

현재 리소스 객체와 관련된 글로벌 트랜잭션의 ID

flags

flags 파라미터는 반드시 다음의 값 중의 하나여야 함

  • XAResource.TMNOFLAGS : 새로운 트랜잭션 브랜치가 시작됨

  • XAResource.TMJOIN : 이미 존재하는 트랜잭션 브랜치에 다음 동작을 조인시킴

  • XAResource.TMRESUME : 정지된 트랜잭션을 다시 시작시킴

  • TbXAResource.TBRTMSERIALIZABLE : Serializable 트랜잭션을 시작시킴

  • TbXAResource.TBRTMREADONLY : Read-only 트랜잭션을 시작시킴

  • TbXAResource.TBRTMREADWRITE : Read/write 트랜잭션을 시작시킴

  • TbXAResource.TBRTRANSLOOSE : Loosely-coupled 트랜잭션을 시작시킴

End

XID와 관련된 트랜잭션의 특정 브랜치에 대한 종료 상태(정상 또는 실패)를 알리거나 이미 존재하는 트랜잭션을 멈추기 위해 사용하는 메소드입니다.

  • 문법

void end(Xid xid, int flags)

  • 파라미터

파라미터
설명

xid

현재 리소스 객체와 관련된 글로벌 트랜잭션의 ID

flags

flags 파라미터는 반드시 다음의 값 중의 하나여야 함

  • XAResource.TMSUCCESS : 현재 트랜잭션 브랜치가 성공했음을 알림

  • XAResource.TMFAIL : 현재 트랜잭션 브랜치가 실패했음을 알림

  • XAResource.TMSUSPEND : 현재 트랜잭션 브랜치를 정지시킴

Prepare

현재의 트랜잭션 브랜치에서 수행된 변경사항을 적용하기 위해 준비하는 과정으로 Two-phase commit 과정의 첫 번째 단계입니다. 만약 분산 트랜잭션 내부에 오직 하나의 트랜잭션만 있는 경우라면 prepare()를 수행할 필요가 없습니다.

  • 문법

int prepare(Xid xid)

  • 파라미터

파라미터
설명

xid

현재 리소스 객체와 관련된 글로벌 트랜잭션의 ID

  • 반환 값 이 메소드는 반드시 아래 값 중의 하나를 반환합니다.

반환값
설명

XAResource.XA_RDONLY

현재의 트랜잭션 브랜치는 read-only 모드로 동작하므로 SELECT 문만 수행할 수 있음

XAResource.XA_OK

현재의 트랜잭션 브랜치에서는 어떠한 수정 작업도 수행할 수 있음

Commit

현재의 트랜잭션 브랜치의 변화를 반영하는 과정으로 Two-phase commit 과정의 두 번째 단계입니다. 단, 모든 트랜잭션 브랜치가 prepare를 완료한 후에 수행됩니다.

  • 문법

void commit(Xid xid, boolean isOnePhase)

  • 파라미터

파라미터
설명

xid

현재 리소스 객체와 관련된 글로벌 트랜잭션의 ID

isOnePhase

isOnePhase 파라미터에 설정된 값에 따라 아래와 같이 동작

  • true : Two-phase commit 과정이 아닌 One-phase commit 과정으로 동작

  • false : Two-phase commit 과정으로 동작

Rollback

현재의 트랜잭션 브랜치의 변화를 롤백시킵니다.

  • 문법

void rollback(Xid xid)

  • 파라미터

파라미터
설명

xid

현재 리소스 객체와 관련된 글로벌 트랜잭션의 ID

Forget

리소스 매니저가 지정한 트랜잭션 브랜치를 무시하도록 합니다.

  • 문법

void forget(Xid xid)

  • 파라미터

파라미터
설명

xid

현재 리소스 객체와 관련된 글로벌 트랜잭션의 ID

Recover

현재 준비가 완료되었거나 수행이 끝난 트랜잭션 브랜치의 목록을 반환합니다.

  • 문법

Xid[] recover(int flag)

  • 파라미터

파라미터
설명

flag

flags 파라미터는 반드시 아래 값 중의 하나여야 함

  • XAResource.TMSTARTSCAN : 현재 준비가 완료된 트랜잭션을 반환

  • XAResource.TMENDSCAN : 현재 수행이 끝난 트랜잭션을 반환

  • XAResource.TMNOFLAGS : 모든 트랜잭션을 반환

isSameRM

두 개의 XA 리소스 객체가 동일한 리소스 매니저에 속해 있는지의 여부를 반환합니다.

  • 문법

boolean isSameRM(XAResource aResource)

  • 파라미터

파라미터
설명

aResource

현재 리소스 객체와 비교할 리소스 객체


XID 인터페이스

트랜잭션 관리자는 XID 객체를 생성하고, 이를 이용하여 분산 트랜잭션의 브랜치를 관리합니다. 각각의 트 랜잭션 브랜치는 개별적으로 XID를 부여받습니다.

XID는 아래의 정보를 포함합니다.

  • 포맷 지시자(4bytes) Java 트랜잭션 관리자를 가리키며, NULL이 될 수 없습니다. 이 정보를 얻기 위한 메소드는 아래와 같습니다.

public int getFormatId()

  • 전역 트랜잭션 지시자(64bytes) 동일한 분산 트랜잭션에 속하는 트랜잭션 브랜치의 경우 모두 같은 값을 가집니다. 이 정보를 얻기 위한 메소드는 아래와 같습니다.

public byte[] getGlobalTransactionId()

  • 브랜치 지시자(64bytes) 이 정보를 얻기 위한 메소드는 아래와 같습니다.

public byte[] getBracheQualifier()

javax.transaction.xa.Xid에 정의된 인터페이스는 아래와 같습니다.

public TbXid(int formatId, byte[] globalId, byte[] branchId) throws XAException

tbJDBC에서는 com.tmax.tibero.jdbc.ext.TbXid 클래스로 제공됩니다.


분산 트랜잭션 예제

아래는 두 개의 트랜잭션 브랜치로 이루어진 Two-phase 분산 트랜잭션 환경을 구현하는 순서입니다.

  1. 트랜잭션 브랜치 #1을 시작합니다.

  2. 트랜잭션 브랜치 #2를 시작합니다.

  3. 트랜잭션 브랜치 #1에서 DML 문장을 수행합니다.

  4. 트랜잭션 브랜치 #2에서 DML 문장을 수행합니다.

  5. 트랜잭션 브랜치 #1의 트랜잭션을 종료합니다.

  6. 트랜잭션 브랜치 #2의 트랜잭션을 종료합니다.

  7. 트랜잭션 브랜치 #1을 준비합니다.

  8. 트랜잭션 브랜치 #2를 준비합니다.

  9. 트랜잭션 브랜치 #1을 커밋합니다.

  10. 트랜잭션 브랜치 #2를 커밋합니다.

아래의 예는 위 순서에 맞게 구현된 Java 프로그램 소스 코드입니다.

import java.sql.*;
import javax.sql.XAConnection;
import javax.transaction.xa.XAResource; 
import com.tmax.tibero.jdbc.ext.*;

public class TwoBranchXA
{
  public static void main(String args[]) throws SQLException
  {
      createBaseTable(); 
      try {
            // Create XADataSource instance 
            TbXADataSource xads1 = new TbXADataSource();
            xads1.setUrl("jdbc:tibero:thin:@localhost:7629:dbsvr"); 
            xads1.setUser("tibero");
            xads1.setPassword("tmax");
                
            TbXADataSource xads2 = new TbXADataSource(); 
            xads2.setUrl("jdbc:tibero:thin:@localhost:8629:dbsvr"); 
            xads2.setUser("wrpark");
            xads2.setPassword("tmax");
                
            // Get the XA connection
            XAConnection xacon1 = xads1.getXAConnection(); 
            XAConnection xacon2 = xads2.getXAConnection();
                
            // Get the physical connection
            Connection conn1 = xacon1.getConnection(); 
            Connection conn2 = xacon2.getConnection();
                
            // Get the XA resource
            XAResource xars1 = xacon1.getXAResource(); 
            XAResource xars2 = xacon2.getXAResource();
                
            // Create the Xid
            Xid xid1 = createXid(1); 
            Xid xid2 = createXid(2);
                
            // Start the resource
            xars1.start(xid1, XAResource.TMNOFLAGS); 
            xars2.start(xid2, XAResource.TMNOFLAGS);
                
            // Execute SQL operations 
            execute1(conn1); 
            execute2(conn2);
                
            // End both the branches 
            xars1.end(xid1, XAResource.TMSUCCESS); 
            xars2.end(xid2, XAResource.TMSUCCESS);
                
            // Prepare the resource manager 
            int pre1 = xars1.prepare(xid1); 
            int pre2 = xars2.prepare(xid2);
                
            // Commit or rollback
            if (pre1 == XAResource.XA_RDONLY || pre1 == XAResource.XA_OK) 
                xars1.commit(xid1, false);
            else
                xars1.rollback(xid1);
                    
            if (pre2 == XAResource.XA_RDONLY || pre2 == XAResource.XA_OK) 
                xars2.commit(xid2, false);
            else
                xars2.rollback(xid2);
                    
            // Clear 
            conn1.close(); 
            conn1 = null; 
            conn2.close(); 
            conn2 = null;
                
            xacon1.close(); 
            xacon1 = null; 
            xacon2.close(); 
            xacon2 = null;
        }
    catch (Exception se) {
            se.printStackTrace();
        }
    }
}

Last updated