사붐이개발일기
[MyBatis] Batch Insert - 대용량 데이터 insert 본문
내용
1. SqlSession 의 ExecutorType.BATCH
ExecutorType.BATCH 를 사용하면 바로 DB 로 쿼리를 날리지 않고 쿼리를 미리 쌓아뒀다가 commit 시 connection에 모든 쿼리를 넘겨준다.
2. flushStatements
MyBatis에서는 flushStatements라는 설정 옵션을 제공합니다. 이 옵션을 활용하면 일정량의 SQL 문을 배치 처리하고 커밋하기 전에 임시로 버퍼에 저장하는 방식으로 대용량 데이터 처리 작업의 성능을 개선할 수 있습니다. 대용량 데이터 작업을 수행하는 동안 많은 SQL문을 생성하고 실행할 수 있습니다. 이때 flushStatement를 사용하면, 일정한 개수나 시간 간격으로 SQL 문을 배치로 처리하고 커밋하므로 다음과 같은 장점이 있습니다.
- 메모리 사용 최적화: 많은 SQL 문을 한 번에 메모리에 보관하지 않고 일부분씩 처리하므로 메모리 부하가 줄어듭니다.
- 작업 속도 향상: 일정한 간격마다 SQL문을 배치로 처리하기 때문에 작업이 빨라집니다.
- 트랜잭션 관리: 일정한 간격마다 커밋하므로 큰 트랜잭션 범위에서 발생하는 장애의 영향을 줄일 수 있습니다.
예시
1. 기본 insert
<!-- 데이터를 한 건씩 Insert 하는 쿼리 -->
<insert id="insertItem" parameterType="com.example.batchjob.dto.ItemDto">
INSERT INTO item_list
VALUES ( #{itemCode}, #{name}, #{kindCode}, #{price})
</insert>
INSERT INTO item_list
VALUES ( '0', '0', '0', 0)
{executed in 6 msec}
INSERT INTO item_list
VALUES ( '1', '1', '1', 1)
{executed in 5 msec}
INSERT INTO item_list
VALUES ( '2', '2', '2', 2)
{executed in 4 msec}
INSERT INTO item_list
VALUES ( '3', '3', '3', 3)
{executed in 4 msec}
INSERT INTO item_list
VALUES ( '4', '4', '4', 4)
{executed in 4 msec}2. Mybatis <foreach> 태그사용
<!-- foreach를 이용한 다건의 데이터 Insert -->
<insert id="insertItemList" parameterType="com.example.batchjob.dto.ItemDto">
INSERT INTO item_list
VALUES
<foreach collection="list" item="item" open="(" close=")" separator="), (">
#{item.itemCode}, #{item.name}, #{item.kindCode}, #{item.price}
</foreach>
</insert>하나의 insert문으로 다 건의 데이터가 입력된다.
INSERT INTO item_list
VALUES
( '0', '0', '0', 0 )
, ( '1', '1', '1', 1)
, ( '2', '2', '2', 2)
, ( '3', '3', '3', 3)
, ( '4', '4', '4', 4)
{executed in 13 msec}3. SqlSession ExcuteType.BATCH 사용
// mapper.XML
<!-- 데이터를 한 건씩 Insert 하는 쿼리 -->
<insert id="insertItem" parameterType="com.example.batchjob.dto.ItemDto">
INSERT INTO item_list
VALUES ( #{itemCode}, #{name}, #{kindCode}, #{price})
</insert>@Test
@DisplayName("SqlSession Execute.BATCH 를 이용한 배치처리")
void BulkInsertUsingSqlSession() {
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
ItemMapper mapper = sqlSession.getMapper(ItemMapper.class);
for (ItemDto itemDto : inputDataList) {
retValue = mapper.insertItem(itemDto); // 리턴값으로 row와 상관없는 값이 나옴
}
sqlSession.flushStatements();
sqlSession.Commit()
}batching 5 statements:
1: INSERT INTO item_list#
VALUES ( '0', '0', '0', 0)
2: INSERT INTO item_list
VALUES ( '1', '1', '1', 1)
3: INSERT INTO item_list
VALUES ( '2', '2', '2', 2)
4: INSERT INTO item_list
VALUES ( '3', '3', '3', 3)
5: INSERT INTO item_list
VALUES ( '4', '4', '4', 4)
{executed in 13 msec}4. 각 방식의 성능비교
sqlSession batch < mybatis <forEach> < 기본 insert 순으로 속도가 느려짐
내가 사용한 방법 !
- 환경
- java 8
- mybatis
- postgreSQL
- 순서
- sqlSession ExecutorType.BATCH 선언
- batchMapper 선언
- batch insert할 임시테이블 생성 (temporary table)
- batch insert 하기 전 가맹점 마다 임시테이블 데이터 모두 삭제 (truncate)
- batch insert
- batch insert를 한꺼번에 저장시 속도가 떨어지므로 1000건씩 끊어 넣기 (1000건이 퍼포먼스 제일 높게 나옴)
- 반드시 finally 에 sqlSession을 닫는 기능을 넣어야함. 오류가 나더라도 finally는 실행됌.
public class service {
private final SqlSessionFactory sqlSessionFactory;
private final PlatformTransactionManager transactionManager;
private final SmartroPayRepo smartroPayRepo;
private final int BATCH_SIZE = 1000; // batch insert size 1000건씩 끊어 넣기
public Boolean insertAdjmData(AdjmResVO adjmResVO, String payDate) {
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
SmartroPayRepo batchMapper = sqlSession.getMapper(SmartroPayRepo.class);
/* Transaction Start */
// TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
/* 임시테이블 생성 */
// smartroPayRepo.createTempMpayAdjm();
batchMapper.createTempMpayAdjm(); // create temporary table
sqlSession.flushStatements(); // commit
} catch (Exception e) {
log.info("already createTempMpayAdjm On insertAdjmData()");
}
try {
/* 임시테이블에 있는 데이터 모두 삭제 */
// sqlSession.update("com.bit.bizportal.admin.modules.smartroPay.SmartroPayRepo.truncateTempMpayAdjm");
batchMapper.truncateTempMpayAdjm(); // truncate temporary table
int batchInsertCount = 0;
List<AdjmDataVO> adjmList = batchMapper.selectAdjmList();
for (AdjmDataVO adjmDataVO : adjmList) {
// sqlSession.insert("com.bit.bizportal.admin.modules.smartroPay.SmartroPayRepo.insertTempPadjData", adjmDataVO);
batchMapper.insertTempPadjData(adjmDataVO); // batch insert
/* BATCH_SIZE 만큼 끊어 넣기 (1000건) */
batchInsertCount++;
if (batchInsertCount % BATCH_SIZE == 0) {
sqlSession.flushStatements(); // commit
batchInsertCount = 0;
}
}
// sqlSession.insert("com.bit.bizportal.admin.modules.smartroPay.SmartroPayRepo.insertPadjData");
batchMapper.insertPadjData(); // batch insert
sqlSession.flushStatements(); // commit
/* Transaction Commit */
// transactionManager.commit(status);
} catch {
/* Transaction Rollback */
// transactionManager.rollback(status);
} finally {
sqlSession.flushStatements();
sqlSession.close(); // SqlSession 종료
sqlSession.clearCache(); // SqlSession 리소스 반납
}
return true;
}
}
mapper.xml
insert, update, select 등 기존 방식과 동일하게 사용하면 됌
// mapper.xml
<!-- create temporary table, 임시테이블 생성 -->
<update id="createTempMpayAdjm">
CREATE temp table tmp_mpay_adjm (
padj_id int,
padj_mpay_id int,
padj_sadj_id int,
padj_reg_dttm timestamp,
padj_updt_dttm timestamp,
tid text,
otid text
);
</update>
<!-- truncate table, 테이블 내 모든 데이터 삭제 -->
<update id="truncateTempMpayAdjm">
TRUNCATE TABLE tmp_mpay_adjm;
</update>
Reference
1. mybatis 대량 데이터 INSERT - sqlSessionFactory 이용!
https://jy-note.tistory.com/26
2. [bulk insert/update] mybatis 배치작업과 리턴값(returnValue)
https://velog.io/@aszxvcb/Bulk-InsertUpdate-%EB%B0%B0%EC%B9%98%EC%9E%91%EC%97%85%EC%97%90%EC%84%9C%EC%9D%98-%EB%8D%B0%EC%9D%B4%ED%84%B0%EC%B2%98%EB%A6%AC-%EB%A6%AC%ED%84%B4%EA%B0%92returnValue
3. myBatis에서 대량의 데이터를 넣을때 bulk insert를 사용하자
https://javairus.tistory.com/18
4. [Oracle] Spring, Mybatis에서 대량 데이터 INSERT 수행. multirow, 다건 삽입 수행하기
https://infjin.tistory.com/192
'Spring > Mybatis' 카테고리의 다른 글
| [MyBatis] Update Join - table끼리 join해서 update 하기 (0) | 2024.05.02 |
|---|---|
| [MyBatis] Insert Join - table끼리 join해서 insert하기 (2) | 2024.05.02 |