1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Django クエリの最適化とパフォーマンス向上について

Posted at

はじめに

DjangoのORM(Object-Relational Mapping)は、データベースとのやり取りを簡単にしてくれる強力なツールですが、適切に設計・運用しないとパフォーマンスの低下を招くことがあります。本記事では、Django ORM関連を個人的にまとめたものです。


1. select_related()prefetch_related() の使い分け

Djangoでは、関連するモデルのデータを取得する際に select_related()prefetch_related() を適切に使うことで、N+1問題を回避できます。

select_related()(JOINを利用)

ForeignKeyOneToOneField のような1対1または多対1のリレーションでは、select_related() を使用してデータを一括取得できます。

例:

class Author(models.Model):
    name = models.CharField(max_length=100)

class Book(models.Model):
    title = models.CharField(max_length=200)
    author = models.ForeignKey(Author, on_delete=models.CASCADE)

# クエリの最適化
books = Book.objects.select_related('author').all()
for book in books:
    print(book.title, book.author.name)  # 1回のクエリで済む

prefetch_related()(別クエリで取得)

ManyToManyFieldreverse ForeignKey では、prefetch_related() を使用すると、クエリ回数を減らしつつ効率的にデータを取得できます。

例:

class Tag(models.Model):
    name = models.CharField(max_length=50)

class Post(models.Model):
    title = models.CharField(max_length=200)
    tags = models.ManyToManyField(Tag)

# クエリの最適化
posts = Post.objects.prefetch_related('tags').all()
for post in posts:
    print(post.title, [tag.name for tag in post.tags.all()])  # 効率的にデータ取得

2. values()only() を活用して不要なデータを除外

テーブルの全フィールドを取得すると、不要なデータの転送コストが増えるため、必要なフィールドのみ取得するのが望ましいです。

values() を活用(辞書型でデータを取得)

例:

books = Book.objects.values('title', 'author__name')

これにより、オブジェクトではなく辞書型でデータを取得でき、メモリ使用量が抑えられます。

only() を活用(不要なフィールドの取得を制限)

例:

books = Book.objects.only('title', 'author')

これにより、不要なフィールドを除外し、データベースからの転送コストを削減できます。


3. F() オブジェクトを活用したデータベース側での計算

Python側でデータを取得して計算すると無駄な処理が増えるため、可能な限りデータベース側で計算処理を行うのが望ましいです。

例:

from django.db.models import F

# 価格を10%増加させる
Book.objects.update(price=F('price') * 1.1)

このように F() を使うことで、Python側でループ処理を回さずにデータベースで計算を実行できます。


4. Q() オブジェクトを活用した複雑な条件検索

複数の条件を組み合わせる際に、Q() を利用すると効率的なクエリを記述できます。

例:

from django.db.models import Q

books = Book.objects.filter(Q(price__gte=1000) & Q(author__name__icontains='John'))

これにより、OR 条件や AND 条件を柔軟に組み合わせることができます。


5. クエリのキャッシュ戦略

同じクエリを何度も実行するとパフォーマンスが低下するため、適切にキャッシュを活用するのが重要です。

django-cacheops を活用したクエリキャッシュ

インストール:

pip install django-cacheops

設定:

CACHEOPS = {
    'myapp.book': {'ops': 'all', 'timeout': 60*15},  # 15分間キャッシュ
}

これにより、頻繁にアクセスされるデータをキャッシュし、データベースの負荷を軽減できます。


6. バルク処理の活用

データを一括更新・挿入する場合、ループ処理ではなくバルク処理を利用すると、パフォーマンスが向上します。

bulk_create() を利用した一括挿入

例:

books = [Book(title=f'Book {i}', price=1000) for i in range(1000)]
Book.objects.bulk_create(books)

通常の .save() を使うと1000回のクエリが実行されますが、bulk_create() を使うと1回のクエリで済みます。

bulk_update() を利用した一括更新

例:

books = Book.objects.all()
for book in books:
    book.price += 500
Book.objects.bulk_update(books, ['price'])

これにより、price フィールドの更新が1回のクエリで完了します。


まとめ

Django ORMのクエリ最適化には、いくつかのポイントがあります。

  • select_related() / prefetch_related() の適切な使い分け(N+1問題の回避)
  • values()only() で不要なデータを除外
  • F() オブジェクトを使ってデータベース側で計算を実行
  • Q() オブジェクトで複雑な条件を効率的に検索
  • クエリキャッシュを適用してパフォーマンスを向上
  • バルク処理を活用してデータの一括更新・挿入を最適化
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?