본문 바로가기
JAVA

[Java] 대용량 Insert 상세 설명 (Batch, Dynamic sql)

by GoodDayDeveloper 2022. 9. 21.
반응형

 

대용량 Insert 기능을 찾아보다가 부족한 내용이 많아 직접 정리해보려합니다.

자바에서 제공하는 PreparedStatement의 addBatch를 이용한 대용량 Insert 방법과 함께

아래와 같이 다양한 Insert에 대해 알아보겠습니다.

 

  • 일반 반복문의 Insert
  • 배치를 사용한 Insert
  • 동적 SQI nsert (foreach)

 

데이터 row갯수는 15,000건으로 다각도의 Insert문을 실행해보았으며, 

대용량 Insert에 어느 것이 최적화되었는지 살펴보겠습니다.

 

 

 

 

 

 

일반 반복문의 Insert

 

 

일반적인 반복문의 insert 코드입니다.

이 insert형태는 리스트의 데이터를 하나하나 뽑은다음 한 행씩 저장시키는 구조입니다.

적은 양의 데이터는 상관이 없으나, 1000개가 넘어가면 지연 시간이 길어지며,

15,000개가 정도의 양은 시간이 많이 소요됩니다.

 

테스트 결과 시간은 대략 3분 13초 (193초)가 나왔습니다.

connection를 자주 호출하기 때문에 속도가 느릴 수 밖에 없습니다..

대용량 저장 방식에는 적합하지 않는 모습이군요..

 

 

 

 

 

 

 

 

 

배치를 이용한 Insert

 

다음은 자바의 내장 객체중, PrepareStatement를 이용하여 addBatch를 사용하는 방식입니다.

쿼리를 메모리에 저장하였다가 한번에 쿼리를 보내는 형태입니다.

 

아래 코드를 보며 이야기해보겠습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
 
 
public void insertCode() {
        
    PreparedStatement pstmt = null;
    Connection con = null;
 
 
try {    
 
    String sql = "Insert Into table(index, title, name, phone) "
    + "Values(?,?,?,?)" ;
    
    Class.forName("org.postgresql.Driver");
    con =  DriverManager.getConnection("jdbc:postgresql://111.222.333.44/dbname","id","pw");
    pstmt = con.prepareStatement(sql);
 
    //Cannot rollback when autoCommit is enabled 에러시 사용
    //con.setAutoCommit(false);
    
    
    List<VO> getList = geList(); 
    int count = 0;
    
    for (VO v : getList) {
        
        ++count;
        
        pstmt.setInt(1, v.getIndex());
        pstmt.setString(2, v.getTitle());
        pstmt.setString(3, v.getName());
        pstmt.setString(4, v.getPhone());
        
        
        pstmt.addBatch();
        pstmt.clearParameters();
        
        
        if((count%100== 0){
            pstmt.executeBatch();
            pstmt.clearBatch();
            con.commit();
        }
        
    }
    
    pstmt.executeBatch();
    con.commit();
    
    
}catch(Exception e){
    con.rollback();
}finally{
    if(pstmt != null) pstmt.close();
    if(con != null) con.close();
}
 
 
}
cs

 

 

디비에 연결할 Connection과,  정보를 담을 PreparedStatement를 선언하고 Import해줍니다.

(Import가 제대로 안되면 에러가 발생합니다.)

 

 

sql 변수에 쿼리를 넣어주는데,

여기서 중요한 부분은 값이 들어가는 부분을 물음표(?) 처리해주는 겁니다.

 

 

Connectiondp 데이터베이스 정보를 담고 

PreparedStatement에 선언한 pstmt 객체에 담아줍니다.

 

 

반복문으로 리스트 객체의 값을 pstmt에 넣어줍니다.

이때 순서를 지정하면 순서대로 물음표에 들어가는 구조입니다.

 

 

 

다 담았다면,

 

  • pstmt.addBatch(); -  정보를 담고
  • pstmt.clearParameters() ; - 파라미터를 지워줍니다.
  • if( (count % 100) == 0){  - 100개마 if문으로 들어와
  • pstmt.executeBatch() ; - Batch를 실행하고
  • pstmt.clearBatch(); - Batch를 초기화해줍니다.

 

 

마지막으로 나머지 갯수의 데이터를 반복문이 끝나면 다시 담아주도록 합니다.

 

 

15,000개의 데이터를 넣어보니 약 1분 7초 (67초)가 나왔습니다.

한번의 connection을 호출해서 저장하기 때문에

connection를 자주 호출하는 일반적인 Insert보다 약 3배 차이가 나는군요!

 

 

 

 

반응형

 

 

 

 

 

 

 

 

동적 SQL Insert (foreach)

 

마지막으론 다이나믹 쿼리를 활용하는 방식입니다.

배치 insert와 같이 한번의 connection을 호출하기 때문에 대용량 저장방식에 적합합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<insert id="insert" parameterType="HashMap">
    INSERT INTO table
    (
        title,
        name,
        phone
    )
    VALUES
    <foreach collection="list" item="value" open="(" close=");"  separator=",">
    (
        #{value.title},
        #{value.name},
        #{value.phone}
    )
    </foreach>
</insert>
 
cs

 

 

collection에 배열이 들어있는 맵의 키를 넣고,

item의 변수 값을 통해서 값을 꺼내주면 됩니다.

 

 

 

<foreach collection="list" item="value" open="(" close=");"  separator=",">

 

문법 형태

  • collection - 전달받은 인자 값 (List 나 Array 형태만 가능)
  • item - 전달받은 인자 값의 alias명
  • open - 해당 구분이 시작될 때의 삽입할 문자열
  • close - 해당 구문이 종료될 때의 삽입할 문자열
  • separator - 반복 되는 사이에 삽입할 문자열
  • index - 반복되는 구문 번호 (목록의 위치 값)

 

 

 

 

 

 

하지만 데이터 양이 많을수록 메모리 부하가 많이 일어나기 때문에 

쿼리 반복문은 권장하지 않는 추세입니다.

 

 

가장 효율적인 배치 Insert를 활용하는 것이 좋지 않을까란 개인적인 생각입니다.

 

 

 

 

[Java] 여러개 Select 구현 방법 (PreparedStatement + Union All)

 

[Java] 여러개 Select 구현 방법 (PreparedStatement + Union All)

PreparedStatement를 이용하여 insert뿐만 아니라 select도 이용할 수가 있습니다. 더군다나 자바를 이용하기 때문에 조건식에 맞춰 여러개의 select를 한번에 불러올 수가 있습니다. 여러개의 select를 각

chobopark.tistory.com

 

 

 

 

반응형

댓글