bulk_create와 bulk_update로 대량 데이터 효율적으로 처리하기

Django ORM의 bulk_create와 bulk_update를 사용하여 대량 데이터 처리 시 발생하는 성능 문제를 해결하고, 단일 쿼리로 효율적인 데이터베이스 연산을 수행하는 방법

bulk 연산이 필요한 이유

일반적인 for 루프와 save() 방식은 각 객체마다 별도의 INSERT 쿼리를 생성하여 데이터베이스와 통신. 10,000개 데이터 처리 시 10,000번의 데이터베이스 왕복 발생으로 심각한 성능 저하 야기

bulk 연산은 여러 객체를 단 하나의 SQL 쿼리로 묶어서 처리하여 데이터베이스 통신 횟수를 획기적으로 감소

bulk_create() 사용법

여러 개의 객체를 데이터베이스에 한 번의 쿼리로 삽입하는 메서드

# 효율적인 방식
products_to_create = [
    Product(name=f"상품 {i}", price=i * 100) for i in range(10000)
]
Product.objects.bulk_create(products_to_create)

주요 파라미터

  • batch_size: 한 번의 쿼리에 포함할 객체 수 지정. 메모리 부족 방지와 데이터베이스 제한 회피

  • ignore_conflicts: UNIQUE 제약 조건 위반 시 해당 데이터 무시하고 삽입 계속 (PostgreSQL, SQLite 지원)

주의사항

  1. save() 메서드 미호출: 모델의 커스텀 save() 로직이 실행되지 않음

  2. 시그널 미발생: pre_save, post_save 시그널이 트리거되지 않음

  3. PK 자동 할당: MySQL/MariaDB나 구버전 Django에서는 생성된 객체의 PK가 자동으로 채워지지 않음

bulk_update() 사용법

여러 객체의 특정 필드를 한 번의 쿼리로 업데이트하는 메서드

# 업데이트할 객체 조회 및 수정
products_to_update = list(Product.objects.filter(category="전자제품"))
for product in products_to_update:
    product.price *= 1.1

# bulk_update로 한 번에 업데이트
Product.objects.bulk_update(products_to_update, fields=['price'])

주요 파라미터

  • fields: 업데이트할 필드명 리스트 (필수)

  • batch_size: 대량 객체 처리 시 적절한 크기로 분할

주의사항

  1. save() 메서드와 시그널 미호출: bulk_create와 동일한 제약

  2. PK 필수: 업데이트 대상 객체는 반드시 PK를 보유해야 함

  3. 관계 필드 제한: ForeignKey는 필드명이 아닌 실제 컬럼명(예: category_id) 사용 필요

활용 시나리오

  • 초기 데이터 세팅: 테스트나 프로덕션 환경의 기본 데이터 대량 입력

  • CSV/Excel 파일 처리: 대용량 파일 데이터의 일괄 업로드 및 저장

  • 데이터 마이그레이션: 기존 데이터의 새로운 스키마 이전

  • 주기적 데이터 동기화: 외부 API 데이터의 정기적 반영

Last updated