BULK INSERT

김맥스·2023년 3월 11일

데이터베이스

목록 보기
1/3
post-thumbnail

회사에서 나에게 많은 도움을 주었던 개발자 선배가 많았는데, 그 중 한명인 두모씨가 쓰윽 나타나서 툭 던져주고 간 Bulk Insert 개념

코드 리팩토링시 가장 중점적으로 리팩토링 되어야 할 부분 중에 하나라고 한다

픽셀플러스 리부트 전에 이 개념을 적용해서 코드를 짰던 기억이 있는데 그 이후로 새까맣게 잊고 있다가 이제서야 다시 떠올랐다!

핵심은 DB에 접촉하는 것을 최대한 줄여야 성능이 좋아진다는 것!

// 현재 내 코드 일부

for (const v of 배열) {
  await 레포지토리.save({
    ...,
    language: v.language,
    description: v.description,
    title: v.title,
  });
}

이 코드의 문제점은 반복문을 돌면서 그때마다 한 번씩 DB에 INSERT 해주는 작업을 하고 있다는 것이다. 이럴 경우 개수가 많아지면 100, 1000, … N번의 INSERT 작업이 필요하게 되는데 상당히 비용이 많이 드는 작업이다. 똑같은 틀의 INSERT 작업이라면 배열로 한 번에 모아서 INSERT 할 수 있다. 그럼 테스트를 해보자

1. TEST

  • 먼저 아래 테스트 코드는 실행시간을 측정하는 부분이 잘못되었다는 점을 미리 알린다. 실행시간 측정하는 부분이 디비에 접촉하는 순간에만 찍혀야 하는게 맞는 것 같다. 그래서 단일 insert는 잘 측정되었지만 벌크 insert 부분에서 console.time()이 await productTranslationBackupRepository.save(aa); 이 문장 바로 위에 와야 하는게 맞는 것 같다.
    console.log("실행시간 :", new Date());
    
    console.time("단일 insert");
    for (const v of 배열) {
      await 레포지토리.save({
        ...,
        language: v.language,
        description: v.description,
        title: v.title,
      });
    }
    console.timeEnd("단일 insert");
    
    console.time("벌크 insert");
    const aa = 배열.map((v) => {
      return {
        ...,
        language: v.language,
        description: v.description,
        title: v.title,
      };
    });
    
    await 레포지토리.save(aa);
    console.timeEnd("벌크 insert");
    
    console.log("============================");

10번의 실행결과

  • 빠를 땐 2배 이상 빠른데 느릴 때도 있고 결과 값이 일정하지 않다…. 왜 그럴까?

    • 추측 1. 비교하려는 데이터의 양이 충분하지 않아서…?
    • 추측 2. 디비 캐싱…?
    • 추측 3. for문과 map 사용의 차이…?
    • 확실한 오류: 실행 시간 측정 위치 잘못됨

위 수정사항을 반영하여 다시 측정

console.log("실행시간 :", new Date());

console.time("단일 insert");
for (var i = 0; i < 1000; i++) {
  console.log("단일 -----", i);
  await 레포지토리.save({
    ...,
    ...: historyCount + 1,
  });

  await 레포지토리.createQueryBuilder();
}
console.timeEnd("단일 insert");

let result = [];
for (var i = 0; i < 1000; i++) {
  result.push({
    ...,
    ...: historyCount + 1,
  });
}

console.time("벌크 insert");
console.log("벌크 -----");
await this.connection
  .createQueryBuilder()
  .insert()
  .into(...)
  .values(result)
  .execute();

console.timeEnd("벌크 insert");

console.log("============================");

1. INSERT 1개

  • 별 차이가 없다

2. 10개

  • 슬슬 눈에 띄는 차이가 보이기 시작한다
  • 가끔 BULK INSERT가 더 느리게 나오는데… 정확한 원인이 궁금하다 (3번째 케이스)

3. 100개

  • 확연한 차이가 보인다
  • 벌크가 대략 11배 빨랐다

4. 1000개

  • 벌크가 대략 15배 빨랐다

5. 10000개

  • 벌크가 대략 7배 빨랐다

6. 30000개

  • 벌크가 대략 2배 빨랐다

배운 점

bulk insert에 대한 개념을 확실히 잡을 수 있었고 모든 비교 조건이 완벽하지는 않았겠지만 눈으로 직접 성능 확인도 해서 앞으로 잘 사용할 수 있을 것 같다

profile
3년차 백엔드 개발자의 공부 기록

0개의 댓글