개발 메모장

[Java] Apache.POI 엑셀 업로드 구현 - DB 처리(3/3) 본문

Java

[Java] Apache.POI 엑셀 업로드 구현 - DB 처리(3/3)

yyyyMMdd 2024. 1. 5. 13:10
728x90

#. 엑셀 파일을 데이터화하는 작업은 처리했으나 DB에 insert 하는 속도 문제 또한 중요합니다.

 

#. Service에서 myBatis를 이용해 DB접근하는 for문으로 처리해보려 했으나 이 또한 DB 접근을 건마다 해야 하므로 좋은 방법은 아니라는 생각이 들었습니다.(1000건에 360초)

 

#. 오라클의 Insert all과 foreach를 이용해보려 했으나 이 또한 시간이 오래 걸렸습니다.(1000건에 7초)

 

#. 벌크 인서트의 경우 PL/SQL에서만 사용가능하다 보니 이 또한 적절하지 않다고 생각했습니다.

 


#. DB 처리 로직 구현

  • 업로드할 데이터의 수는 약 20만 건으로 고정적인 데이터라 그 이상이 될 일은 거의 없었습니다.

  • 여러 가지로 구현해 보고 테스트해봤을 때 가장 효과가 좋다고 생각한 addBatch를 통해 처리하였습니다.
  • 분명 더 좋은 퍼포먼스를 보여주는 방법도 있을 것이지만 혼자 서치 및 구현하는 데에 시간적 제한이 있었습니다.

  • 더 좋은 방법이 있다면 꼭 댓글 부탁드립니다!
@Transactional
public void addBatch(List<String> headers, List<HashMap<String, String>> postData) throws Exception {
    Connection conn = null;
    PreparedStatement pstmt = null;
    String sql = "insert into 테이블명 values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
    try {
        Class.forName("net.sf.log4jdbc.sql.jdbcapi.DriverSpy");
        // DB 연결
        conn = DriverManager.getConnection("jdbc:log4jdbc:oracle:thin:@IP주소:포트번호:SID", "접속 ID", "비밀번호");
        pstmt = conn.prepareStatement(sql);

        // AutoCommit 해제
        conn.setAutoCommit(false);

        int i = 0;
        // sql 변수에 들어갈 내용을 꺼내어 셋팅 및 처리
        for(HashMap<String, String> postDatum:postData) {
            // 변수 번호 / 데이터 반복 처리
            for(int j = 0; j < headers.size(); j++) {
                pstmt.setString(j + 1, postDatum.get(headers.get(j)));
            }

            pstmt.addBatch();
            pstmt.clearParameters();

            i++;

            // 5000건 단위 수동 커밋
            if((i%5000)==0) {
                pstmt.executeBatch();
                pstmt.clearBatch();
                conn.commit();
            }
        }

        // 5000건 외 나머지 처리
        pstmt.executeBatch();
        conn.commit();

    } catch (Exception e) {
        e.printStackTrace();
        try {
            conn.rollback();
            throw e;
        } catch (SQLException e1) {
            e.getStackTrace();
            throw e1;
        }
    } finally {
        // 자원 반환
        if(pstmt != null) { try {pstmt.close();pstmt = null; } catch (SQLException e) { e.getStackTrace();}}
        if(conn != null) { try {conn.close();conn = null; } catch (SQLException e) { e.getStackTrace();}}
    }
}

 


#. 처리 속도 이슈

  • 구현 예제와는 다르게 실제 데이터의 행 개수는 약 50개이다 보니 속도 문제가 중요했었습니다.
  • 서두에 적어놨지만 1000건 처리에 시간이 꽤나 소요되었으나 위와 같이 처리 후 테스트 해 본 결과 1000건에 1초도 걸리지 않았습니다.

  • 다만 건수가 많아지면 많아질수록 데이터를 메모리에 올리는 작업에 들어가는 시간이 길어졌습니다.

  • 약 20만 건의 데이터를 인서트 하는 데에 총 2분 정도 소요되는 걸 확인하였습니다.

  • 실질적인 인서트 작업 이전에 메모리에 올리는 작업의 속도를 더 챙겨줄 수 있는 방법을 알았다면 조금 더 만족했을 텐데 아쉬운 부분이 있습니다.

  • 추후 시간적 여유가 생긴다면 해당 부분을 수정해 보도록 하겠습니다.

 

 

===========================================================
틀린 내용이 있거나 이견 있으시면 언제든 가감 없이 말씀 부탁드립니다!
===========================================================

728x90