Taze Logo
Performans17 Mart 2026

Veritabanı Sorgularınız Yalan Söylüyor: Kullandığınız ORM'lerin Gizlediği, Uygulamanızı Yavaşlatan Ölümcül Hata

Veritabanı Sorgularınız Yalan Söylüyor: Kullandığınız ORM'lerin Gizlediği, Uygulamanızı Yavaşlatan Ölümcül Hata

Çok masum görünen bir kod satırı, nasıl olur da uygulamanızı saniyelerce yavaşlatır ve bulut faturanızı patlatır? ORM'lerin en büyük günahı olan 'N+1 Sorgu Problemi'ni, gerçek kod senaryoları ve SQL kanıtlarıyla deşifre ediyoruz.

Giriş: Masumiyetin Sonu

Kod editörünüzde her şey mükemmel görünüyor. Tertemiz, okunabilir ve modern bir kod yazdınız. ORM'niz (Object-Relational Mapper) sayesinde, karmaşık SQL sorguları yerine zarif objelerle çalışıyorsunuz. Birkaç satırda, önce tüm blog yazılarını alıyor, sonra da her birinin yazar adını ekrana basıyorsunuz. Her şey yolunda, değil mi? Ama sonra bir test ortamında veya canlıda o korkunç gerçekle yüzleşiyorsunuz: 100 blog yazısı olan basit bir sayfanın yüklenmesi 5 saniye sürüyor. Sunucu CPU'su tavan yapmış, veritabanı adeta yalvarıyor.

Tebrikler, en güvendiğiniz aracınız, ORM'niz tarafından sırtınızdan bıçaklandınız. Ve bu, geliştiricilerin en sık düştüğü, en maliyetli ve en sinsi tuzaklardan biridir: N+1 Sorgu Problemi.

Olay Mahalli: Suç Ne Kadar Basit Olabilir?

Suçun ne kadar basit ve masum göründüğünü anlamak için, popüler bir ORM olan Prisma ile yazılmış bir Node.js/Express kodu inceleyelim. Senaryo: Blog yazılarını listelediğimiz bir API endpoint'i hazırlıyoruz ve her yazının yanında yazarının adını da göstermek istiyoruz.

❌ YANLIŞ ve TEHLİKELİ Kod (N+1 Yaratan Kod)


// app.js

app.get('/posts', async (req, res) => {
    console.log("İstek geldi. Tüm post'lar çekiliyor...");
    const posts = await prisma.post.findMany(); // 1. Sorgu: Tüm post'ları çek

    const results = [];
    for (const post of posts) {
        // Döngü içinde her bir post için yazar bilgisi çekiliyor
        console.log(`Post ID ${post.id} için yazar aranıyor...`);
        const author = await prisma.user.findUnique({ // 2., 3., 4., ... N+1. Sorgu
            where: { id: post.authorId },
        });
        results.push({
            title: post.title,
            authorName: author.name,
        });
    }

    res.json(results);
});
        

Bu kod ilk bakışta tamamen mantıklı. Önce yazıları al, sonra bir döngüyle her yazının yazarını bul. Peki, ORM'niz bu masum görünen kodu SQL'e çevirdiğinde arka planda gerçekte ne yapıyor?

Kanıt: SQL Logları Asla Yalan Söylemez

Eğer veritabanı loglarınızı açarsanız, aşağıdaki gibi korkunç bir manzarayla karşılaşırsınız (100 adet post olduğunu varsayarsak):


-- 1. Sorgu
SELECT "public"."Post"."id", "public"."Post"."title", "public"."Post"."authorId" FROM "public"."Post"

-- Döngü Başlıyor...
-- 2. Sorgu
SELECT "public"."User"."id", "public"."User"."name" FROM "public"."User" WHERE "public"."User"."id" = 12
-- 3. Sorgu
SELECT "public"."User"."id", "public"."User"."name" FROM "public"."User" WHERE "public"."User"."id" = 5
-- 4. Sorgu
SELECT "public"."User"."id", "public"."User"."name" FROM "public"."User" WHERE "public"."User"."id" = 12
-- ...
-- ...ve bu şekilde 100 kere daha devam eder!
-- ...
-- 101. Sorgu
SELECT "public"."User"."id", "public"."User"."name" FROM "public"."User" WHERE "public"."User"."id" = 28
        

Toplamda tam 101 adet veritabanı sorgusu! İşte N+1 problemi budur: N adet öğe listesi için yapılan 1 adet sorgu + her bir öğenin ilişkili verisi için yapılan N adet ekstra sorgu.

Sonuçlar: Bu "Küçük" Hatanın Devasa Maliyeti

  • Performans Ölümü: Her veritabanı sorgusu, bir ağ gidiş-dönüşü (network roundtrip) demektir. 100 sorgu, 100 gidiş-dönüş demektir. Bu, API yanıt sürenizi milisaniyelerden saniyelere çıkarır.
  • Veritabanı Katliamı: Sunucunuza aynı anda sadece 10 kullanıcı bile bu isteği atsa, veritabanınız bir anda 1000'den fazla sorguyla boğulur. Bu, tüm sisteminizi yavaşlatabilir veya çökertebilir.
  • Para Tuzağı: Bulut veritabanı kullanıyorsanız, genellikle CPU, bellek ve I/O (girdi/çıktı) operasyonları için para ödersiniz. N+1, bu metriklerin hepsini patlatarak size ay sonunda acı bir fatura sürprizi yaşatır.

Çözüm: ORM'nize "Aç Gözlü" Olmasını Söyleyin (Eager Loading)

Çözüm, şaşırtıcı derecede basittir. ORM'nize, ilk sorguyu yaparken "ileride ihtiyacım olacak ilişkili verileri de şimdiden, tek seferde getir" demelisiniz. Bu tekniğe "Eager Loading" denir. Prisma'da bu, include parametresiyle yapılır.

✅ DOĞRU ve PERFORMANSLI Kod


// app.js

app.get('/posts-optimized', async (req, res) => {
    console.log("İstek geldi. Tüm post'lar ve ilişkili yazarları tek seferde çekiliyor...");
    
    const postsWithAuthors = await prisma.post.findMany({
        include: {
            author: true, // Sihirli dokunuş: Yazarları da sorguya dahil et!
        },
    });

    const results = postsWithAuthors.map(post => ({
        title: post.title,
        authorName: post.author.name, // Veri zaten burada, yeni bir sorguya gerek yok!
    }));

    res.json(results);
});
        

Aydınlanma: Optimize Edilmiş Kodun SQL Kanıtı

Şimdi aynı isteği optimize edilmiş endpoint'e attığımızda veritabanı logları neye benziyor?


-- Tek ve Akıllı Sorgu
SELECT 
    "public"."Post"."id", 
    "public"."Post"."title", 
    "public"."Post"."authorId", 
    "t1"."id" AS "author_id", 
    "t1"."name" AS "author_name" 
FROM "public"."Post" 
LEFT JOIN "public"."User" AS "t1" ON "public"."Post"."authorId" = "t1"."id"
        

Gördünüz mü? Sadece 1 adet, akıllıca bir JOIN kullanan sorgu. Veritabanı, tüm işi tek seferde halletti ve uygulamanıza hazır bir şekilde sundu.

Önce & Sonra: Şok Edici Karşılaştırma

Metrik ❌ N+1 Problemli Kod ✅ Eager Loading ile Optimize Kod Sonuç
Veritabanı Sorgu Sayısı 101 1 101x İyileşme
API Yanıt Süresi (Tahmini) ~5200 ms ~75 ms ~70x Daha Hızlı
Veritabanı Yükü Çok Yüksek Düşük Sistem Sağlığı
Geliştirici Mutluluğu "Neden bu kadar yavaş!?" 😫 "Kodum uçuyor!" 😎 Paha Biçilemez

Sonuç: Aracınıza Güvenin ama Kontrolü Asla Bırakmayın

ORM'ler, modern yazılım geliştirmenin en güçlü araçlarındandır. Bizi sıkıcı ve hataya açık SQL'ler yazmaktan kurtarırlar. Ancak bu sihir, bir bedelle gelir: Soyutlama (abstraction). Eğer aracınızın sizin için ne yaptığını anlamazsanız, en iyi niyetlerle yazdığınız kod bile bir performans kabusuna dönüşebilir.

Kural basittir: Ne zaman bir liste verisi çekip, döngü içinde o listenin elemanlarına ait ilişkili bir veriye erişiyorsanız, durup kendinize sorun: "Ben N+1 tuzağına mı düşüyorum?". Cevabınız evet ise, çözümün Eager Loading olduğunu artık biliyorsunuz. Şimdi gidin ve kodunuzdaki bu ölümcül hataları avlayın!