MySQL batch insert 가이드: 대량 쓰기를 빠르게 하면서도 안전하게 운영하려면
DB
마지막 업데이트

MySQL batch insert 가이드: 대량 쓰기를 빠르게 하면서도 안전하게 운영하려면


대량 데이터를 저장해야 할 때 많은 팀이 가장 먼저 하는 질문은 “한 번에 몇 건씩 넣어야 하지?”입니다. 그런데 실무에서 batch insert는 단순히 “한 SQL에 row를 많이 붙인다”로 끝나는 문제가 아닙니다. 배치를 너무 작게 잡으면 throughput이 안 나오고, 너무 크게 잡으면 트랜잭션이 길어지고 잠금 영향과 실패 비용이 커집니다.

그래서 batch insert를 잘 쓰려면 SQL 문장만 보는 것이 아니라, 네트워크 왕복, commit 빈도, 인덱스 갱신 비용, 중복 처리 방식, 라이브 트래픽과의 충돌까지 함께 봐야 합니다.

이 글은 아래 질문을 기준으로 정리했습니다.

  • batch insert가 왜 row-by-row insert보다 유리한지
  • 배치 크기와 commit 단위를 어떻게 잡아야 하는지
  • 인덱스와 중복 처리 방식이 왜 쓰기 비용을 바꾸는지
  • 운영 중 어떤 실수 때문에 오히려 느려지는지

짧게 말하면 MySQL batch insert의 핵심은 “최대한 크게 넣기”가 아니라, 왕복 비용은 줄이되 트랜잭션과 잠금 리스크는 통제되는 크기로 안정적으로 밀어 넣는 것입니다.

대량 데이터 삽입 개념


Quick answer

MySQL batch insert를 실무적으로 정리하면 아래 순서가 가장 중요합니다.

  1. row-by-row insert가 정말 병목인지 먼저 확인한다
  2. 여러 row를 한 statement와 한 transaction에 적절히 묶어 왕복 비용을 줄인다
  3. 배치 크기는 “가장 큰 값”이 아니라 실패 비용과 잠금 시간을 감당할 수 있는 수준으로 정한다
  4. 인덱스 수와 unique 제약이 쓰기 비용을 얼마나 키우는지 같이 본다
  5. INSERT IGNORE, ON DUPLICATE KEY UPDATE처럼 중복 처리 방식이 성능과 의미를 바꾸는 점을 이해한다
  6. 라이브 트래픽과 같은 테이블을 함께 쓰면 lock, replication lag, deadlock 가능성도 점검한다
  7. 수정 전후 throughput, latency, 에러율을 같이 비교한다

즉, batch insert 최적화의 핵심은 한 문장에 row를 더 많이 붙이는 기술이 아니라 대량 쓰기 경로 전체를 운영 가능한 형태로 설계하는 것입니다.


1. 왜 row-by-row insert는 금방 비효율적이 될까

한 건씩 insert하는 방식은 코드가 단순해 보입니다. 하지만 대량 쓰기에서는 같은 비용을 너무 많이 반복합니다.

row-by-row insert에서는 매번:

  • SQL 전송
  • 파싱과 실행
  • 네트워크 왕복
  • commit 또는 autocommit 처리

가 반복됩니다.

예를 들어 10,000건을 아래처럼 한 건씩 넣는다면:

INSERT INTO event_logs (event_type, user_id, created_at)
VALUES ('login', 42, NOW());

같은 작업을 10,000번 반복하게 됩니다.

반대로 여러 row를 한 번에 묶으면:

  • statement 수가 줄고
  • commit 횟수가 줄고
  • 왕복 오버헤드가 줄어듭니다

즉, batch insert가 빠른 이유는 “INSERT 문법이 특별해서”라기보다 반복 오버헤드를 덜 내기 때문입니다.


2. 어떤 상황에서 batch insert가 특히 효과적인가

batch insert는 보통 단건 응답 시간보다 총 처리량이 중요한 경로에서 가장 효과가 큽니다.

대표적인 예시는 아래와 같습니다.

  • 로그나 이벤트 적재
  • CSV, 외부 데이터 일괄 import
  • 백오피스 대량 등록
  • 주기적 동기화 작업
  • 메시지나 배치 큐 소비 결과 저장

이런 경로의 공통점은 “한 요청의 50ms 차이”보다 “1분 동안 몇 건을 안정적으로 처리하느냐”가 더 중요하다는 점입니다.

즉, batch insert는 온라인 read API보다는 백그라운드 처리, ingest, admin bulk action 쪽에서 체감 효과가 더 큰 경우가 많습니다.


3. 가장 기본적인 형태: multi-row insert

가장 단순한 batch insert는 여러 row를 한 statement에 넣는 방식입니다.

INSERT INTO event_logs (event_type, user_id, created_at)
VALUES
  ('login', 42, NOW()),
  ('logout', 42, NOW()),
  ('purchase', 42, NOW());

이 방식은 row-by-row보다 보통 아래에서 유리합니다.

  • statement 개수 감소
  • 네트워크 왕복 감소
  • commit 횟수 감소

하지만 여기서 바로 “그럼 무조건 최대한 많이 묶자”로 가면 위험합니다. batch insert의 성패는 대개 문법보다 배치 크기와 transaction scope에서 갈립니다.


4. 배치 크기는 어떻게 정해야 할까

실무에서 가장 자주 받는 질문이 이것입니다. 정답은 고정 숫자 하나가 아니라, 아래 기준 사이의 균형입니다.

  • 처리량을 충분히 높일 수 있는가
  • 실패했을 때 재시도 비용을 감당할 수 있는가
  • 트랜잭션이 너무 오래 열리지 않는가
  • 잠금 영향이 운영에서 버틸 만한가

배치를 너무 작게 잡으면:

  • statement가 많아지고
  • commit이 자주 일어나고
  • 왕복 비용이 커집니다

배치를 너무 크게 잡으면:

  • statement 자체가 무거워지고
  • transaction이 길어지고
  • rollback 비용이 커지고
  • live traffic과 충돌할 가능성이 높아집니다

그래서 보통은 “가장 크게”보다 중간 크기에서 안정적으로 반복 처리되는 지점을 찾는 편이 좋습니다.

실무적으로는 아래 질문이 더 유용합니다.

  • 실패 시 이 배치를 그대로 다시 넣어도 괜찮은가
  • 한 배치가 1초 이상 오래 잡히면 운영에서 부담이 큰가
  • 같은 테이블을 읽고 쓰는 온라인 요청이 동시에 많은가

즉, batch size는 성능 숫자 하나가 아니라 운영 리스크를 포함한 설계 값입니다.


5. transaction 범위와 commit 빈도를 같이 보기

많은 경우 batch insert 성능은 VALUES 개수만이 아니라 transaction boundary에 더 크게 좌우됩니다.

예를 들어:

  • 10건마다 commit
  • 1,000건마다 commit
  • 50,000건을 한 transaction으로 commit

은 모두 운영 영향이 다릅니다.

transaction이 너무 짧으면 commit 오버헤드가 자주 발생합니다. 반대로 너무 길면:

  • 잠금이 오래 유지되고
  • 충돌 시 되돌릴 양이 커지고
  • 장애 시 재처리 단위가 지나치게 커질 수 있습니다

특히 batch insert 중에 아래 패턴은 위험합니다.

  • transaction 안에서 외부 API 호출
  • transaction 안에서 파일 처리나 긴 검증 로직 수행
  • 여러 unrelated 작업을 하나의 긴 transaction에 몰아넣기

핵심은 대량 쓰기를 batch로 묶더라도 transaction은 가능한 짧고 명확한 단위로 유지하는 것입니다.

트랜잭션 자체를 더 넓게 이해하고 싶다면 MySQL transaction 가이드를 같이 보는 편이 좋습니다.


6. 인덱스가 많을수록 쓰기 비용도 커진다

batch insert를 설계할 때 자주 놓치는 부분이 인덱스 비용입니다. INSERT는 테이블 데이터만 쓰는 것이 아니라 관련 인덱스도 함께 갱신해야 합니다.

즉, 읽기 최적화를 위해 인덱스를 많이 붙인 테이블은 대량 쓰기에서 더 무거워질 수 있습니다.

아래 상황은 특히 주의해야 합니다.

  • secondary index가 많은 테이블
  • unique index가 여러 개인 테이블
  • insert와 동시에 duplicate check가 많이 일어나는 경우

그래서 batch insert가 기대보다 느리다면 SQL 문장만 보지 말고:

  • 인덱스 개수
  • unique 제약
  • clustered index 특성

도 같이 봐야 합니다.

핵심은 “인덱스가 있으니 좋다”가 아니라 읽기 성능을 위해 만든 구조가 쓰기 경로에는 비용이 된다는 점입니다.


7. 중복 처리 방식은 성능과 의미를 함께 바꾼다

대량 insert에서는 중복 키 처리 전략도 매우 중요합니다. 예를 들어 아래 방식들은 이름만 비슷해 보여도 의미가 다릅니다.

  • INSERT
  • INSERT IGNORE
  • INSERT ... ON DUPLICATE KEY UPDATE

예를 들어:

INSERT INTO users (email, name)
VALUES ('a@example.com', 'Alice')
ON DUPLICATE KEY UPDATE name = VALUES(name);

이 방식은 편리하지만, 단순 insert가 아니라 duplicate check와 update 경로까지 열리므로 비용과 락 패턴이 달라질 수 있습니다.

또한 INSERT IGNORE는 에러를 조용히 넘겨 버리므로, 성능 이전에 “정말 데이터 품질상 무시해도 되는가”를 먼저 생각해야 합니다.

즉, 중복 처리 전략은 단순히 성공률을 올리는 옵션이 아니라 쓰기 의미, 비용, 재처리 전략을 함께 바꾸는 선택입니다.


8. live traffic과 같이 돌면 무엇이 위험한가

배치 작업은 보통 백그라운드라서 괜찮아 보이지만, 같은 테이블을 서비스 요청도 동시에 사용하고 있다면 이야기가 달라집니다.

이때는 아래 문제가 같이 생길 수 있습니다.

  • row lock 또는 gap lock 경합
  • deadlock 증가
  • replication lag 증가
  • online query latency 상승

예를 들어 실시간 주문 처리 테이블에 밤마다 큰 배치를 넣으면, 그 시간대의 사용자 요청이 같이 영향을 받을 수 있습니다.

그래서 운영에서는 아래를 함께 판단해야 합니다.

  • 라이브 요청과 같은 테이블을 쓰는가
  • batch를 더 잘게 나눌 필요가 있는가
  • 트래픽이 낮은 시간대로 옮길 수 있는가
  • staging table을 거쳐 later merge하는 편이 나은가

즉, batch insert는 “백그라운드니까 안전하다”가 아니라 운영 트래픽과 어떤 자원을 공유하는가까지 봐야 합니다.

잠금 충돌이 이미 보인다면 MySQL deadlock 가이드MySQL lock wait timeout 가이드를 같이 보는 편이 좋습니다.


9. 아주 큰 적재라면 giant INSERT 하나만 고집하지 않기

대량 입력이라고 해서 항상 거대한 multi-row INSERT가 최선은 아닙니다. 데이터 소스가 이미 파일 형태라면, 경우에 따라서는 더 적합한 적재 방식이 있을 수 있습니다.

예를 들어:

  • 애플리케이션 메모리에서 row를 하나씩 조립해 giant INSERT 생성
  • CSV나 dump를 staging 경로로 적재
  • staging table에 넣고 후처리 merge

는 운영 성격이 다릅니다.

특히 엄청 큰 데이터를 한 statement로만 밀어 넣으려 하면:

  • 쿼리 생성 자체가 무거워지고
  • 실패 시 재처리 범위가 커지고
  • 디버깅이 어려워질 수 있습니다

즉, “한 INSERT에 최대한 많이 우겨넣자”보다 현재 데이터 유입 경로에 맞는 ingest 방식이 무엇인지를 먼저 보는 편이 좋습니다.


10. 개선 뒤에 무엇을 비교해야 하나

batch insert 조정은 느낌으로 끝내면 위험합니다. 최소한 아래는 전후 비교가 있어야 합니다.

  • 초당 처리 row 수
  • 배치 1회당 실행 시간
  • commit 빈도
  • 에러율과 재시도 횟수
  • lock wait, deadlock, replication lag 변화

예를 들어 “배치 크기를 5배 늘렸더니 throughput은 20% 좋아졌지만 deadlock이 급증했다”면, 그건 단순 성공이 아닙니다.

즉, batch insert 최적화는 쓰기 throughput만 높이는 작업이 아니라 시스템 전체가 버티는 운영 지점을 찾는 작업입니다.


자주 하는 오해

1. 배치는 무조건 크게 할수록 빠르다

일정 지점 이후에는 transaction 길이, lock 영향, 실패 비용이 더 큰 문제가 될 수 있습니다.

2. 쓰기 성능은 DB 스펙만 높이면 해결된다

배치 크기, commit 전략, 인덱스 구조가 훨씬 큰 차이를 만들 때가 많습니다.

3. 읽기용 인덱스는 batch insert와 별 상관이 없다

모든 인덱스는 쓰기 때도 유지 비용을 만듭니다.

4. 배치가 백그라운드면 운영 영향이 거의 없다

같은 테이블이나 같은 디스크, 같은 replication 경로를 공유하면 충분히 사용자 요청에도 영향을 줄 수 있습니다.


FAQ

Q. batch size는 몇 건으로 시작하는 게 좋나요?

고정 정답은 없습니다. 실패 비용, 잠금 영향, statement 크기, 처리량을 같이 보며 중간 크기부터 측정하는 편이 안전합니다.

Q. INSERT IGNOREON DUPLICATE KEY UPDATE를 쓰면 더 편하지 않나요?

편할 수 있지만, duplicate check와 update 비용이 추가되고 데이터 의미도 바뀝니다. 성능과 데이터 품질을 같이 판단해야 합니다.

Q. batch insert가 느릴 때 무엇부터 봐야 하나요?

배치 크기, transaction 길이, index 수, duplicate 처리 방식, 라이브 트래픽 충돌 여부를 먼저 보면 좋습니다.


먼저 읽어볼 가이드

검색 유입이 많은 핵심 글부터 이어서 보세요.

광고