스프링 + MyBatis 다중 데이터소스 사용시 트랜잭션 관련 질문드립니다.

1,824 views
Skip to first unread message

자바개발자

unread,
Aug 3, 2017, 6:38:38 AM8/3/17
to Korea Spring User Group Q&A
안녕하세요?

현재 개발 환경은 스프링4.x + 오라클 10g + MyBatis 3.x 입니다.
오라클 서버는 동일하고 계정만 다른 상황에서 다중 접속 데이터소스를 구현했는데
정상적으로 커밋이 이루지지 않는 현상이 발생했습니다.

한건, 두건 입력할 때는 정상적으로 INSERT 되는데
JMeter 를 이용해서 수백건씩 동시에 INSERT 를 하게 되면
A 계정이 사용하는 Notice 테이블에는 정상적으로 데이터가 들어가는데
B 계정이 사용하는 Board 테이블에는 몇 건씩 빠진 데이터가 들어갑니다.
(오류는 없는 상황입니다.)

이 상태에서 몇 분 있다가 다시 Board 테이블을 조회하면
그때서야 빠진 데이터가 들어가 있습니다.
(전부 들어가 있는 경우도 있고 그렇지 않은 경우도 있습니다.)

혹시 저와 같은 경험을 하신 분들 계신가요?
정말 답답해서 이렇게 질문을 드립니다.

아래는 제가 만든 소스입니다.
참고로 cglib 을 사용해서 인터페이스는 만들지 않았습니다.

root-context.xml


<context:component-scan base-package="kr.co.my.sample.*">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

<!-- JNDI mySample -->
<jee:jndi-lookup id="mySampleDataSource" jndi-name="jdbc/mySample" />

<!-- JNDI yourSample -->
<jee:jndi-lookup id="yourSampleDataSource" jndi-name="jdbc/yourSample" />

<bean id="mySampleSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="mySampleDataSource" />
    <property name="configLocation" value="classpath:resources/mapper/zsql-mapper-config.xml" />
    <property name="mapperLocations" value="classpath:resources/mapper/mySample/*_SQL.xml" />
</bean>
<bean id="yourSampleSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="yourSampleDataSource" />
    <property name="configLocation" value="classpath:resources/mapper/zsql-mapper-config.xml" />
    <property name="mapperLocations" value="classpath:resources/mapper/yourSample/*_SQL.xml" />
</bean>
 
<bean id="mySampleSqlSession" class="org.mybatis.spring.SqlSessionTemplate" destroy-method="clearCache">
    <constructor-arg index="0" ref="mySampleSqlSessionFactory"/>
</bean>
<bean id="yourSampleSqlSession" class="org.mybatis.spring.SqlSessionTemplate" destroy-method="clearCache">
    <constructor-arg index="0" ref="yourSampleSqlSessionFactory"/>
</bean>

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="mySampleSqlSession" />
</bean>
<bean id="yourSampleTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="yourSampleSqlSession" />
</bean>

<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
<tx:annotation-driven transaction-manager="yourSampleTransactionManager" proxy-target-class="true"/>


Service 단

@Autowired
private SampleDao sampleDao;

..................

// 저장
@Transactional(readOnly=false, propagation=Propagation.REQUIRED, rollbackFor=Exception.class)
public void insertNotice(SampleVO vo) throws Exception
{       
    // 공지사항 저장
    this.sampleDao.insertNotice(vo);
   
    // Board 테이블 저장       
    this.sampleIDao.insertBoard(vo.getCont_id());
}


Dao 단


@Autowired
@Resource(name="mySampleSqlSession")
private SqlSessionTemplate mySampleSqlSession;

@Autowired
@Resource(name="yourSampleSqlSession")
private SqlSessionTemplate yourSampleSqlSession;

..................

// 공지사항 저장
public void insertNotice(SampleVO vo) throws Exception
{
    this.mySampleSqlSession.insert("sql.my.sample.insertNotice", vo);
}

// Board 저장
public void insertBoard(String contId) throws Exception
{
    this.yourSampleSqlSession.insert("sql.my.sample.insertBoard", contId);
}


정말 답답하고 미치겠습니다.
답변 부탁드립니다.

Jisung Ahn

unread,
Aug 3, 2017, 10:23:49 PM8/3/17
to ks...@googlegroups.com
XA 데이터 소스와 Global Tx Manager를 사용하지 않으시면 
 
Tx Manager는 단 하나의 DS만 관리합니다. 



2017년 8월 3일 오후 7:38, 자바개발자 <kore...@gmail.com>님이 작성:

--
이 메일은 Google 그룹스 'Korea Spring User Group Q&A' 그룹에 가입한 분들에게 전송되는 메시지입니다.
이 그룹에서 탈퇴하고 더 이상 이메일을 받지 않으려면 ksug+unsubscribe@googlegroups.com에 이메일을 보내세요.
https://groups.google.com/group/ksug에서 이 그룹을 방문하세요.
웹에서 이 토론을 보려면 https://groups.google.com/d/msgid/ksug/5148b245-81cc-4838-85c3-3846e588b951%40googlegroups.com을(를) 방문하세요.
더 많은 옵션을 보려면 https://groups.google.com/d/optout을(를) 방문하세요.

Jisung Ahn

unread,
Aug 3, 2017, 10:31:13 PM8/3/17
to ks...@googlegroups.com
XA DS와 Global Tx Manager(Atomikos등)을 사용하지 않으실 거라면 
DS마나 하나의 TX Manager를  생성해서 연결 시켜주셔야 하며 

@Transactional 에 txManager를 지정하여 이 트랜잭션을 관여할 txManager를 지정해주셔야 하는데 

이 이야기는 실질적으로 두 DS로 전달되는 SQL실행이 하나의 트랜잭션으로 묶이지 않는다는 이야기 입니다. 

두개의 DS로 가는 SQL를 하나의 트랜잭션으로 묶는다면 그게 바로 Global TX의 정의죠.. 그러니  관련 설정을 하셔야 하죠...

관련 설정 없이는 
star TX1 
  - insert DS1
  - start TX2
      - insert DS2 
  - commit TX2
commit TX1 

여기에서 startTX2부분을 별도의 메소드로 분리해서 별도의 @Transaction을 붙여서 흉내는 낼수 있지만, commit TX1 에서 오류가 발생했을때 tx2가 롤백 되지는 않을겁니다. 

정말로 글로벌 TX가 필요한 것인지 잘 판단하시고 설정하세요. 

그냥 임시 테이블에 저장해두고, 별도의 배치로 DS2로 전달하면 끝인 업무일수도 있습니다. 


참고: Bean이 자기 자신의 메소드를 호출하면 AOP가   적용되지 않기 때문에 TX2가 실행되지 않을수 있습니다. 






2017년 8월 4일 오전 11:23, Jisung Ahn <nar...@gmail.com>님이 작성:

Jisung Ahn

unread,
Aug 3, 2017, 10:36:19 PM8/3/17
to ks...@googlegroups.com
그러니까 다시 말하면 @Transactional 에서 별도로 지정하지 않으면 "transactionManager" 라는 이름의 기본 Tx Manager 빈을 사용하게 되어 있습니다. 

복수의 DS를 XA 없이 사용하시면 각 @Transactional 이 붙은 메소드에 주의 깊게 txManager="transactionManager" 또는 txManager="yourSampleTransactionManager" 을 구분해서 붙여 주셔야 합니다 

그리고 그 트랜잭션 메니저와 관련된 SQL만 실행해야 하고요  



2017년 8월 4일 오전 11:31, Jisung Ahn <nar...@gmail.com>님이 작성:
Message has been deleted

자바개발자

unread,
Aug 4, 2017, 2:33:21 AM8/4/17
to Korea Spring User Group Q&A
narusas님 답변 감사합니다.
저도 JOTM을 이용해서 분산 트랜잭션을 구성했다가 동일한 문제가 발생해서 원인을 파악하고자
이것 저것 해보는 것이었습니다.

자답

1. 트랜잭션을 제거하면 모든 데이터가 정상적으로 insert 되는 것을 확인했습니다.

2.데이터소스가 하나일 때는 상관없는데
데이터소스가 두개 이상일 때 어노테이션 방식의 트랜잭션은 데이터 무결성 문제가 있음을 확인했습니다.
한건, 두건씩 입력하는 것은 크게 문제가 안되는데
JMeter 를 이용한 동시다발적으로 수백건씩 입력을 하게되면 데이터 무결성 문제가 발생했습니다.
그래서 데이터소스가 두개 이상일 때는 AOP를 이용한 선언적 방식으로 트랜잭션을 설정해서 해결했습니다.

Jisung Ahn

unread,
Aug 4, 2017, 3:08:13 AM8/4/17
to ks...@googlegroups.com
트랜잭션을 제거하시면  오류시 롤백이 않될텐데요..

Global TX를 하실때는 Datasource도 XA로 등록 되어야 하는데요. 

Driver를 oracle.jdbc.xa.client.OracleXADataSource   같이 XA 로 사용하셔야합니다 




2017년 8월 4일 오후 3:33, 자바개발자 <kore...@gmail.com>님이 작성:

--
이 메일은 Google 그룹스 'Korea Spring User Group Q&A' 그룹에 가입한 분들에게 전송되는 메시지입니다.
이 그룹에서 탈퇴하고 더 이상 이메일을 받지 않으려면 ksug+unsubscribe@googlegroups.com에 이메일을 보내세요.
https://groups.google.com/group/ksug에서 이 그룹을 방문하세요.

자바개발자

unread,
Aug 4, 2017, 4:59:49 AM8/4/17
to Korea Spring User Group Q&A
narusas님 답변 감사합니다.

1번은 제가 트랜잭션을 제거하고 테스트했던 상황입니다.
트랜잭션은 AOP를 이용한 선언적 방식으로 적용했습니다.

늘 행복하세요..^^
Reply all
Reply to author
Forward
0 new messages