[Java] 대용량 데이터 전산 처리 방안(유량제어) - 처리 size 만큼의 분할 처리

Hyo Kyun Lee·2024년 10월 31일
0

Java

목록 보기
57/61

1. 개요

성능테스트를 진행하면서 대용량 데이터를 안전하게 처리할 수 있는 방법, 유량제어를 할 수 있는 방법을 계속 고민해보았다.

이 중 원리가 가장 기본적이고 유용하게 적용이 가능할 것으로 보이는 방안을 찾게 되어 기록한다.

2. 처리 size 만큼의 분할 처리 개요

  • 유량 제어 : <배치처리/대용량 데이터 다건 처리>
    -> 배치처리 및 대용량 데이터 다건 처리 시, 한번에 bulk처리 혹은 여러 번의 단건 처리를 일괄적으로 진행해야할 경우 하나의 connection pool을 사용하므로 메모리 관점에서도 문제가 많고 대용량 데이터를 처리해야할 경우 그 자체로 시간 소요/부하 등 여러 문제가 발생할 수 있다.
    -> 이때 할 수 있는 유량제어방안 중 하나인 처리 size를 나누어 여러번 하는 방법이 있다.
    -> 배치처리에서 chunk와 비슷하지만, 위 방법의 경우 모든 데이터 size를 처리할 때 까지 하나의 트랜잭션으로 간주한다는 점, 병렬처리가 아니라는 점, 단일 트랜잭션이면서 분할처리가 가능하다는 점에서 의미가 있다.

  • JPA에서도 해당 기능을 응용한다면 안전한 처리를 구현할 수 있을 것이다.

3. 구현 순서

  • 1) 처리해야할 전체 데이터 리스트를 받아와서 처리 size만큼 분할한다.
  • 2) 그 분할한 데이터 리스트를 처리 엔티티(매개변수)로 넘겨준다.
  • 3) 해당 list를 넘겨받아 <foreach collection = “list” item = “item” separator “,”>(다건처리가능 : 등록) or <foreach collection = “list” item = “item” separator “;”>(다건처리불가 : 수정) 등을 사용해서 적절한 동적 쿼리를 사용하여 처리하는 로직을 구현한다.

4. foreach (mybatis 문법이지만 다른 곳에서도 동일한 원리로 활용 가능)

INSERT INTO TABLE ( A , B , C , ..) 이처럼 INSERT는 내부적으로 A, B, C 전산 다건 처리가 모두 가능하므로

INSERT INTO ~~~

<foreach collection ="list" item = "item" seperator = ",">
(
	#{item.gdsCd, jdbcType = CHAR},
  #{item.coNm, jdbcType = CHAR},
  ....
)
</foreach>

위와 같은 동적쿼리 구성이 가능하며

UPDATE의 경우 다건처리가 불가능하므로, 여러 개의 단건처리를 반복 순회하여 처리하는 방법으로 동적 쿼리를 구성할 수 있다.

<foreach colection = list" item = "item" seperator = ";" open = "DECLARE BEGIN" close = ";" END ;"
UPDATE TABLE
<set>
	<if test = "item.gdsCD != null">
   	GDS_CD = #{item.jdbcType = CHAR}
   </if>
</set>
</foreach>

5. 코드스니펫

  • 아직 정리해야할 부분이 많으나 일단 기록함

    public void doBondKndCdFileList(List<String> fileList){
    	List<DTbBondKndCdEntity> entityFileList = new ArrayList<>();
       
       ITM00001DTO itm00001DTO = new ITM00001DTO();
       
       //처리해야 할 데이터 리스트
       for(String line : fileList){
       	item00001DTO.setCoscomBondKndCd(substringByByte(line, 2, 4));
           item00001DTO.setBondKndNm(substringByByte(line, 6, 30));
           item00001DTO.setEtcCn(substringByByte(line, 36, 155));
           
           DTbBondKndCdEntity dTbBondKndCdEntity = 
           	TbBondKndCdMapper.INSTANCE.toDTbBondKndCdEntity(item00001DTO);
               entityFileList.add(dTbBondKndCdEntity);
       }
       
       //데이터 리스트를 size만큼 분할(=100 = BATCH_SIZE)
       List<DTbBondKndCdEntity> selectList = new ArrayList<>();
       
       for(int i = 0 ; i < entryFileList.size() ; i + = BATCH_SIZE){
       	//BATCH_SIZE만큼 처리하다가 끝 부분은 entityFileList.size()로 처리
       	int end = Math.min(i + BATCH_SIZE, entityFileList.size());
           List<DTbBondKndCdEntity> batchList =  entityFileList.subList(i, end);  //분할처리할 리스트를 여기서 나눈다. (i는 계속 증가하고 이 만큼 분할하여 batchlist에 보관한다는 점이 핵심)
           List<DTbBondKndCdEntity> resultBatch = ditm00001.selectBondKndCdList(batchList);
           selectList.addAll(resultBatch); //selectList에는 분할 처리 size만큼 나뉘어진 데이터리스트들이 저장됨
       }
       
       List<DTbBondKndCdEntity> updateList = new ArrayList<>();
       List<DTbBondKndCdEntity> insertList = new ArrayList<>();
       
       for(DTbBondKndCdEntity fileItem : entityFileList){
       	boolean found = false;
           for(DTbBondKndCdEntity selectItem : selectList){
           	selectItem.getCoscomBondKndCd().equals(fileItem.getCoscomBondKndCd())){
               	found = true;
                  	break; //break를 할 경우 for문에서 탈출
               }
           }
           
           if(found) {
           	updateList.add(fileItem);
           }else {
           	insertList.add(fileItem);
           }
       }
       
       //최종적으로 분할 처리 진행 함
       for(int i = 0 ; i < updateList.size() ; i += BATCH_SIZE){
       	int end = Math.min(i + BATCH_SIZE, updateList.size());
           ditem00001.update03(new ArrayList<>(updateList.subList(i, end)));
       }
       
       //최종적으로 분할 처리 진행 함
       for(int i = 0 ; i < insertList.size() ; i += BATCH_SIZE){
       	int end = Math.min(i + BATCH_SIZE, insertList.size());
           ditem00001.insert03(new ArrayList<>(insertList.subList(i, end)));
       }
    }
post-custom-banner

0개의 댓글