N+1 Sorgu Sorunu
- İngilizcesi
- N+1 Query Problem
- Okunuşu
- en plas van kuiri problım
Günlük kullanımda iki ad da yaygın.
Kısaca
N+1 sorgu sorunu, kodun bir listeyi tek sorguyla yükleyip sonra her öğe için bir sorgu daha çalıştırdığı, hepsini birden getirmediği bir performans hatasıdır.
N+1 sorgu sorunu nedir?
N+1 sorgu sorunu, kod N kayıttan oluşan bir listeyi tek bir sorguyla getirip ardından üzerlerinde dolaşarak her kaydın ilişkili verisi için ayrı bir sorgu çalıştırdığında ortaya çıkar. Bu toplamda 1 + N sorgu eder. 10 kayıtla neredeyse fark edilmez, ancak 1.000 kayıtla tek bir sayfa yüklemesi veritabanına 1.001 sorgu gönderir.
Sorun çoğu zaman bir ORM'in lazy loading'i tarafından gizlenir: bir döngü içinde post.author okumak basit bir özellik erişimi gibi görünür, ancak her seferinde sessizce bir sorgu çalıştırır. Her sorgu tek başına küçük ve hızlıdır, ancak ağ gidiş-dönüşleri ve sorgu başına ek yük birleşince yavaş bir sayfa ortaya çıkar. Çözüm, ilişkili veriyi sabit ve az sayıda sorguyla getirmektir: ORM'de eager loading, tek bir JOIN ya da WHERE id IN (...) ile tek bir toplu sorgu kullanarak. GraphQL sunucuları bunu genellikle ID'leri toplayıp birlikte yükleyen, çoğu zaman data loader denen bir toplu işleme katmanıyla çözer.
Bu, alışveriş listenizdeki her ürün için markete bir kez gitmeye, hepsini tek seferde almak yerine, benzer. Sorunu veritabanı sorgu günlüklerinde, ORM hata ayıklama çıktısında ya da istek başına düzinelerce kez tekrarlanan aynı sorgu biçimini gösteren istek izlerinde (trace) fark edebilirsiniz; bazı ekipler bir endpoint belirli bir sorgu sayısını aştığında başarısız olan testler de ekler.
N+1 sorunu yavaş bir sorgudan farklıdır. Yavaş sorgu, bir indeks ya da yeniden yazımla çoğu zaman düzeltilebilen tek bir pahalı ifadedir; N+1 ise her biri kendi başına iyi görünen çok sayıda ucuz ifadedir, bu yüzden indeks eklemek yardımcı olmaz. Her şeyi eager loading ile yüklemek de her zaman cevap değildir; çünkü bir sayfanın hiç kullanmadığı ilişkili veriyi yüklemek bellek ve zaman israfıdır.
Bir bakışta
Önemli noktalar
- N+1, bir liste için bir sorgu artı listedeki her öğe için bir sorgu demektir.
- ORM lazy loading'i en yaygın gizli nedendir.
- Her sorgu hızlıdır, ancak liste büyüdükçe gidiş-dönüşler birikir.
- Eager loading, bir
JOINya da toplu birINsorgusuyla düzeltin. - İndeksler N+1'i çözmez; çünkü sorun sorgu sayısıdır.
Örnek
// N+1: 1 query for the posts, then 1 query per post for its author
const posts = await db.query("SELECT * FROM posts LIMIT 50");
for (const post of posts) {
const rows = await db.query("SELECT * FROM users WHERE id = ?", [post.author_id]);
post.author = rows[0];
}
// Fix: load all the authors in one extra query (2 queries in total)
const ids = posts.map((p) => p.author_id);
const authors = await db.query("SELECT * FROM users WHERE id IN (?)", [ids]);
const byId = new Map(authors.map((a) => [a.id, a]));
for (const post of posts) post.author = byId.get(post.author_id);Sık sorulan sorular
N+1 sorguları nasıl tespit ederim?
ORM'inizde ya da veritabanında sorgu günlüğünü açın ve tek bir istek sırasında farklı ID'lerle tekrarlanan aynı sorguyu arayın. İzleme ve performans takip araçları ile bazı ORM eklentileri örüntüyü otomatik olarak işaretleyebilir.
ORM kullanmak N+1 sorununa yol açar mı?
ORM tek başına buna yol açmaz, ancak lazy loading bunu yanlışlıkla yazmayı çok kolaylaştırır. Çoğu ORM, ilişkili kayıtları baştan yüklemek için include ya da prefetch gibi eager loading seçenekleri sunar.
JOIN her zaman N+1 sorgulardan daha mı iyidir?
Genellikle, ama her zaman değil. Bir birleştirme üst veriyi birçok satır boyunca çoğaltabilir; bu yüzden büyük bire-çok ilişkilerde biri üst kayıtlar, diğeri alt kayıtlar için toplu iki sorgu çoğu zaman daha temiz ve aynı derecede hızlıdır.
İlgili sayfalar
- ORMVeritabanları, s. 24ORM, veritabanı tablolarını programlama dilinizdeki nesnelere eşleyen ve ham SQL yerine kodla veri okuyup yazmanızı sağlayan bir kütüphanedir.
- SQL JOINVeritabanları, s. 30SQL JOIN, iki ya da daha fazla tablonun satırlarını yabancı anahtar gibi ilişkili sütunlar üzerinden eşleştirerek tek bir sonuçta birleştiren sorgu işlemidir.
- Veritabanı İndeksiVeritabanları, s. 38Veritabanı indeksi, tüm tabloyu taramadan satırları hızla bulmayı sağlayan, bir kitabın sonundaki dizine benzeyen bir veri yapısıdır.
- GecikmeAğlar, s. 9Gecikme, bir istek ile yanıtın başlaması arasında geçen süredir; genellikle milisaniyeyle ölçülür ve bir uygulamanın ne kadar hızlı hissettirdiğini belirler.
- GraphQLBackend ve API'ler, s. 18GraphQL, istemcilerin tam ihtiyaç duyduğu veriyi, çoğunlukla tek bir endpoint üzerinden tek istekte istemesini sağlayan API sorgu dili ve çalışma zamanıdır.
- Connection PoolVeritabanları, s. 4Connection pool, bir uygulamanın istekler arasında yeniden kullandığı açık veritabanı bağlantıları önbelleğidir; her seferinde bağlantı açma maliyetini önler.
Bu sayfada bir hata ya da eksik mi gördünüz?Düzeltme önerin