تکه کد ORM جنگو – Queryset slicing – سوال مصاحبه

آیا ORM جنگو با اجرای کد زیر به دیتابیس query میزند؟ اگر آره چند Hit دیتابیس داریم؟

ORM جنگو lazy است یعنی در هر حالتی به دیتابیس دسترسی نمیگیرد مگر اینکه بخواهد queryset را کامل از دیتابیس بگیرد. درحالتی که از slicing پایتون برای یک queryset استفاده می کنیم ORM یک queryset جدید برمیگردونه و اون رو اجرا نمیکنه. یعنی توی خط اول به دیتابیس Hit نداریم. قاعدتا باید توی خط دوم هم همین باشه اما slicing پایتون با step استثنا هست و توی خط دوم یه Hit به دیتابیس داریم.

مشکل N+1 – سوال مصاحبه (دوم)

کد زیر چند Hit دیتابیس دارد؟

همچنان N+2!!!

حتی با وجود اینکه از prefetch_related استفاده شده است دوباره در داخل حلقه به N دسترسی مجدد به دیتابیس وجود دارد. چرا که prefetch_related بالا برای Pizaa.objects.all بوده ولی پایین از filter استفاده کردیم که ORM جنگو دسترسی بالایی را در نظر نمیگیرد و دوباره query میزند. اتفاقا بدتره چرا که یه query بالا هم اضافی هست. راه حل زیر درست میشه:

یا

مثال پایتون – تکه کد – سوال مصاحبه

توی یه مصاحبه خروجی یک تکه کد پایتون از من خواسته شد و تحلیلش. من اون کد رو کامل تر کردم که شده کد زیر:

خوب خروجی کد زیر:

خروجی کد یه جوریه که انگار پایتون شیشه زده :)))) حالا چرا؟

چند تا بحثه:

1.توابع توی پایتون خود آبجکت رو به عنوان پارامتر میگیرن نه کپی اونها رو:

ینی چی؟ وقتی ما به تابع test یه لیست و یه integer پاس دادیم، کپی اون لیست و integer به تابع داده نشده بلکه خود اون آبکت داده شده یعنی reference اون داده شده پس اگه توی تابع test اون پارامتر رو تغییر بدی خود اون آبجکت رو تغییر دادی. به بیان ساده تر اون l1 همون a هست نه کپی اون. انگار توی خانه حافظه اون لیست یه برچست جدید روش خورده و حالا با اون برچست جدید صداش میزنیم توی test. پس پارامترها توی توابع پایتون اینجوری پاس داده میشن.

2.توی پایتون list یک mutable هست و integer یک immutable

سوالی که پیش میاد اینه که چرا c تغییر نکرد پس؟ مگه اونجا هم دقیقا همون c را ندادیم به test؟ چرا اون c که توی test هست دقیقا همونی هست که توی main هست اما بعد از اینکه یه مقدار به c اضافه کردیم c+=1 دیگه از اونجا به بعد همون c قبلی نیست. این یه بحث جدیده به اسم mutable و immutable بودن توی پایتون.

توی پایتون list یک mutable هست یعنی قابل تغییره اما یک integer یک immutable یعنی غیرقابل تغییر است بنابراین وقتی c+=1 میکنیم اون c قبلی که برابر با 1 بود از حافظه پاک میشه و یه مقدار جدید توی جای جدیدی از حافظه ساخته میشه با مقدار 2 و به c وصل میشه (توی این لینک توضیحش هست)

یه مثال جالب توی پایتون – تکه کد

خروجی تکه کد زیر چیه؟

خروجی این کد 2 عدده که با هم متفاوت هستن یعنی توی پایتون اگر یه متغیر integer یا string تغییر کنن (مثلا یه واحد بهشون اضافه بشه یا یه کاراکترش حذف بشه) دیگه اون متغیر قبلی نیست و عملا اون قسمت حافظه که متغیر قبلی گرفته آزاد میشه و یه قسمت جدید allocate میشه و مقدار جدید توی اون ذخیره میشه و به a بایند میشه. اینه که گفته میشه توی پایتون integer و string ها immutable هستن.

توی مثال بالا وقتی a+=1 صدا اجرا میشه متغیر قبلی پاک میشه و یه متغیر جدید توی حافظه ساخته میشه با آدرس جدید و مقدار جدید ینی 2 بهش داده میشه و این خانه حافظه به a داده میشه.

حالا کد زیر چطور میشه؟

خروجی این کد همینطور که میبینید با قبلی فرق داره این بار خروجی 1 عدده. چون list توی پایتون mutable هست و وقتی یک لیست رو تغییر میدیم دیگه لیست جدید ساخته نمیشه همون قبلی توی همون جای قبلی حافظه تغییر کرده.

لینک های بدرد بخور:

لینک 1

لینک 2

مشکل N+1 – سوال مصاحبه

N+1 query problem وقتی است که برنامه دیتایی که با یک query می توانست به دست بیاره رو N بار بیشتر query بزنه. طبیعتا مشکل پرفورمنسیه.

توی مدیوم یه مقاله هست که این مشکل رو با یک مثال توضیح داده که باعث میشه خیلی خوب مشکل N+1 query رو بفهمیم.

فرض کنید که میخاید کیک درست کنید. یخچال توی آشپزخونه نیست و توی اتاق زیر شیروانیه بنابراین برای برداشتن هر چیزی از تو یخچال باید پله ها رو بالا بریم. شما توی آشپزخونه هستید ولی کتاب شیرینی پزی توی اتاق خوابه.

اول میرید از اتاق خواب کتاب آشپزی رو میارید به آشپزخونه بعد میبینید گفته که به 200 گرم آرد نیاز دارید. میرید اتاق زیرشیروانی و آرد رو میارید. دستور پخت نوشته 3 عدد تخم مرغ، دوباره میرید اتاق زیرشیروانی و 3 تا تخم مرغ میارید. دوباره میبینید که نوشته یه لیوان شیر و ….

میبینید که با این روش شما N بار اضافه رفتید بالا در حالی که همون دفعه اول که رفتید کتاب آشپزی رو بیارد بهتر بود همه وسایل مورد نیاز رو هم بیارید.

حالا یه مثال از ORM جنگو ببینیم که این مشکل رو چطور حل میکنه.

فرض کنید که یه مدل Post داریم که هر Post یه Author داره و طبیعتا هر Author میتونه چند تا Post داشته باشه. رابطه 1->N هست. حالا میخایم پست ها و نویسنده هاشون رو چاپ کنیم.

توی کد بالا مشکل N+1 وجود داره.

توی خط اول چند query به دیتابیس فرستاده میشه؟ هیچ به دلیل اینکه ORM جنگو Lazy هست (تا زمان درخواست داده query ارسال نمیشه)

توی خط دوم که توی حلقه درخواست داده شده یک query به دیتابیس ارسال میشه و لیست پست ها رو میگیره و میریزه توی queryset. توی قسمت print وقتی title رو میخات چاپ کنه دیگه query زده نمیشه چون title یکی از property های Post هست که قبلا گرفته شده ولی وقتی میخات author رو چاپ کنه دوباره یک query به دیتابیس زده میشه که Author مربوط به اون پست رو بگیره (چون دو تا model جدا با رابطه 1->N هستن) بنابراین توی هر iteration از حلقه یک query دیگه زده میشه و این پرهزینه است و بهش میگن N+1 problem.

برای حل این مشکل باید برای پست ها Author ها رو هم یکجا با یک query بگیریم. برای حل این موضوع باید روی دو تا model یک join زده بشه.

ORM جنگو این مشکل رو با select_related() و prefetch_related() حل کرده.

select_related()

prefetch_related()

توی دو تا کد بالا فقط 1 query به دیتابیس زده میشه.

فرق select_related و prefetch_related چیه؟

select_related به یک Join تبدیل شده و به دیتابیس ارسال میشود و برای مدل هایی که ارتباط 1->N یا one to one دارند استفاده میشود اما prefetch_related دو داده را گرفته و به صورت پایتونی join میزنه در ضمن برای رابطه های چند به چند یا Generic استفاده میشه.

مثالی که توی مستندات جنگو هست:

مثال زیبای بعدی

سوالات مصاحبه

  1. context manager توی پایتون چیه؟ به چه دردی می خوره و چطور می شه یکی نوشت؟
    • https://book.pythontips.com/en/latest/context_managers.html
    • https://www.geeksforgeeks.org/context-manager-in-python/
  2. middleware توی جنگو رو توضیح بدید.
  3. فرض کنید توی یک حلقه یک آبجکت ORM میسازیم و توی همون لوپ توی دیتابیس save میکنیم. این حلقه یک میلیون بار تکرار میشه. آیا این مشکلی داره؟ اگه داره راه حلش چیه؟
  4. فرض کنید یک اپلیکیشن مثل توییتر می نویسیم و صفحه اول توئیت ها رو میبینیم ساختار API این صفحه رو بگید.
  5. فرض کنید داریم یک اپلیکیشن URL shortner مینویسیم از چه تکنولوژی هایی توی این پروژه استفاده میکنیم؟ stack
  6. فرض کنید یک جدول توی دیتابیس داریم توی ستون آخر دو تا تگ ذخیره می شه وسط پروژه فهمیدیم این ستون بهتره به دو ستون تبدیل بشه. چطور این ستون رو به دو ستون تبدیل می کنیم. در نظر بگیرید دیتای میلیونها کاربر توی جدول وجود داره و به پروداکشن هم دسترسی نداریم. توی این پروژه هم از ORM داریم استفاده می کنیم.
  7. فرض کنید دو تا تیبل دیتابیس داریم که رابطه 1-n را هم دارن دیتای جدول دوم رو میخایم به صورتی که دیتای جدول اول هم کنارش باشه. بهینه ترین query که میتونیم بزنیم چطوره؟
  8. مدل دیتابیس یک سایت ارائه دهنده فیلم مثل IMDB رو پیاده سازی کن (فیلم، لایک، عکس فیلم ها، فیلم های مشابه و کاربران). مدل API و معماری رو هم طراحی کن.
  9. مدل دیتابیس و معماری نرم افزار توئیتر رو طراحی کن.
  10. فرق Model با Serializer چیه؟
  11. decorator چیه؟
  12. generator توی پایتون چیه؟
    • https://wiki.python.org/moin/Generators
  13. operator overloading توی پایتون چطور پیاده سازی میشه؟
  14. Manager توی جنگو چه کاربردی داره؟
  15. از Celery استفاده کردی؟ چرا؟
  16. فرض کن دوتا App توی جنگو داری که به هم مرتبط هستند. مثلا اگر یک متد توی App اول اجرا میشه بعدش یک متد یا پارامتر توی App دوم رو استفاده میکنه. آیا این از دید مهندسی نرم افزار مشکل داره؟ اگر داره راه حل چیه؟
  17. از Multiprocessing و Multithreading توی پایتون استفاده کردید؟ محدودیت ها مزایا و معایبش رو بگید. از هر کدوم توی چه use case ای باید استفاده بشه.
  18. فرق ماژول با پکیج توی پایتوی چیه؟
  19. migration توی جنگو چیه و کاربردش چیه؟
  20. فرض کنید توی یک اپلیکیشن وب یک درخواست میدیم (مثل درخواست خرید توی فروشگاه) درخواست 5 دقیقه طول میکشه و fail میشه. این ایراد رو چطور ریشه یابی میکنید؟
  21. فرض کنید توی DRFمیخایم response هایی که به کاربر برگردونده میشه رو از نظر ساختار پاسخ (status، پیغام و …) یکپارچه کنیم. چطور این کار رو می کنیم؟
  22. فرض کنید توی جنگو توی Model های مختلف میخایم رو تمامی query هایی که به دیتابیس ارسال میشه یک تغییر یکسان بدیم (مثلا یک فروشگاه داریم میخایم از امروز توی تمامی query ها اطلاعات 2020 به قبل رو نشون نده) برای این کار بهترین راه حل تغییر مدل ها چیه؟
  23. خروجی کد زیر چه می شود و تحلیل کنید:

24. تسک های ناتمام در Celery چه اتفاقی براش میوفته

25. چه وقت هایی باید از multithreading یا Multiprocessing یا Celery استفاده کرد؟