-/Pandas

Pandas: 대량 데이터 DB 적재와 성능

106. 2026. 9. 3. 17:47

먼저 알아야 할 핵심 개념

대량 적재(Bulk Insert)는 데이터를 한 행씩 저장하지 않고 여러 행을 묶어서 DB에 저장하는 방식이다.

한 행씩 INSERT하면 DB에 계속 요청을 보내야 하므로 데이터가 많을수록 비효율적이다. 따라서 여러 데이터를 묶어서 보내 DB와의 왕복 횟수를 줄이는 것이 핵심이다.

90,000행

1행씩 전송      → 최대 90,000번의 실행/요청 ❌
5,000행씩 묶기  → 훨씬 적은 횟수로 처리 ⭕
 

대량 적재 방법

1. execute() 반복

  • 한 행씩 SQL을 실행하므로 대량 적재에 비효율적일 수 있다.
  • 가장 느리다.

2. executemany()

  • 같은 SQL에 여러 데이터를 전달해 처리한다.

3. to_sql()

  • DataFrame을 DB 테이블에 바로 저장한다. -> 내부적으로는 executemany를 사용한다.
  • 직접 insert문을 작성하지 않아도 된다.
  • if_exists : "append"(뒤에 붙임) / "replace"(테이블 지우고 새로만듬) / "fail"(실패처리)

4. to_sql(method="multi")

  • 여원하는 개수만큼 청킹해서 INSERT시킬 수 있다.

df.to_sql(
    "daily_price",
    engine,
    if_exists="append",
    method="multi",
    chunksize=5000
)
 

대량 데이터는 한 행씩 처리하기보다 여러 행을 묶어서 처리하는 것이 일반적으로 효율적이다.


DB 왕복 비용

왕복 비용(Round Trip Cost)은 프로그램이 DB에 요청을 보내고 응답을 받는 데 드는 비용이다.

DB가 같은 컴퓨터에 있으면 왕복 비용이 작아서 차이를 체감하기 어려울 수 있다.

하지만 실무에서는 애플리케이션과 DB가 다른 서버에 있는 경우가 많아 네트워크 비용이 추가된다.

Application
    ↓ 요청
   DB
    ↓ 응답
Application
 

이 작업이 수만 번 반복되면 시간이 누적되므로 가능하면 여러 데이터를 묶어서 요청 횟수를 줄인다.


⭐ Chunking과 chunksize

Chunking은 대량 데이터를 일정한 크기로 나누어 처리하는 방식이며, chunksize는 한 번에 처리할 행의 개수다.

예를 들어 90,000행을 chunksize=5000으로 처리하면 데이터를 5,000행 단위로 나누어 적재한다.

왜 그냥 90,000행을 한꺼번에 보내지 않을까?

너무 작게 묶음
→ 왕복 횟수 증가

적당하게 묶음
→ 왕복 횟수와 처리 부담의 균형

너무 크게 묶음
→ 메모리·SQL 크기·트랜잭션 부담 증가
 

따라서 chunksize=5000 같은 값은 절대적인 정답이 아니라 환경에 따라 테스트해서 결정해야 한다.


max_allowed_packet

max_allowed_packet은 MySQL이 한 번에 주고받을 수 있는 패킷의 최대 크기다.

너무 많은 데이터를 하나의 요청으로 묶으면 이 제한을 초과하여 오류가 발생할 수 있다.

이런 경우 한 번에 보내는 데이터가 작아지도록 chunksize를 줄일 수 있다.

한 번에 너무 많은 데이터
        ↓
max_allowed_packet 초과 가능
        ↓
chunksize ↓
        ↓
여러 번 나누어 전송
 

⭐ 인덱스와 INSERT 성능

인덱스는 조회를 빠르게 해주지만, INSERT할 때 인덱스도 갱신해야 하므로 적재 비용이 증가할 수 있다.

데이터 하나가 추가되면 DB는 데이터만 저장하는 것이 아니라 관련 인덱스도 관리해야 한다.

INSERT
  ├─ 데이터 저장
  └─ 인덱스 갱신
 

따라서 인덱스가 많을수록 대량 적재가 느려질 수 있다.

UNIQUE(code, date)
 

하지만 중복 방지를 위해 필요한 제약조건은 단순히 속도를 높이기 위해 제거하면 안 된다.

이건 속도를 위한게 아니라 중복 차단을 위한 것. 목적이 다르다~


참고: itertuples()

for row in df.itertuples(index=False):
    ...
 

DataFrame을 반드시 행 단위로 반복해야 한다면 iterrows()보다 일반적으로 itertuples()가 빠르다.

다만 대량 DB 적재에서는 행 반복 자체를 줄이고 executemany()나 to_sql()처럼 묶어서 처리하는 방법을 우선 고려하는 것이 좋다.


최종 정리

개념 핵심
Bulk Insert 여러 행을 묶어서 DB에 저장
왕복 비용 DB 요청·응답이 반복되면서 발생하는 비용
executemany() 같은 SQL로 여러 데이터를 처리
to_sql() DataFrame을 DB에 직접 저장
Chunking 대량 데이터를 일정 크기로 나누어 처리
chunksize 한 번에 처리할 행 개수
max_allowed_packet MySQL의 한 번에 주고받을 수 있는 패킷 크기 제한
인덱스 조회는 빨라지지만 INSERT 시 갱신 비용 발생

대량 데이터는 한 행씩 보내지 말고 묶어서 보낸다.

다만 무조건 크게 묶는 것이 좋은 것은 아니다.

너무 작으면 DB 왕복이 많아지고, 너무 크면 메모리·트랜잭션·전송 크기의 부담이 커지므로 Chunking을 이용해 적절한 크기로 나누는 것이 핵심이다.