Django ORM 최적화

강민성·2022년 1월 14일
0

내가 만든 Django ORM(Queryset API)의 성능 테스트
실제 어느부분에서 쿼링이 이뤄지는지 코드를 실행하는 레벨에서 확인하거나, Shell 상에서도 해볼 수 있음
보통의 장고 프로젝트(페이지가 존재하는 경우)에는 django-debugger-toolbar라는 것을 사용
하지만 우리는 API를 만들뿐, 이를 위해 페이지를 구성해서 디버거를 실행시키는 것은 자원 낭비

로깅하는 방법

서버 실행시 코드 실행때마다 쿼리가 찍히게 설정.
예시)

#settings.py
LOGGING = {
    'disable_existing_loggers': False,
    'version': 1,
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'level': 'DEBUG',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'level': 'DEBUG',
            'propagate': False,
        },
    },
}

파일로 저장하는 방법

아래와 같이 옵션을 추가하면 디버깅을 통해 볼 수 있음
예시)

#settings.py

LOGGING = {
    'disable_existing_loggers': False,
    'version': 1,
    'formatters': {
         'verbose': {
            'format': '{asctime} {levelname} {message}',
            'style': '{'
        },
    },
    'handlers': {
        'console': {
            'class'     : 'logging.StreamHandler',
            'formatter' : 'verbose',
            'level'     : 'DEBUG',
        },
        'file': {
            'level'     : 'DEBUG',
            'class'     : 'logging.FileHandler',
            'formatter' : 'verbose',
            'filename'  : 'debug.log',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers' : ['console','file'],
            'level'    : 'DEBUG',
            'propagate': False,
        },
    },
}

Queryset의 속성

Lazy Loading

쿼리셋.filter() 등을 쓴다고 해서 그 때 데이터베이스를 호출하지 않음
(대신 queryset.query 안에 호출 조건들의 속성값들을 저장)
그 뒤 쿼리셋.filter()[0], 반복문, list 등을 쓸 때 데이터베이스에 호출(쿼리셋을 평가)을 보냄

Caching

리스트 등을 사용하면 쿼리셋을 미리 호출해서 결과값에 저장두고 그 다음에 그 결과값에서 쿼리셋을 꺼내 쓸 수 있음
(두 번 호출하지 않고 한 번만 호출 가능)

N+1 problem

1. 정참조

반복문을 사용할 때 테이블 바깥에 있는 다른 테이블의 값을 바로 정참조하여 가져오면 연산이 너무 많이 발생
--> Eager Loading을 사용하여 해결

2. 역참조

반복문을 사용할 때 테이블 바깥에 있는 다른 테이블의 값을 바로 역참조하여 가져오면 연산이 너무 많이 발생
--> Eager Loading을 사용하여 해결

3.

prefetch_related를 사용했는데도 연산이 많은 경우
--> Prefetch를 사용하여 해결

Eager Loading(N+1 problem 해결)

1. 정참조

테이블.objects.all().select_related('다른테이블명') 을 사용하여 해결 가능

2. 역참조

테이블.objects.all().prefetch_related('다른테이블명')을 사용하여 해결 가능

  • 원리의 차이: select_related는 테이블을 join해서 한 번에 가져오고, prefetch_related는 루트 쿼리(테이블.objects.all())를 날리고 추가 쿼리를 날림

3.

테이블.objects.all().prefetch_related(Prefetch('다른테이블명', to_attr='가져올속성명1'),Prefetch('다른테이블명', to_attr='가져올속성명2'))을 사용하여 해결 가능
예시)

loadtest

loadtest -n 횟수 url

profile
Back-end Junior Developer

0개의 댓글