DB

[MySQL] 대량 INSERT 성능 최적화 방법 완벽 정리

인생아 2025. 6. 20. 17:43
반응형

MySQL에서 데이터를 삽입하는 INSERT 문은 기본 중의 기본이지만,
수천, 수만 건의 대량 데이터를 INSERT할 경우,
성능 저하디스크 부하, 트랜잭션 병목 등 다양한 이슈가 발생할 수 있다.

🔍 기본 INSERT는 어떻게 동작할까?

MySQL에서 INSERT는 한 번에 한 건씩 처리하면 다음과 같은 쿼리가 된다.

INSERT INTO members (name, age) VALUES ('홍길동', 25);
INSERT INTO members (name, age) VALUES ('김철수', 31);
INSERT INTO members (name, age) VALUES ('이영희', 22);

위 방식은 SQL 파싱, 실행, 인덱스 처리, 디스크 쓰기가 반복적으로 일어나기 때문에 성능이 매우 떨어질 수 있다.

반응형

1. 다중 행 INSERT로 병합하기

가장 기본적인 최적화 방법은 한 번에 여러 행을 삽입하는 것이다.

INSERT INTO members (name, age) VALUES
('홍길동', 25),
('김철수', 31),
('이영희', 22),
('최민수', 28);

이렇게 하면 SQL 파싱 및 디스크 쓰기 비용이 절반 이하로 줄어든다.
MySQL은 내부적으로 이를 한 트랜잭션으로 처리하므로 훨씬 빠르게 작동한다.

💡 Tip: 한 번에 삽입하는 행 수는 100~1,000개 정도가 권장됨

2. 트랜잭션 사용으로 COMMIT 최적화

기본적으로 MySQL은 autocommit 모드가 켜져 있어서
각 INSERT마다 자동 커밋을 수행한다.
이 방식은 성능에 큰 병목이 된다.

SET autocommit = 0;
START TRANSACTION;

INSERT INTO ...
INSERT INTO ...
...

COMMIT;

이렇게 여러 INSERT를 한 번의 트랜잭션으로 묶으면
디스크 커밋 횟수가 줄어들어 속도가 대폭 향상된다.

반응형

3. 인덱스 최소화 또는 작업 후 생성

데이터를 많이 넣는 테이블이라면
초기에는 인덱스를 제거한 상태로 INSERT하고,
모든 데이터를 넣은 후 인덱스를 다시 생성하는 전략이 유효하다.

-- 인덱스 제거
ALTER TABLE members DROP INDEX idx_name;

-- 대량 INSERT 수행

-- 인덱스 복구
ALTER TABLE members ADD INDEX idx_name(name);

인덱스가 많을수록 INSERT 속도가 느려지므로
대량 데이터 적재 시에는 인덱스를 최소화하자.

4. REPLACE 또는 UPSERT 대신 INSERT만 사용하기

REPLACE나 INSERT ... ON DUPLICATE KEY UPDATE 구문은
내부적으로 DELETE → INSERT 또는 INSERT + UPDATE 처리를 수행한다.
이로 인해 성능이 급격히 하락할 수 있다.

대량 데이터를 처리할 때는 충돌 가능성이 없는 순수 INSERT가 가장 빠르다.

5. LOAD DATA INFILE 사용하기

MySQL에서 대량 데이터를 삽입할 때 가장 빠른 방법은
바로 LOAD DATA INFILE이다.

LOAD DATA INFILE '/var/lib/mysql-files/members.csv'
INTO TABLE members
FIELDS TERMINATED BY ',' 
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 ROWS;

CSV 파일 형식으로 데이터를 미리 저장해두고,
LOAD DATA INFILE을 사용하면 수백만 건도 단 몇 초 안에 삽입할 수 있다.

💡 Tip: LOCAL 옵션을 사용하면 클라이언트 파일도 불러올 수 있음

6. 비동기 방식 또는 배치 처리 도입

애플리케이션에서는 실시간으로 모든 데이터를 DB에 넣지 말고,
배치(Batch)나 버퍼링 큐 방식으로 모아서 한 번에 INSERT하는 것이 좋다.

예를 들어 Node.js나 Java에서는 아래와 같이 구현한다.

// Node.js 예시 - 100건 단위로 쌓이면 한 번에 INSERT
const buffer = [];

function insertData(data) {
  buffer.push(data);
  if (buffer.length >= 100) {
    connection.query('INSERT INTO members ...', buffer);
    buffer.length = 0;
  }
}

이 방식은 DB 부하를 줄이고, 전체 성능을 안정적으로 유지시켜 준다.

반응형

7. INSERT DELAYED (주의: 더 이상 비추천)

과거에는 INSERT DELAYED 명령어도 존재했지만,
MySQL 5.7부터는 더 이상 권장되지 않으며 일부 버전에서는 제거되었다.
사용 금지 또는 대체 전략 권장

🧠 정리하자면…

  • INSERT 시 다중 행 병합은 기본 중 기본
  • 트랜잭션으로 커밋 횟수 최소화
  • 인덱스는 최소화 → 후에 재생성
  • UPSERT 대신 순수 INSERT만 사용
  • 가능하다면 LOAD DATA INFILE 적극 활용
  • 백엔드에서는 비동기, 버퍼, 배치 INSERT로 처리
  • 성능 측정을 통해 적절한 삽입량(행 수)을 조정

📎 공식 문서 참고

MySQL INSERT 성능 공식 가이드
https://dev.mysql.com/doc/refman/8.0/en/insert-optimization.html

 

반응형