آنچه در این مقاله میخوانید [پنهانسازی]
اگر Query های Django کند شدهاند، معمولا مشکل از استفاده اشتباه از ORM، تعداد زیاد Query ها یا طراحی نادرست ارتباط بین مدل هاست. کند شدن Query ها یکی از رایجترین مشکلات پروژههای Django محسوب میشود و اگر به موقع برطرف نشود، میتواند کل Performance پروژه را نابود کند. بیشتر مواقع با چند تغییر ساده میتوان سرعت پروژه را چند برابر بهتر کرد.
سرفصل های مقاله
- چرا Query های Django کند میشوند؟
- ORM در Django چگونه کار میکند؟
- رایجترین دلیل کند شدن Query ها در Django
- مشکل N+1 Query
- روش حل N+1 Query
- استفاده نکردن از prefetch_related
- گرفتن همه دادهها بدون نیاز
- استفاده اشتباه از count()
- Query داخل Loop
- چگونه Query های کند را پیدا کنیم؟
- استفاده از Django Debug Toolbar
- بررسی Query ها در Console
- استفاده از explain()
- اهمیت Index در Django
- چه زمانی باید Index اضافه کنیم؟
- اشتباهات رایج در Pagination
- کش کردن Query ها در Django
- چه زمانی ORM کافی نیست؟
- اشتباهات رایج برنامه نویسان Django
- استفاده زیاد از Serializer های سنگین
- استفاده نکردن از select_related
- Query گرفتن داخل Template
- روش جلوگیری از کند شدن Query ها
- همیشه Query ها را Monitor کنید
- Query های غیر ضروری را حذف کنید
- ساختار دیتابیس را اصولی طراحی کنید
- از Cache استفاده کنید
- جمع بندی
چرا 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 دریافت میشوند.
برای 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 افت میکند.
خیلی از پروژهها بدون این قابلیت نوشته میشوند و بعد از مدتی شدیدا کند میشوند.
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 پروژه به شکل محسوسی بهتر میشود.





