skip to Main Content

N+1 queries چیست و چگونه از آن جلوگیری کنیم

مساله N+1 کوئری

 

خوب اول ببینیم این مساله N+1 کوئری چیه و چطوری به وجود میاد.

خیلی ساده بخواهیم بگیم N+1 کوئری یک مشکلی هست که توسط برنامه نویس ایجاد میشه و باعث سرازیر شدن کوئری ها به سمت دینابیس میشه و تعداد بالای کوئری ها و اتصال و قطع شدن کانکشن های دیتابیس و زمان لود دیتا باعث کندی عملکرد اپلیکیشن هامون میشه .

زمانی که شما تو فاز MVP هستید و توسعه میدید شاید اتفاق عجیبی نیفته ولی موقعی که روی Production میرید و بار روی سیستم زیاد تر میشه خودش رو به خوبی نشون میده . و مخصوصا اگر دیتابیس روی سروی مستقل باشه اون موقع باید برای هر کوئری و کانکشن یک زمان پاسخ در نظر بگیرید که خودش عدد قابل توجهی میشه .

یک مثال ساده بزنیم :‌

فرض کنید شما یک اپلیکیشن دارید که یکسری یوزر داره و هر کاربر ۱۰۰۰ یا N تا دوست داره . حالا ما میخواهیم تو برنامه بگیم تمام کاربر با تمام دوستاشون رو لود کن و برامون نشون بده . متودهاش رو ببینید:

 

[php]

$users = load_users();

foreach ($users as $user) {

$users_friends = load_friends_for_user($user);

}

[/php]

 

خوب میتونید حدس بزنید زیر این کدها چه کوئری هایی لود میشن ؟

خیلی ساده اس :

برای خط اول کد ،کوئری شبیه به این باید لود بشه تا بتونه کاربرها رو لود کنه

 

[php]

SELECT * FROM users WHERE …

[/php]

و  داخل foreach  و متود load_friends_for_user هم کوئری هایی شبیه به این رو صدا میکنه .

[php]

SELECT * FROM user WHERE …

SELECT * FROM friend WHERE userID = 1

SELECT * FROM friend WHERE userID = 2

SELECT * FROM friend WHERE userID = 3

SELECT * FROM friend WHERE userID = 4

SELECT * FROM friend WHERE userID = 5

[/php]

پس همونطور که میبینید ما برای اینکه به داده دلخواه برسیم میبایست N+1 کوئری رو اجرا کنیم .

خوب چیکار کنیم که این همه کوئری اجرا نشه ؟ میتونیم با یک کوئری تمام نتایج رو بگیریم بعد اصطلاحا تو حلقه بگردونیمش (iterating)

خوب پس باید اول یه تغییری تو کدمون بدیم که اول اطلاعات رو به دست بیاریم.

 

[php]

$cats = load_users();

$friends = load_all_friends_for_these_users($users);

foreach ($users as $user) {

$users_friends = $friends[$user->getID()];

}

[/php]

حالا چه اتفاقی می افته ؟

هیچی به جای اون همه کوئری این کوئری ها زیر انجام میشه و تو این قطعه کد، اتصال به دیتابیس یکبار بیشتر باز و بسته نمیشه و خیلی سریعتر از حالت قبل خروجی به دست میاد.

 

[php]

SELECT * FROM users WHERE …

SELECT * FROM friends WHERE userID IN (1,2,3,4,5,…)

[/php]

 

مهم نیست چقدر آبجکت داشته باشید هر چقدر که باشه این دو تا کوئری بیشتر اجرا نمیشن.

 

 

Back To Top