CodeBlanks Logo Icon CodeBlanks

N+1 sorgu problemi

PHP kursu · Uzman · Ders 26/54

ORM ve veri yüklemede N+1 sorgu problemini ve eager loading çözümünü öğrenirsin.

N+1 sorgu problemi nedir?

ORM kullanan bir uygulamada N satır listeliyorsanız ve her satırın ilişkili verisini ayrı bir sorguda çekiyorsanız, N+1 sorunu yaşıyorsunuz demektir: 1 sorgu satırlar için, N sorgu her birinin ilişkisi için. Yüz satırda yüz bir sorgu çalışır; bin satırda bin bir.

Eager loading bu sorunu çözer: ilişkili veriyi tek sorguda JOIN ya da IN ile topluca çeker; toplam sorgu sayısı bire iner. Lazy loading ise varsayılan davranıştır ve gereksiz yere sorgu üretir; liste işlerinde eager, tek öğe işlerinde lazy tercih edilir.

Eager loading uygulaması

Eager loading bir kereliğe ilişki verisini önbelleğe alır. Önce tüm postları çekersiniz, sonra author_id değerlerini array_column ile toplarsınız, tek bir SELECT ... WHERE id IN (...) sorgusuyla tüm yazarları çekersiniz ve bunu id => row biçiminde bir lookup tablosuna koyarsınız.

Yazdırırken her post için lookup tablosundan yazar adını alırsınız; sorgu sayısı 1 + 1 = 2 olur (N+1 yerine). Bu desen batch query olarak da bilinir ve ORM'lerde with() veya join() çağrılarıyla yapılır.

Uygulama N+1 ve eager loading'i sayımla karşılaştır

Aşağıdaki $posts ve $authors dizileri hazırdır. $queryCount = 0 ile başlat. Lazy (N+1) yaklaşımını simüle et: foreach ile her $post için $queryCount++ ve $author = $authors[$post['author_id']] al. echo $queryCount . "\n" yazdır. Eager yaklaşımı: $queryCount = 1 ve echo $queryCount . "\n". Sonra foreach ile her $post'un 'title' değerini yazdır.

Örnek çözümü göster
$queryCount = 0; ile başlat. foreach ($posts as $post) { $queryCount++; $author = $authors[$post['author_id']]; } echo $queryCount . "\n"; $queryCount = 1; echo $queryCount . "\n"; foreach ($posts as $post) { echo $post['title'] . "\n"; }.
Örnek çözüm:
<?php
$posts = [
    ['id' => 1, 'title' => 'A', 'author_id' => 10],
    ['id' => 2, 'title' => 'B', 'author_id' => 10],
    ['id' => 3, 'title' => 'C', 'author_id' => 20],
];
$authors = [10 => 'Ayşe', 20 => 'Deniz'];

$queryCount = 0;
foreach ($posts as $post) {
    $queryCount++;
    $author = $authors[$post['author_id']];
}
echo $queryCount . "\n";

$queryCount = 1;
echo $queryCount . "\n";

foreach ($posts as $post) {
    echo $post['title'] . "\n";
}

Kendini test et

  1. Eager loading sorgusunu tamamla

    $posts = fetchPosts();
    $author_ids = array_column($posts, 'author_id');
    $authors = ____('SELECT * FROM authors WHERE id IN (?)', $author_ids);
    Cevabı göster
    $posts = fetchPosts();
    $author_ids = array_column($posts, 'author_id');
    $authors = fetchAll('SELECT * FROM authors WHERE id IN (?)', $author_ids);
  2. N+1 ve eager loading kavramlarını eşleştir

    N+1
    Her satır için ayrı sorgu
    eager loading
    Tek sorguda toplu yükleme
    lazy loading
    İlk erişimde yükle
    JOIN
    İlişkiyi tek sorguda birleştir
  3. Eager loading ne zaman tercih edilir?

    • Tek bir öğe için veri çekerken
    • Birden fazla satırın ilişkisini listelerken
    • Önbellek temizliği yaparken
    • Çöp toplayıcıyı tetiklerken
    Cevabı göster

    Birden fazla satırın ilişkisini listelerken — Doğru. Liste işlerinde eager loading N+1'i önler ve sorgu sayısını ikiye indirir.

Uzman bölümündeki tüm dersler