اگر Query های Django کند شده‌اند، معمولا مشکل از استفاده اشتباه از ORM، تعداد زیاد Query ها یا طراحی نادرست ارتباط بین مدل هاست. کند شدن Query ها یکی از رایج‌ترین مشکلات پروژه‌های Django محسوب می‌شود و اگر به موقع برطرف نشود، می‌تواند کل Performance پروژه را نابود کند. بیشتر مواقع با چند تغییر ساده می‌توان سرعت پروژه را چند برابر بهتر کرد.

چرا Query های Django کند می‌شوند؟

کندی Query ها معمولا زمانی اتفاق می افتد که:

  • تعداد درخواست به دیتابیس زیاد شود
  • Query های غیر ضروری اجرا شوند
  • داده‌های سنگین بدون بهینه سازی خوانده شوند

خیلی از پروژه‌ها در ابتدا سریع هستند اما بعد از بزرگ شدن دیتابیس ناگهان کند می‌شوند.

دلیل اصلی هم معمولا Query های اشتباه است.

ORM در Django چگونه کار می‌کند؟

Django ORM واسطه ای بین Python و دیتابیس است.

مثلا:

users = User.objects.all()

این دستور پشت صحنه تبدیل به SQL می‌شود.

مزیت ORM این است که:

  • توسعه سریع تر می‌شود
  • کد خواناتر می‌شود
  • مدیریت دیتابیس ساده تر می‌شود

اما اگر اشتباه استفاده شود، Performance پروژه شدیدا افت می‌کند.

رایج‌ترین دلیل کند شدن Query ها در Django

مشکل N+1 Query

این مشکل یکی از معروف‌ترین مشکلات Django است.

مثلا:

posts = Post.objects.all()

for post in posts:
    print(post.author.username)

در ظاهر فقط یک Query داریم اما عملا:

  • یک Query برای Post ها
  • و یک Query جدا برای هر Author

اجرا می‌شود.

اگر 100 پست داشته باشید:

  • 101 Query اجرا می‌شود

این موضوع Performance را نابود می‌کند.

روش حل N+1 Query

برای ForeignKey باید از select_related استفاده شود:

posts = Post.objects.select_related('author')

حالا همه اطلاعات در یک Query دریافت می‌شوند.

استفاده نکردن از prefetch_related

برای ManyToMany و Reverse Relation باید از prefetch_related استفاده شود.

مثلا:

products = Product.objects.prefetch_related('categories')

این کار تعداد Query ها را به شدت کاهش می‌دهد.

گرفتن همه داده‌ها بدون نیاز

بعضی پروژه‌ها کل جدول را دریافت می‌کنند:

users = User.objects.all()

در حالی که فقط چند فیلد لازم دارند.

روش بهتر:

users = User.objects.only('id', 'username')

یا:

users = User.objects.values('id', 'username')

استفاده اشتباه از count()

این اشتباه خیلی رایج است:

len(User.objects.all())

این دستور همه داده ها را از دیتابیس می خواند.

روش درست:

User.objects.count()

Query داخل Loop

این اشتباه در پروژه های واقعی خیلی دیده می شود.

مثلا:

for user in users:
    orders = Order.objects.filter(user=user)

این یعنی برای هر User یک Query جدید اجرا می شود.

در پروژه های بزرگ این فاجعه است.

چگونه Query های کند را پیدا کنیم؟

استفاده از Django Debug Toolbar

یکی از بهترین ابزارها برای تحلیل Query هاست.

نصب:

pip install django-debug-toolbar

این ابزار نشان می دهد:

  • چند Query اجرا شده
  • هر Query چقدر زمان برده
  • کدام Query کند است

برای پیدا کردن Bottleneck فوق العاده کاربردی است.

بررسی Query ها در Console

می توانید Query ها را چاپ کنید:

print(queryset.query)

این کار SQL واقعی را نمایش می دهد.

استفاده از explain()

برای تحلیل Performance:

queryset.explain()

این دستور نحوه اجرای Query در دیتابیس را نشان می دهد.

اهمیت Index در Django

اگر روی فیلدهای مهم Index نداشته باشید، دیتابیس مجبور می شود کل جدول را جستجو کند.

مثلا:

email = models.EmailField(db_index=True)

این موضوع مخصوصا در جدول های بزرگ خیلی مهم است.

چه زمانی باید Index اضافه کنیم؟

برای فیلدهایی که:

  • زیاد Search می شوند
  • Filter دارند
  • Sort می شوند
  • Join دارند

استفاده از Index ضروری است.

اشتباهات رایج در Pagination

بعضی پروژه ها هزاران رکورد را یکجا لود می‌کنند.

مثلا:

posts = Post.objects.all()

این کار RAM و CPU را شدیدا مصرف می کند.

روش بهتر استفاده از Pagination است.

مثلا:

Paginator(posts, 20)

کش کردن Query ها در Django

اگر Query ها سنگین هستند، Cache خیلی کمک می کند.

مثلا با Redis:

CACHE_TTL = 60 * 15

این کار فشار روی دیتابیس را کم می کند.

چه زمانی ORM کافی نیست؟

گاهی Query خیلی پیچیده است.

در این شرایط می توان از Raw SQL استفاده کرد:

User.objects.raw('SELECT * FROM auth_user')

اما باید با احتیاط استفاده شود.

چون:

  • امنیت مهم می‌شود
  • نگهداری سخت تر می‌شود
  • وابستگی به دیتابیس بیشتر می‌شود

اشتباهات رایج برنامه نویسان Django

استفاده زیاد از Serializer های سنگین

در Django REST Framework بعضی Serializer ها Query اضافی تولید می‌کنند.

اگر Nested Serializer زیاد باشد، Performance افت می‌کند.

استفاده نکردن از select_related

خیلی از پروژه‌ها بدون این قابلیت نوشته می‌شوند و بعد از مدتی شدیدا کند می‌شوند.

Query گرفتن داخل Template

این کار اشتباه بزرگی است:

{{ user.orders.count }}

اگر تعداد زیادی User وجود داشته باشد، تعداد زیادی Query اجرا می‌شود.

روش جلوگیری از کند شدن Query ها

همیشه Query ها را Monitor کنید

خیلی از مشکلات از ابتدا مشخص نیستند.

Monitoring منظم کمک می‌کند قبل از بحرانی شدن مشکل را پیدا کنید.

Query های غیر ضروری را حذف کنید

هر Query هزینه دارد:

  • CPU
  • RAM
  • زمان پاسخ

پس فقط داده‌ای را بگیرید که واقعا نیاز دارید.

ساختار دیتابیس را اصولی طراحی کنید

طراحی اشتباه Model ها بعدا باعث Query های سنگین می‌شود.

رابطه ها را از اول اصولی طراحی کنید.

از Cache استفاده کنید

بعضی داده‌ها لازم نیست هر بار از دیتابیس خوانده شوند.

Cache می‌تواند فشار زیادی را کم کند.

جمع بندی

افزایش سرعت Query در Django بیشتر از هر چیز به نحوه استفاده از ORM بستگی دارد. مشکلاتی مثل N+1 Query، Query داخل Loop و استفاده اشتباه از Relationship ها معمولا دلیل اصلی کند شدن پروژه هستند. اگر Query ها را بهینه کنید و از ابزارهایی مثل select_related، prefetch_related و Debug Toolbar استفاده کنید، Performance پروژه به شکل محسوسی بهتر می‌شود.