N+1 problemi və həlli
- 30 İyun 2024
- 2 dəqiqə
Proqramlaşdırmaya yeni başladığım vaxtlarda özüm də bilmədən məlumat bazasına qan uddururdum 🫢
C dilini öyrəndiyim vaxtlarda, yazdığım kodların, icra olunduğu cihazlarda resurslardan necə istifadə etdiyini də öyrənmişdim və bundan sonra bir növ performans obsession yaranıb məndə.
Yazdığım bütün kodlarda buna mütləq şəkildə diqqət yetirirəm.
Səliqəli kod, minimum resurs istifadəsi, maksimum nəticə.
Bununla belə yenə də, gözümdən qaçan bir problem olmuşdu: “N + 1”
Bu problem ilə onun haqqında bloq oxuyarkən tanış olmuşdum.
Aşağıdakı kod nümunəsini nəzərdən keçirək:
// Modellər arası əlaqələr
class Post extends Model
{
public function comments()
{
return $this->hasMany(Comment::class);
}
}
class Comment extends Model
{
public function post()
{
return $this->belongsTo(Post::class);
}
}
/*--------------------------------------------*/
// N+1 problem nümunəsi
$posts = Post::all();
foreach ($posts as $post) {
$posts = $post->comments;
}
Yuxarıdakı, N + 1 probleminin olduğu nümunədə əvvəlcə bütün postları almaq üçün məlumat bazasına sorğu göndəririk.
Postları aldıqdan sonra isə onları dövrdən keçirərək hər bir post üçün ayrılıqda məlumat bazasına onlara yazılmış kommentləri almaq üçün sorğu göndəririk.
Məsələn, məlumat bazasında 1000 ədəd post varsa, onların hər birinin kommentlərini əldə etmək üçün tək-tək sorğu göndərdiyimizə görə, 1000 ədəd ayrıca sorğu göndərmiş oluruq.
Beləliklə bir sorğu bütün postları almaq üçün, 1000 ədəd sorğu isə kommentləri əldə etmək üçün göndəririk.
N + 1 = 1000 + 1
Bunun qarşısını almaq üçün “eager loading” yanaşmasından istifadə edirik.
Aşağıdakı nümunədə postları çağırarkən, onların kommentlərini də birlikdə çağırırıq.
// Modellər arası əlaqələr
class Post extends Model
{
public function comments()
{
return $this->hasMany(Comment::class);
}
}
class Comment extends Model
{
public function post()
{
return $this->belongsTo(Post::class);
}
}
/*--------------------------------------------*/
// N+1 problem nümunəsinin "eager loading" ilə həlli
$posts = Post::with('comments')->get();
foreach ($posts as $post) {
$posts = $post->comments;
}
Beləliklə 1001 sorğu yerinə, cəmi 1 sorğu ilə bütün lazımi məlumatları bazadan çəkmiş oluruq.
Onu da qeyd edim ki, “eager loading”-dən düzgün istifadə edilmədikdə, onun özü də performans problemlərinə səbəb ola bilər.
Məsələn, çəkilən məlumatların həcmi çox böyükdürsə və ya dərin “nested relationship”-ləri “eager loading” ediriksə və s.
Bu haqda da növbəti postlardan birində qeyd edəcəm 🤝