Laravel-এ N+1 Query Problem Solve
Laravel-এ N+1 Query Problem একটি সাধারণ পারফরম্যান্স সমস্যা যা ডেটাবেইস থেকে ডেটা রিট্রাইভ করার সময় দেখা যায়, বিশেষ করে যখন রিলেশনাল ডেটা ব্যবহৃত হয় (যেমন: hasMany, belongsTo ইত্যাদি)। নিচে বাংলায় বিস্তারিত ব্যাখ্যা করছি:
🧠 কী বোঝায় N+1 Query Problem?
N+1 Query Problem তখন ঘটে যখন আপনি একটি মূল মডেলের N টি রেকর্ড আনছেন, এবং প্রতিটি রেকর্ডের সাথে সম্পর্কিত ডেটা (রিলেশনশিপ) আলাদাভাবে কোয়েরি করে আনছেন।
🧾 উদাহরণ:
ধরা যাক, আপনার দুটি মডেল আছে:
-
Post -
Comment(একটি পোস্টের অনেক কমেন্ট থাকতে পারে —hasManyসম্পর্ক)
আপনি যদি নিচের মতো কোড ব্যবহার করেন:
কি হচ্ছে এখানে:
-
প্রথম লাইনে
Post::all()— একটি কোয়েরিতে সব পোস্ট নিয়ে আসছে। (১টি কোয়েরি) -
তারপর প্রতিটি পোস্টের জন্য
$post->comments— আলাদাভাবে কমেন্টগুলো নিচ্ছে। যদি ১০টি পোস্ট থাকে, তাহলে এখানে ১০টি আলাদা SQL কোয়েরি হবে।
👉 মোট কোয়েরি = 1 (Post fetch) + 10 (each Post’s Comments) = 11 কোয়েরি
এটাই হলো N+1 Query Problem। যেখানে N = 10, তাই হয়েছে 1 + N = 11 কোয়েরি।
🚀 সমাধান: Eager Loading
Laravel আমাদেরকে এই সমস্যার সমাধানে Eager Loading দেয়, যার মাধ্যমে আমরা একসাথে রিলেটেড ডেটা নিয়ে আসতে পারি।
এখানে Laravel দুটি কোয়েরিতে সব পোস্ট এবং তাদের কমেন্টগুলো নিয়ে আসবে:
-
SELECT * FROM posts -
SELECT * FROM comments WHERE post_id IN (...)
👉 মোট কোয়েরি = 2 ✅
একটি ই-কমার্স সাইট
🎯 মডেলসমূহ:
-
Order -
Customer -
OrderItem -
Product
⚙️ সম্পর্কগুলো:
-
এক
Customer-এর অনেকগুলোOrder -
এক
Order-এর অনেকগুলোOrderItem -
প্রতিটি
OrderItemএকটিProduct-এর সাথে যুক্ত
❌ N+1 Problem Example:
👉 এখানে কী হচ্ছে?
-
Order::all()— সব অর্ডার আনছে (ধরা যাক 100টি অর্ডার) → 1 কোয়েরি -
প্রতিটি
$order->customerকল করলে 100টি আলাদা কোয়েরি হবে → 100 কোয়েরি -
প্রতিটি
$order->orderItemsকল করলে আবার 100 কোয়েরি → 100 কোয়েরি -
প্রতিটি
$item->productকল করলে যদি 500 আইটেম থাকে, তাহলে 500 কোয়েরি → 500 কোয়েরি
মোট = 1 + 100 + 100 + 500 = 701 কোয়েরি 😱
✅ সমাধান: Eager Loading দিয়ে
👉 এখানে Laravel নিচের মত করে কাজ করবে:
-
SELECT * FROM orders→ 1 কোয়েরি -
SELECT * FROM customers WHERE id IN (...)→ 1 কোয়েরি -
SELECT * FROM order_items WHERE order_id IN (...)→ 1 কোয়েরি -
SELECT * FROM products WHERE id IN (...)→ 1 কোয়েরি
মোট = 4 কোয়েরি ✅
✅ আরও একটি বাস্তব উদাহরণ: ব্লগ সাইট
মডেলসমূহ:
-
Post -
User -
Category -
Tag
Laravel এখানে চারটি Eager Load কোয়েরির মাধ্যমে সব রিলেশন লোড করবে।
📌 উপসংহার:
| বিষয় | খারাপ পদ্ধতি | ভালো পদ্ধতি |
|---|---|---|
| Customer Load | $order->customer বারবার | with('customer') |
| Items Load | $order->orderItems বারবার | with('orderItems') |
| Nested Load | $item->product বারবার | with('orderItems.product') |
🛠 পরামর্শ:
-
Laravel Debugbar বা Telescope দিয়ে কোয়েরির সংখ্যা পর্যবেক্ষণ করুন।
-
সর্বদা large loop এর আগে Eager Load করুন।
-
প্রয়োজনে Lazy Eager Loading (
load()) ব্যবহার করুন যদি পরবর্তী ধাপে লোড করতে চান।
Laravel Query Builder ব্যবহার করে N+1 problem-এর একটি বাস্তব উদাহরণ এবং তার সমাধান দিচ্ছি।
🎯 ধরুন আমাদের দুইটি টেবিল আছে:
-
categories -
products(প্রতিটি প্রোডাক্ট একটি ক্যাটেগরির অন্তর্ভুক্ত —products.category_id)
❌ N+1 Problem (Query Builder দিয়ে)
🔎 এখানে কী হচ্ছে?
-
প্রথমে সব
productsআনছে → ১টি কোয়েরি -
তারপর প্রতিটি
productএর জন্য আলাদা করে ক্যাটেগরি আনছে → ধরুন ১০০টা প্রোডাক্ট হলে ১০০টি কোয়েরি
মোট কোয়েরি = 1 + 100 = 101 ❌
✅ সমাধান (Eager Loading এর মতো Query Builder ব্যবহার করে JOIN)
✅ এখানে কী হচ্ছে?
-
আমরা
JOINব্যবহার করে একবারেই প্রোডাক্ট এবং তার ক্যাটেগরি নিয়ে আসছি। -
শুধুমাত্র ১টি কোয়েরি হচ্ছে!
মোট কোয়েরি = 1 ✅
✅ আরেকটি উদাহরণ: users এবং posts
❌ N+1 Problem (Query Builder)
মোট কোয়েরি = 1 + N (number of posts)
✅ সমাধান (JOIN দিয়ে)
মোট কোয়েরি = 1 ✅
🔚 উপসংহার
| ধরন | খারাপ পদ্ধতি (N+1) | ভালো পদ্ধতি (JOIN) |
|---|---|---|
| products → category | প্রতি লুপে DB::table()->where() | JOIN দিয়ে একবারে আনুন |
| posts → user | প্রতি লুপে ইউজার কল | JOIN দিয়ে select করুন |
কমপ্লেক্স ও বড় প্রজেক্ট ভিত্তিক উদাহরণ দিব, যাতে বুঝতে পারেন যে বড় অ্যাপ্লিকেশনে N+1 প্রোবলেম কিভাবে দেখা দেয় এবং কিভাবে তার সমাধান করা হয়।
🏨 উদাহরণ: Hotel Booking System
📦 মডেল সমূহ:
-
Hotel -
Room -
Booking -
User(Customer)
সম্পর্ক:
-
একটি
Hotel-এর অনেকগুলোRoom -
প্রতিটি
Room-এ অনেকBooking -
প্রতিটি
BookingএকজনUserদ্বারা করা
❌ N+1 Problem (Eloquent)
👉 ধরা যাক:
-
10টা হোটেল
-
প্রতিটিতে 20টা রুম → 200টি রুম
-
প্রতিটি রুমে 5টা বুকিং → 1000টি বুকিং
-
প্রতিটি বুকিং-এ ইউজার লোড হচ্ছে
মোট কোয়েরি:
-
Hotel::all()→ 1 -
প্রতিটি হোটেলের জন্য
rooms→ 10 কোয়েরি -
প্রতিটি রুমের জন্য
bookings→ 200 কোয়েরি -
প্রতিটি বুকিং এর জন্য
user→ 1000 কোয়েরি
👉 মোট = 1 + 10 + 200 + 1000 = 1211 কোয়েরি ❌
✅ সমাধান: Eager Loading
✅ Laravel করবে:
-
hotelsটেনে আনবে → 1 কোয়েরি -
সব
roomsটেনে আনবে (hotel_id IN (...)) → 1 কোয়েরি -
সব
bookingsটেনে আনবে (room_id IN (...)) → 1 কোয়েরি -
সব
usersটেনে আনবে (user_id IN (...)) → 1 কোয়েরি
👉 মোট = 4 কোয়েরি ✅
🔁 Query Builder দিয়ে (JOINs):
👉 এখানে শুধুমাত্র ১টি JOIN কোয়েরি দিয়ে আমরা সব কিছু পেলাম।
✅ Laravel Tips:
-
অনেক লেভেলের রিলেশন থাকলে:
with('a.b.c') -
Conditional Eager Loading:
with(['comments' => function($q) { $q->where('status', 'approved'); }]) -
Lazy Eager Loading:
$model->load('relation')
No comments