본문 바로가기

Spring

Mybatis에서 대량의 데이터를 넣을 때 bulk insert를 사용하자

개발을 하다보면 API 호출 시 1000건이상의 데이터를 삽입해야하는 상황이 있습니다.

1000건의 데이터를 insert하는데 걸리는 시간은 약 5초였으며 시간을 줄일 수 있는 방안을 생각해봤는데요. 

mybatis에서 대량의 데이터를 한번에 삽입할 수 있도록 insert foreach문(DBMS마다 지원하는 쿼리가 다름)을 지원하는 것을 알게 되었습니다.

 

수정 전 SQL

	<insert id="add" parameterType="com.dto.Receiver$Info">
		insert into tb_receiver(receiver_key, token, user_id, amount, is_received, receiver_date) 
		values(#{receiver_key}, #{token}, #{user_id}, #{amount}, #{is_received}, #{receiver_date})
	</insert>

 

수정 후 SQL

	
	<insert id="addList" parameterType="com.dto.Receiver$Info">
		insert into tb_receiver(receiver_key, token, user_id, amount, is_received, receiver_date)
		values
		<foreach collection="list" index="index" item="receiver" separator=",">
		(
			#{receiver.receiver_key},
			#{receiver.token},
			#{receiver.user_id},
			#{receiver.amount},		
			#{receiver.is_received},		
			#{receiver.receiver_date}		
		)		
		</foreach>
	</insert>	

 

기존방식에서 bulk insert 방식으로 변경 시 성능향상이 얼마만큼 되는지 JUnit 테스트를 진행해봤습니다.

 

테스트코드


	@Autowired
	ReceiverDao receiverDao;
	
	@Autowired
	SpreadService spreadService;
	
	private final int peopleCnt = 7000;
	@Test
	public void 벌크_인석트_테스트() {

		long amount = 1000000;
		
		long[] amounts = spreadService.distribute(amount, peopleCnt);
		List<Receiver.Info> listReceiver = new ArrayList<Receiver.Info>();
				
		//뿌리기 받을사람 정보 추가
		for(int i=0; i<amounts.length; i++) {
			Receiver.Info receiver = new Receiver.Info();
			receiver.setToken("AsD");			
			receiver.setAmount(amounts[i]);
			receiver.setReceiver_key(SeedUtil.createUUID());
			receiver.setIs_received("0");
			receiver.setReceiver_date(new Date());
			listReceiver.add(receiver);
		}
		
		int result = receiverDao.addList(listReceiver);
				
		assertThat(result, is(peopleCnt));
	}
	
	@Test
	public void 단일_인석트_테스트() {

		long amount = 1000000;
		int sum = 0, result = 0;
		long[] amounts = spreadService.distribute(amount, peopleCnt);
				
		//뿌리기 받을사람 정보 추가
		for(int i=0; i<amounts.length; i++) {
			Receiver.Info receiver = new Receiver.Info();
			receiver.setToken("Asq");			
			receiver.setAmount(amounts[i]);
			receiver.setReceiver_key(SeedUtil.createUUID());
			receiver.setIs_received("0");
			receiver.setReceiver_date(new Date());
			result = receiverDao.add(receiver);
			sum += result;
		}
						
		assertThat(sum, is(peopleCnt));
	}

 

테스트는 다음과 같이 진행했습니다.

삽입하려는 데이터의 건수는 1,000건부터 8,000건까지 1,000단위로 테스트 진행

 

1,000건 

 

2,000건

건수 single insert bulk insert
1,000건 약 5초 약 0.7초
2,000건 약 7초 약 1초
3,000건 약 9초 약 1초
4,000건 약 12초 약 1.5초
5,000건 약 14초 약 1.5초
6,000건 약 17초 약 2초
7,000건 약 20초 약 2.2초
8,000건 약 25초 약 2.2초

 

* 테스트결과는 컴퓨터 사양에 따라 달라질 수 있습니다.

* bulk insert를 이용하여 10,000건 이상 삽입 시 성능이 좋지 않을경우에는 일정 건수로 분할하여 삽입하거나 엑셀파일로 export한 후 DBMS에서 엑셀파일을 읽는 방식으로 진행하는 방안도 존재합니다.