이런..코드 개선하기에 대한 2편을 써야하는데 몇달쨰 미루고 있다. 이번달 내에 끝내봐야지
그전에 이 주제로 이야기를 먼저 해보고 싶다.
한번은 회사에서 대량의 데이터를 넣어보고 처리 시간이 얼마나 걸리는 지 테스트해야하는 미션이 있었다.
그러면 당연히 생각하게 되는 몇가지 방법이 떠오른다.
단순 insert
public void insertProducts() {
String sql = "INSERT INTO products (name, price, stock) VALUES (?, ?, ?)";
try (Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
PreparedStatement pstmt = conn.prepareStatement(sql)) {
for (int i = 1; i <= 1000; i++) {
pstmt.setString(1, "Product " + i);
pstmt.setDouble(2, Math.random() * 100);
pstmt.setInt(3, (int) (Math.random() * 500));
pstmt.executeUpdate(); // 매번 개별 쿼리 실행
}
System.out.println("1000개의 데이터 삽입이 완료되었습니다.");
} catch (SQLException e) {
e.printStackTrace();
}
}
코드를 단순히 for문으로 작성하고 데이터에 insert시키는 방법이 있다.
매번 개별 쿼리를 실행하기 떄문에 1000번의 쿼리가 날라가므로 매우 비효율적이다.
CSV 파일 Insert
public void insertProductsFromCSV() {
String sql = "INSERT INTO products (name, price, stock) VALUES (?, ?, ?)";
try (Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
PreparedStatement pstmt = conn.prepareStatement(sql);
BufferedReader br = new BufferedReader(new FileReader(CSV_FILE_PATH))) {
String line;
br.readLine(); // CSV 헤더 건너뛰기
while ((line = br.readLine()) != null) {
String[] data = line.split(",");
pstmt.setString(1, data[0]);
pstmt.setDouble(2, Double.parseDouble(data[1]));
pstmt.setInt(3, Integer.parseInt(data[2]));
pstmt.addBatch(); // 배치에 추가
}
pstmt.executeBatch(); // 모든 배치 실행
System.out.println("CSV 파일을 통해 데이터 삽입이 완료되었습니다.");
} catch (Exception e) {
e.printStackTrace();
}
}
csv 파일을 만들어서 insert하는 방법도 있다. batch로 Data를 삽입한다는 점에서 한번에 로직이 실행될 수 있어 이전보다 향상된 성능을 보일 수 있다. 하지만 여전히 데이터를 만들기 위한 csv파일을 작성해야한다는 번거로움이 있다.
Batch Insert
public void insertProducts() {
String sql = "INSERT INTO products (name, price, stock) VALUES (?, ?, ?)";
try (Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
PreparedStatement pstmt = conn.prepareStatement(sql)) {
conn.setAutoCommit(false); // 자동 커밋 비활성화
for (int i = 1; i <= 1000; i++) {
pstmt.setString(1, "Product " + i);
pstmt.setDouble(2, Math.random() * 100); // 가격 랜덤 생성
pstmt.setInt(3, (int) (Math.random() * 500)); // 재고 랜덤 생성
pstmt.addBatch(); // 배치에 추가
// 배치 처리: 100개 단위로 실행
if (i % 100 == 0) {
pstmt.executeBatch();
conn.commit(); // 커밋
pstmt.clearBatch(); // 배치 초기화
}
}
// 나머지 데이터 처리
pstmt.executeBatch();
conn.commit();
System.out.println("1000개의 데이터 삽입이 완료되었습니다.");
} catch (SQLException e) {
e.printStackTrace();
}
}
대량의 데이터를 csv를 사용하지 않고 단순 insert하는 방법도 있다.
1000건이라면 큰 차이가 나지는 않겠지만, 10000, 100000개의 데이터를 넣으려면 DB와의 커넥션 연결에 따라서 지연이 발생하거나 데이터 삽입이 실패할 가능성도 있다. 코드와 쿼리를 모두 고민해야한다는 문제도 여전히 남아있다.
여기까지는 내가가 활용해볼 수 있는 가장 선의 방법이라고 생각했다. 하지만, 조금 야매같지만, 더 확실한 방법이 있었다.
DB에 직접 삽입
INSERT INTO {table_name}
(Column1, Column2, Column3, Column4, Column5)
SELECT
(Column1, Column2, Column3, Column4, Column5)
FROM {table_name}
LIMIT {내가 넣고 싶은 데이터 수}
자기 자신을 Select해서 데이터를 삽입하는 방법이다.
LIMIT 절에 내가 넣고 싶은 데이터 수만큼을 조회하고, 그만큼 데이터를 그대로 Insert한다.
물론 주의해야할 점은, LIMIT절에 넣는 데이터는 현재 DB에 저장된 데이터만큼의 개수는 가지고 있어야 한다.
단순히 더미데이터를 넣는 과정이기 때문에 데이터의 중복이 발생한다는 점도 알아두어야 한다. 보통 성능 테스트를 위한 목적으로 활용하는게 적절하다고 알고 있으면 될 듯 하다.
대단한 방법은 아니지만, 이런식으로 쿼리를 데이터를 넣게 되면 몇 초 내에도 데이터를 집어넣을 수 있다는 점을 알 수 있다.
앞으로도 이런 식으로 내가 알고 있는 범위 내에서 최선의 결과를 보기 위한 노력도 필요하다고 느꼈다
'개발 생각' 카테고리의 다른 글
개발 생각 1 : 레거시와 공존하기 (DDD 월드클래스 아닙니다!) (0) | 2024.08.27 |
---|