프로젝트를 진행하면서, DB베이스에 저장된 데이터셋에서 조건을 통해 필터링한 객체들을 랜덤 하게 불러와야 할 상황이 생겼고, 이를 구현하기 위해 다양한 방법들을 찾아보았다.
프로젝트 규모나 현재 상황에 따라 효율적인 방법이 달라지는 문제들의 최고의 호율을 찾는것이 정말 어려운것 같다.
order_by("?")
order_by("?")는 Django ORM에서 가장 간단하게 객체를 무작위로 정렬하는 방법이다. SQL 쿼리에서 ORDER BY RAND() 또는 ORDER BY RANDOM()과 유사하게 작동하며, 무작위 객체를 정렬한 후 필요한 개수 만큼 가지고 온다.
random_objects = MyModel.objects.order_by('?')[:10]
장점
매우 직관적이고 간단하게 사용할 수 있다.
모든 데이터베이스에서 작동한다.
단점
대규모 데이터셋에서 매우 비효율적이다. 데이터베이스가 모든 행을 무작위로 정렬해야하기 때문에 큰 테이블에서는 성능 저하가 발생할 수 있다.
사용 시나리오
작은 데이터셋에서 랜덤 객체를 선택할 때 적절하다.
order_by(Random())
PostgreSQL과 같이 특정 데이터베이스에서 제공하는 함수로 Django의 Random()함수를 사용해 SQL 레벨에서 무작위 값을 생성하고, 이를 기준으로 정렬할 수 있다. 이 방식은 order_by("?")보다 성능이 조금 더 최적화된 방법이다.
from django.db.models.functions import Random
random_objects = MyModel.objects.order_by(Random())[:10]
장점
PostgreSQL과 같은 데이터베이스에서 order_by("?")보다 효율적이다. SQL에서 직접 무작위값을 생성하여 정렬하기 때문이다.
단점
PostgreSQL에서는 효율적이지만, 모든 데이터베이스에서 사용할 수 있는것은 아니다.
대규모 데이터셋에서는 여전히 성능 문제가 있을 수 있다.
사용 시나리오
PostgreSQL과 같이 특정 데이터베이스에서 성능을 최적화 시키고자 할때 사용하면 좋다.
ID기반 랜덤 샘플링
order_by("?")와 같은 방식은 대규모 데이터셋에서 비효율적일 수 있기 때문에, ID 기반으로 무작위 객체를 선택하는 방법이 더 효율적이다. 먼저 데이터 베이스에서 모든 ID를 가지고 온 후, Python의 random.sample()나 random.choice()를 사용해 무작위로 ID를 선택하고, 그 ID에 해당하는 객체를 가지고 오는 방식이다.
import random
# 모든 ID를 가져옴
ids = list(MyModel.objects.values_list('id', flat=True))
# 무작위로 하나의 ID 선택
random_id = random.choice(ids)
# 선택한 ID로 객체 조회
random_object = MyModel.objects.get(id=random_id)
values_list는 Django ORM의 쿼리 최적화 기법으로 과거 정리한 글에 나와 있다.
장점
쿼리셋 전체를 메모리에 적재하지 않고, ID만 선택한 후 필요한 객체만 가져오기 때문에 성능이 훨씬 뛰어나다.
단점
ID를 가지고 오는 쿼리와 그 후 선택된 객체를 가지고 오는 쿼리가 필요하다.
대규모 데이터셋에서 모든 ID를 메모리에 적재하는 과정이 추가적인 메모리 사용을 유발할 수 있다.
사용 시나리오
대규모 데이터셋에서 랜덤 객체를 효율적으로 선택할 때 적합하다.
PK범위를 선택한 랜덤 객체 선택
PK가 연속적인 경우, PK의 범위를 이용해 무작위로 PK를 선택한 후, 그에 해당하는 객체를 조회하는 방법이다. 대규모 데이터셋에서 매우 효율적이다.
import random
from django.db.models import Max, Min
# PK의 최소값과 최대값 구하기
min_id = MyModel.objects.aggregate(min_id=Min('id'))['min_id']
max_id = MyModel.objects.aggregate(max_id=Max('id'))['max_id']
# 무작위로 ID 선택
random_id = random.randint(min_id, max_id)
# 선택한 ID로 객체 조회
random_object = MyModel.objects.filter(id=random_id).first()
장점
대규모 데이터셋에서 빠르고 효율적인 무작위 선택이 가능하다.
단점
삭제된 레코드로 인해 PK가 비연속적일 경우, 선택한 ID가 존재하지 않을 가능성이 있다.
존재하지 않은 ID를 선택할 경우 다시 쿼리를 실행해야할 수 도 있다.
사용 시나리오
연속적인 PK를 가진 데이터셋에서 대규모 데이터를 처리할 때 적합하다.
'BackEnd > Django, DRF' 카테고리의 다른 글
[Django] 테스트 : pytest-django, factory-boy, facker (2) | 2024.10.21 |
---|---|
[Django] 읽기 전용 데이터베이스 설정 및 테스트 (0) | 2024.10.11 |
[DRF] Django REST framework의 권한 관리 (0) | 2024.09.12 |
[Django] 캐시 API (0) | 2024.08.21 |
[Django] 장고에서의 페이징 처리 (0) | 2024.08.12 |