개발/spring-batch
MyBatisBatchItemWriter 와 일반 itemWriter
방푸린
2024. 12. 24. 17:38
반응형
환경: springbatch5, java17, mysql
MyBatisBatchItemWriter<GmahjongRanking> writer = new MyBatisBatchItemWriterBuilder<GmahjongRanking>().sqlSessionFactory(casualDb)
.statementId(Constant.GAME_MAPPER + "insertGmahjongDayRank")
.build();
<insert id="insertGmahjongTotalRank" parameterType="com.hangame.batch.casual.application.model.gmahjong.ranking.GmahjongRanking">
INSERT INTO GAME (regdate, memberid, wincnt, defeatcnt, slevel, rating, ranking, avatarid, nickname, oranking)
VALUES (#{registerDate}, #{memberId}, #{winCount}, #{defeatCount}, #{level}, #{rating}, #{ranking}, #{avatarId}, #{nickname}, #{oRanking})
</insert>
insert 문이 이렇게 있을 때 insert문이 1개만 나가는지, 청크 수만큼 나가는지 궁금해졌다.
insert 문이 1개만 나간다는 의미는 values 뒤로 n개 붙은 문이 한번 나가는 것이고
청크 수 만큼 나간다는 것은 insert 문 자체가 n 개 있다는 뜻.
MyBatisBatchItemWriter의 write 함수를 살펴보면 아래와 같다.
while 문으로 청크를 돌아서 sql을 만들고 들고 있다가 한 번에 실행한다.
ExecutorType.BATCH로 설정된 SqlSessionTemplate에서는, update() 메서드 호출 시 쿼리를 바로 실행하지 않고 내부 배치 큐에 저장하고 flushStatements()를 호출하면, 지금까지 배치 큐에 저장된 모든 SQL 문을 한 번에 실행
- 장점:
- 네트워크 요청 최소화: 각 SQL 문을 개별적으로 실행하지 않고, 배치로 묶어서 처리
- 성능 향상: 배치 처리 시 JDBC 드라이버가 여러 쿼리를 내부적으로 최적화
- 주의점:
- 메모리 사용량: 배치 큐에 저장된 쿼리가 많아질 경우 메모리 사용량이 증가
- 트랜잭션 관리: 배치 처리 중 하나의 쿼리가 실패하면, 전체 배치가 롤백
그럼 values 뒤로 쫙 붙여서 한번에 쏘고 싶다면?
우선 mapper를 수정하고
@Bean(INSERT_NINE_RATING_RANKING_WRITER)
@StepScope
public ItemWriter<BadukEnrichedRanking> insertNineRatingRankingWriter() {
return chunk -> {
@SuppressWarnings("unchecked") var items = (List<BadukEnrichedRanking>) chunk.getItems();
var splittedNineRankings = ListUtil.splitList(items, SPLIT_LIST_SIZE);
splittedNineRankings.forEach(badukNineRankingMapper::insertNineRankings);
};
}
MyBatisBatchItemWriter를 안 쓰고 수동으로 itemWriter를 만든 후
chunk를 sublist로 쪼갠 후 foreach 에 연결시킨다.
그러면 1 insert 의 values에 여러 개가 붙고 각 호출이 개별적인 SQL 실행을 하게 된다.
혹시 배치 방식으로 바꾸려면..
return chunk -> {
@SuppressWarnings("unchecked")
var items = (List<BadukEnrichedRanking>) chunk.getItems();
var splittedNineRankings = ListUtil.splitList(items, SPLIT_LIST_SIZE);
// Batch 처리 활성화
try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
var mapper = sqlSession.getMapper(BadukNineRankingMapper.class);
splittedNineRankings.forEach(mapper::insertNineRankings);
// 배치 실행
sqlSession.flushStatements();
sqlSession.commit();
}
};
MyBatisBatchItemWriter(ExecutorType.BATCH):
- ExecutorType.BATCH 모드에서는 하나의 SqlSession을 열고 여러 쿼리를 실행한 후 한 번에 flushStatements()를 호출하여 쿼리들을 모아서 데이터베이스에 전송
- 이 모드는 SQL 세션을 한 번만 열고, 여러 개의 쿼리를 하나의 트랜잭션 내에서 실행. 세션을 닫기 전에 모든 쿼리가 메모리에 쌓이고, flushStatements()를 호출하여 한 번에 실행되므로 성능 면에서 효율적
forEach 방식 (기본 SqlSession):
- 반면에 forEach를 사용하여 각각의 항목을 처리하는 경우, 매번 update 또는 insert가 실행될 때마다 SqlSession을 생성
- 이 방식은 각각의 쿼리가 별도의 세션을 사용하거나, 적어도 별도의 쿼리 실행이 이루어지는 방식. 즉, SQL 세션을 매번 열고 update 또는 insert를 실행한 후 세션을 닫고, 다시 열어서 쿼리를 실행하는 방식
728x90
반응형