해결) 장고 bulk_update의 메모리 누수 문제(django orm bluk_update method memory leak)

작성자: [관리자] 하얀설표

2025.07.12 15:10 (KST) 작성됨

2025.07.12 15:13 (KST) 수정됨






(07.12) 수정됨.

문제 상황

장고에서 다수 object의 값들을 한 번에 update하기 위해 .bulk_update() method를 사용.

그런데 .bulk_create()와는 다르게 메모리 사용량이 200mb, 300mb, 500mb 순으로 늘어나더니 이윽고 2gb의 메모리를 잡아먹는 현상이 발생함

그런 와중에도 bulk update 작업이 끝나지 않아 메모리 부족으로 인한 성능 다운을 경험하게 됨.

 

해결 방법

장고 orm이 아닌 raw query를 작성해 실행한다.

 

설명

 

 

문제의 원인은 장고의 bulk update 작동 방식에 있는 듯 하다.

이유는 모르겠으나, object_list를 준비하고 bulk update에 전달을 하면 bulk update에서 무언가의 이유로 메모리 사용량이 급증하는 현상이 발생했다.

 

bulk update의 코드를 보면 transaction을 사용하는데, 아마 이것이 영향을 주는 것이 아닌가 생각된다.

 

https://github.com/django/django/blob/main/django/db/models/query.py#L898

    def bulk_update(self, objs, fields, batch_size=None):
        ...
        updates = []
        for batch_objs in batches:
            update_kwargs = {}
            for field in fields:
                when_statements = []
                for obj in batch_objs:
                    attr = getattr(obj, field.attname)
                    if not hasattr(attr, "resolve_expression"):
                        attr = Value(attr, output_field=field)
                    when_statements.append(When(pk=obj.pk, then=attr))
                case_statement = Case(*when_statements, output_field=field)
                if requires_casting:
                    case_statement = Cast(case_statement, output_field=field)
                update_kwargs[field.attname] = case_statement
            updates.append(([obj.pk for obj in batch_objs], update_kwargs))
        rows_updated = 0
        queryset = self.using(self.db)
        with transaction.atomic(using=self.db, savepoint=False):
            for pks, update_kwargs in updates:
                rows_updated += queryset.filter(pk__in=pks).update(**update_kwargs)
        return rows_updated

 

이렇게 급격한 메모리 증가 현상을 겪고 있고, 문제를 해결하기 위한 방법을 2가지 생각할 수 있었다.

  1. RAW QUERY를 작성하여 실행하기
  2. 기존 데이터를 삭제 후 bulk create하기

 

단순히 머리쓰는 시간, 작업 시간, db에 반복적으로 쓰여지는 것들을 신경쓰고 싶지 않다면 2안을 사용하는게 편하다.

어차피 기존 데이터를 지우고 새로 입력하는 것과 수정하는 것은 크게 차이가 없기 때문이다.

물론, 쓰기 과정에서 에러가 발생하지 않았을 때의 이야기다.

 

이런 것들이 걱정된다면 RAW QUERY를 직접 작성해야 한다.

작성해야하는 쿼리는 다음과 같다.

UPDATE product
SET price = data.price
FROM (VALUES
    (1, 89.91),
    (2, 71.91),
    (3, 53.91)
) AS data(id, price)
WHERE product.id = data.id;

 

장고 orm으로 RAW QUERY 대체하기

방법은 조금 복잡하긴 하지만 쿼리를 직접 작성하는 것에 자신이 없을 때는 장고 orm을 사용해서 대체하는 것도 가능하다.

Case와 When을 사용하는 것이다.

 

다음과 같이 Case와 When을 혼합해 사용한다면 한 번의 쿼리 요청을 복수의 object 필드값을 수정하는 것이 가능하다.

from django.db.models import Case, When, Value, F, IntegerField

Product.objects.update(
    type=Case(
        When(category="snack", then=Value(100)),
        When(category="drink", then=Value(50)),
        default=F("type"),
        output_field=IntegerField()
    )
)

 

 

 






추천 (0)


글 목록

댓글을 달 수 없는 게시물입니다.


"분류없음" 카테고리의 #Python, #Django 관련 게시물

분류없음
예제)특정 조합이 리스트 요소에 반드시 포함되어야 한다는 사실만 알 때의 조건식
수정 08.20 | [관리자] 하얀설표
👍 0
#Python, #예제
🗨️ 0
썸네일
분류없음
주식시장 개장일과 휴장일 정보를 간단하게 가져오는 방법(엑셀, 파이썬)
수정 07.20 | [관리자] 하얀설표
👍 0
#Python, #주식
🗨️ 0
분류없음
거지같은 subQuery와 outerRef
수정 06.29 | [관리자] 하얀설표
👍 0
#Django
🗨️ 0
분류없음
장고) pk 리스트에 등록된 순서대로 오브젝트를 가져오기 예제
작성 06.29 | [관리자] 하얀설표
👍 0
#Django
🗨️ 0
분류없음
해결) django.db.utils.OperationalError: database is locked
수정 06.18 | [관리자] 하얀설표
👍 0
#Python, #에러해결, #Django
🗨️ 0
썸네일
분류없음
장고 예제) 게시판 기능 만들기
수정 06.17 | [관리자] 하얀설표
👍 0
#Django, #Django 예제
🗨️ 0
분류없음
장고 튜토리얼) settings.py 분리하기
수정 06.15 | [관리자] 하얀설표
👍 0
#Django, #Django 튜토리얼
🗨️ 0
썸네일
분류없음
장고 튜토리얼) 패키지 설치와 프로젝트 설치 및 실행 방법
수정 06.15 | [관리자] 하얀설표
👍 0
#Django, #Django 튜토리얼
🗨️ 0
분류없음
장고 튜토리얼) 관리자 계정과 일반 계정 만드는 방법
수정 06.15 | [관리자] 하얀설표
👍 0
#Django, #Django 튜토리얼
🗨️ 0
분류없음
장고 튜토리얼) makemigrations와 migrate 알아보기(migration과 database)
수정 06.15 | [관리자] 하얀설표
👍 0
#Django, #Django 튜토리얼
🗨️ 0