PHP
N+1 sorgu problemi
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
-
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);
-
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
-
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.
Sıradaki ders: Lazy loading