IT&개발

Django 쿼리 최적화 기법

KimGPT 2023. 3. 1. 00:37

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 애플리케이션의 쿼리 실행 속도를 높일 수 있습니다.