트러블슈팅

대용량 데이터 삽입 시 JPA를 JDBC로 교체한 성능 최적화 사례

초록색거북이 2024. 10. 4. 16:01
728x90
반응형
SMALL

문제상황

 

기존에 대량의 데이터를 데이터베이스에 저장할 때, JPA의 saveAll 기능을 사용하고 있었습니다. 하지만 많은 양의 데이터를 처리할 때 성능 이슈가 발생하여, 보다 효율적인 방법이 필요했습니다. 특히, 한 번에 수천 건의 데이터를 삽입하는 경우, JPA의 saveAll은 예상보다 느린 성능을 보였습니다. 이를 해결하기 위해 JDBC 기반의 batchInsert 방식으로 코드를 개선했습니다.

 

 

수정코드

 

public List<SmsSendTargetDto> bulkInsertSmsMsgSendDetail(SmsQueueDto smsQueueDto, String msgGroupId) {
    String sql = "INSERT INTO table (msg_id, msg_group_id, recv_no, user_seq, send_state_cd, daou_msg_id, app_uid, reg_user_id, reg_datetime) VALUES (?,?,?,?,?,?,?,?,?)";
    List<SmsSendTargetDto> sendTargetDtoList = new ArrayList<>();

    jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
        @Override
        public void setValues(PreparedStatement ps, int i) throws SQLException {
            String msgId = UUID.randomUUID().toString();
            MsgSendMasterDto dto = smsQueueDto.getMsgSendMasterDto();
            SmsDetailDto smsDetailDtos = smsQueueDto.getSmsDetailDtos().get(i);
            ps.setString(1, msgId);
            ps.setString(2, msgGroupId);
            try {
                ps.setString(3, AES256Cipher.encrypt(smsDetailDtos.getRecvNo()));
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            if (smsDetailDtos.getUserSeq() != null) {
                ps.setLong(4, Long.parseLong(smsDetailDtos.getUserSeq()));
            } else {
                ps.setNull(4, Types.BIGINT); // BIGINT 타입의 NULL 처리
            }
            ps.setString(5, "READY");
            ps.setString(6, null);
            ps.setString(7, null);
            ps.setString(8, dto.getRegUserId());
            ps.setTimestamp(9, Timestamp.valueOf(now()));

            sendTargetDtoList.add(SmsSendTargetDto.builder()
                    .msgId(msgId)
                    .recvNo(smsDetailDtos.getRecvNo())
                    .build());
        }

        @Override
        public int getBatchSize() {
            return smsQueueDto.getSmsDetailDtos().size();
        }
    });
    return sendTargetDtoList;
}

 

개선된 부분

 

 

  • 성능 향상: 기존의 JPA saveAll 방식을 JDBC의 batchInsert로 전환함으로써, 대량의 데이터를 훨씬 빠르게 삽입할 수 있었습니다. 이 방법은 특히 트랜잭션을 관리하고 성능을 높이는 데 효과적입니다.
  • null 처리: user_seq 필드가 null일 수 있는 상황을 고려하여, null 값일 경우 setNull(4, Types.BIGINT)을 사용해 데이터베이스에 null로 저장하도록 처리했습니다.
  • 에러 처리: AES256Cipher.encrypt 과정에서 발생할 수 있는 예외를 처리하여 안정성을 높였습니다.

 

 

결론

 

대량의 데이터를 삽입할 때 JPA의 saveAll을 사용하는 것보다 JDBC의 batchInsert를 사용하는 것이 성능 측면에서 훨씬 효율적입니다. 특히, null 값 처리와 예외 상황에 대한 고려를 통해 안정성을 확보할 수 있었습니다. 대규모 트랜잭션을 처리하는 경우에는 JDBC 기반의 배치 삽입을 적극 활용하는 것이 좋습니다.

728x90
반응형
LIST