Django 쿼리 최적화 기법
Django는 ORM(Object Relational Mapping)을 제공하여 SQL 쿼리를 생성합니다. 하지만, 이러한 ORM은 SQL 쿼리의 최적화나 튜닝을 자동으로 수행하지 않습니다. 따라서 Django에서는 쿼리를 최적화하는 기법이 필요합니다. 아래는 Django에서 사용할 수 있는 쿼리 최적화 기법입니다.
1. select_related()
select_related() 메서드는 foreign key 관계에 있는 모델을 한 번의 쿼리로 가져올 수 있도록 최적화해줍니다. 이 메서드를 사용하면, 불필요한 SQL 쿼리를 줄이고 쿼리 실행 속도를 향상시킬 수 있습니다.
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
class Author(models.Model):
name = models.CharField(max_length=50)
age = models.IntegerField()
위의 모델에서 Book 모델과 Author 모델은 foreign key 관계에 있습니다. Book 모델에서 author 필드를 사용할 때 select_related() 메서드를 사용하여 Author 모델을 미리 가져올 수 있습니다.
books = Book.objects.select_related('author').all()
2. prefetch_related()
prefetch_related() 메서드는 many-to-many 관계나 reverse foreign key 관계에 있는 모델을 최적화할 수 있도록 해줍니다. 이 메서드를 사용하면, 쿼리 수행 시간을 단축할 수 있습니다.
class Category(models.Model):
name = models.CharField(max_length=100)
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
categories = models.ManyToManyField(Category)
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE)
content = models.TextField()
위의 모델에서 Post 모델과 Comment 모델은 foreign key 관계에 있으며, Post 모델과 Category 모델은 many-to-many 관계에 있습니다. Post 모델에서 categories 필드와 Comment 모델에서 post 필드를 사용할 때 prefetch_related() 메서드를 사용하여 최적화할 수 있습니다.
posts = Post.objects.prefetch_related('categories', 'comment_set').all()
3. annotate()
annotate() 메서드는 쿼리셋의 객체들에 대한 추가적인 정보를 제공합니다. 이 메서드를 사용하면, 모델 인스턴스의 필드 값을 계산하여 그 결과를 QuerySet 객체에 추가할 수 있습니다.
예를 들어, 아래와 같은 Book 모델이 있다고 가정해봅시다.
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
price = models.IntegerField()
annotate() 메서드를 사용하여 각 작가(author)가 출판한 책의 수와, 그 작가(author)가 출판한 책들의 가격(price)의 합을 구할 수 있습니다.
from django.db.models import Count, Sum
authors = Author.objects.annotate(
num_books=Count('book'),
total_price=Sum('book__price')
)
위 예제에서 Count() 함수는 각 작가의 책 수를 세는데 사용되었고, Sum() 함수는 각 작가의 책들의 가격 합계를 계산하는 데 사용되었습니다.
이렇게 annotate() 메서드를 사용하여 추가 정보를 QuerySet 객체에 추가하면, 데이터베이스에 대한 쿼리를 한 번만 실행할 수 있으므로 성능을 향상시킬 수 있습니다.
4. only()
only() 메서드는 필요한 필드만 가져와서 메모리 사용량을 줄일 수 있습니다. 이 메서드를 사용하면, 쿼리 실행 시간도 단축됩니다.
books = Book.objects.only('title', 'author').all()
5. defer()
defer() 메서드는 가져오지 않을 필드를 지정하여 메모리 사용량을 줄일 수 있습니다. 이 메서드를 사용하면, 불필요한 데이터를 가져오지 않으므로 쿼리 실행 속도가 향상됩니다.
books = Book.objects.defer('content').all()
6. raw()
raw() 메서드는 직접 SQL 쿼리를 작성할 수 있습니다. 이 메서드를 사용하면, Django ORM이 생성하는 쿼리보다 더 효율적인 쿼리를 작성할 수 있습니다.
books = Book.objects.raw('SELECT * FROM book WHERE author_id = %s', [author_id])
7. bulk_create()
bulk_create() 메서드는 여러 개의 모델 인스턴스를 한 번에 저장할 수 있습니다. 이 메서드를 사용하면, SQL 쿼리 수행 횟수를 줄일 수 있어서 쿼리 실행 속도가 향상됩니다.
books = [
Book(title='Book1', author_id=1),
Book(title='Book2', author_id=2),
Book(title='Book3', author_id=3),
]
Book.objects.bulk_create(books)
위에 소개한 기법들을 적절히 활용하면 Django 애플리케이션의 쿼리 실행 속도를 높일 수 있습니다.