پرش به مطلب اصلی

N+1

مسئله N+1 یکی از مشکلات متداول در دسترسی به دیتابیس است و معمولاً وقتی پیش می‌آید که داده‌ها را به صورت تودرتو (nested) یا رابطه‌ای (مثل user و posts) می‌خوانیم.

فرض کن می‌خوای همه کاربران و پست‌های آن‌ها را از دیتابیس بگیری.

یک برنامه بد این کار را اینطور انجام می‌دهد:

  1. اول همه کاربران را از دیتابیس می‌گیری → این یک query است.
  2. بعد برای هر کاربر، پست‌هایش را جداگانه از دیتابیس می‌گیری → اگر N کاربر داشته باشی، این N query اضافه می‌شود.

نتیجه:

  • 1 query برای کاربران
  • N query برای پست‌ها
  • جمعاً N + 1 query → به همین دلیل اسمش N+1 problem است.

مشکل این روش

  • تعداد query‌ها به تعداد رکوردها وابسته است → با بزرگ‌تر شدن داده‌ها، سرعت برنامه شدیداً افت می‌کند.
  • فشار روی دیتابیس زیاد می‌شود.
var users []User
db.Find(&users)

for _, u := range users {
db.Model(&u).Association("Posts").Find(&u.Posts)
}

راه حل

راه حل اصلی استفاده از join یا eager loading است:

  • به جای N query جداگانه، داده‌ها را یکجا با یک query می‌گیری.
  • اکثر ORM‌ها (مثل GORM در Go، Eloquent در Laravel) قابلیت Preload یا Include دارند که این مشکل را حل می‌کنند.

مثال ساده (Go + GORM)

var users []User
db.Find(&users)
for _, u := range users {
db.Model(&u).Association("Posts").Find(&u.Posts)
}

// Preload(like join)
var users []User
db.Preload("Posts").Find(&users)