Skip to content

Bölüm 18 / 19

NoSQL ve MongoDB

Doküman modeli, sorgular; ne zaman ilişkisel yerine.

16 dakikalık okuma

NoSQL, "SQL'in modası geçti" demek değildir; farklı bir denge demektir — şema esnekliği ve yatay ölçekleme karşılığında join, kısıt ve çok kayıtlı tutarlılıktan taviz. MongoDB bu ailenin en yaygın üyesidir ve doğru kullanıldığında güçlüdür, yanlış kullanıldığında ilişkisel veritabanının kötü bir taklidine dönüşür. Bu ders modeli, sorguları ve indeksleri öğretirken asıl olarak seçim ölçütünü veriyor: 13. derste gördüğün jsonb ile ne zaman yetinilir, ne zaman gerçekten doküman veritabanına geçilir.

01Ne öğreneceksin

  • NoSQL'in dört ailesi ve her birinin çözdüğü problem
  • ACID ile BASE arasındaki denge; CAP teoreminin doğru ve yanlış okunuşu
  • Doküman modeli: koleksiyon, doküman, BSON ve _id
  • Gömme mi referans mı kararı ve 16 MB doküman sınırı
  • Sınırsız büyüyen dizi anti-deseni ve kova (bucket) deseni
  • Sorgular: filtre operatörleri, projeksiyon, güncelleme operatörleri
  • Toplama hattı (aggregation pipeline) ve aşama sırasının önemi
  • İndeksler ve ESR kuralı; çok anahtarlı, TTL, kısmi ve metin indeksleri
  • Tek doküman atomikliği ile çok doküman transaction'ı arasındaki fark
  • Kopya kümesi, yazma/okuma tercihi ve bayat okuma riski
  • Parçalama (sharding) ve parça anahtarı seçmenin geri dönüşü zor kararı
  • MongoDB ile PostgreSQL jsonb karşılaştırması
  • .NET sürücüsü: MongoClient ömrü ve DbContext ile zıtlığı

02NoSQL: dört aile, tek gerekçe

2000'lerin sonunda büyük web sistemleri iki sınıra çarptı: ilişkisel veritabanını yatay ölçeklemek zordur (join ve transaction makineler arasına yayılmaz) ve şema değişikliği büyük tablolarda pahalıdır. NoSQL bu iki soruna farklı cevaplar veren bir aileler kümesidir:

AileVeri modeliTipik kullanımÖrnek
Dokümanİç içe JSON benzeri belgelerKatalog, içerik, olay, kullanıcı profiliMongoDB, Couchbase
Anahtar-değerAnahtar → opak değerÖnbellek, oturum, sayaç, kuyrukRedis, DynamoDB
Sütun ailesiSatır anahtarı + sütun gruplarıÇok yüksek yazma hacmi, zaman serisiCassandra, HBase
GrafDüğüm ve kenarlarSosyal ağ, öneri, yol bulmaNeo4j

ACID ve BASE. 10. derste ACID'i öğrendin. Dağıtık NoSQL sistemlerinin bir kısmı BASE dengesini seçer: Basically Available, Soft state, Eventual consistency — yani yazma her zaman kabul edilir, kopyalar bir süre sonra tutarlı hâle gelir. "Bir süre sonra" ifadesi kritiktir: kullanıcı kendi yazdığı veriyi hemen sonra okuduğunda göremeyebilir.

CAP teoremi sık yanlış anlatılır. Doğru okunuşu şudur: ağ bölünmesi yaşandığında (P kaçınılmazdır) tutarlılık (C) ile erişilebilirlik (A) arasında seçim yapmak zorundasın. Bölünme yokken üçü birden mümkündür. Ayrıca bu bir "ya hep ya hiç" anahtarı değil, ayarlanabilir bir eksendir — MongoDB'de yazma ve okuma tercihiyle bu ekseni istek bazında kaydırırsın. Mülakatta "MongoDB CP mi AP mi" sorusunun doğru cevabı "yapılandırmaya bağlı; varsayılan olarak birincil düğümden okuyup çoğunluk onayı istersen tutarlılık tarafındasın"dır.

03Doküman modeli

MongoDB'de veritabanı → koleksiyon → doküman hiyerarşisi vardır; koleksiyon tabloya, doküman satıra karşılık gelir ama doküman iç içe yapı barındırabilir. Saklama biçimi BSON'dur (ikili JSON): JSON'un olmayan tiplerini ekler — Decimal128, Date, ObjectId, ikili veri. 6. dersteki JSON sayı hassasiyeti sorunu bu sayede çözülür: parayı Decimal128 ile saklarsın, double ile değil.

{
  "_id": ObjectId("66c0a1f2e13b4a0012ab34cd"),
  "ad": "Ayşe Yılmaz",
  "eposta": "ayse@ornek.com",
  "adres": { "sehir": "Ankara", "posta": "06500" },      // GÖMÜLÜ nesne
  "etiketler": ["kurumsal", "vip"],                       // dizi
  "sonSiparis": ISODate("2026-08-20T10:15:00Z"),
  "bakiye": NumberDecimal("1520.75")
}

_id her dokümanda zorunludur ve otomatik üretilir. Varsayılan ObjectId 12 bayttır ve içinde oluşturulma zaman damgası taşır — yani kimlikten yaklaşık oluşturma zamanını okuyabilirsin. Buna karşılık monoton artan olduğu için parça anahtarı olarak kullanılırsa tüm yazmalar tek parçaya düşer (aşağıda).

"Şemasız" yanıltıcı bir kelimedir. MongoDB şemayı zorlamaz ama şema vardır — yalnızca uygulama kodunun içindedir. Aynı koleksiyonda üç farklı alan setine sahip dokümanlar birikirse okuma kodu savunmacı kontrollerle dolar. Bu yüzden üretimde şema doğrulaması kullanılır:

db.createCollection("musteri", {
  validator: { $jsonSchema: {
    bsonType: "object",
    required: ["ad", "eposta"],
    properties: {
      ad:     { bsonType: "string", maxLength: 200 },
      eposta: { bsonType: "string", pattern: "^.+@.+$" },
      bakiye: { bsonType: "decimal" }
    }
  }},
  validationLevel: "strict"
});

Gömme mi, referans mı?

Doküman modelinin tek gerçek tasarım kararı budur ve ilişkisel normalizasyonun (8. ders) yerini alır.

ÖlçütGöm (embed)Referans ver
Birlikte okunuyor muEvet — tek okumada gelsinHayır — ayrı sorgula
İlişki türüBire-az (adres, sipariş satırı)Bire-çok-çok, çok-çok
BüyümeSınırlı olmalıSınırsız büyüyebilir
Güncelleme sıklığıAna belgeyle birlikte değişiyorBağımsız değişiyor
TekrarKabul edilebilir (denormalizasyon)Tek kaynak gerekli

Sert sınır: bir doküman en fazla 16 MB olabilir. Bu, "yorumları gönderiye göm" gibi kararların neden tehlikeli olduğunu açıklar — popüler bir gönderide dizi sınırsız büyür ve bir gün yazma işlemi hata verir. Dahası, dizi büyüdükçe her güncelleme tüm dokümanı yeniden yazmaya yaklaşır. Kural: sınırsız büyüyebilen hiçbir şeyi gömme.

Zaman serisi ve olay verisinde standart çözüm kova desenidir: her ölçümü ayrı doküman yapmak yerine, saatlik/günlük kovalarda sabit sayıda ölçüm toplarsın. Doküman sayısı ve indeks boyutu düşer, okuma tek seferde gelir. (MongoDB 5.0+ ayrıca yerleşik zaman serisi koleksiyonları sunar.)

$lookup bir join değildir — daha doğrusu, ilişkisel join'in performansına sahip değildir: sol taraftaki her doküman için sağ koleksiyonda arama yapar (yani 3. dersteki nested loops'un tek algoritmalı hâli), hash ya da merge join seçeneği yoktur. Bu yüzden modeli "join yazacağım" varsayımıyla kurarsan MongoDB'yi yavaş bir ilişkisel veritabanı gibi kullanmış olursun.

04Sorgular, güncellemeler ve toplama hattı

// Filtre + projeksiyon + sıralama + sayfalama
db.musteri.find(
  { "adres.sehir": "Ankara", bakiye: { $gt: NumberDecimal("1000") } },   // WHERE
  { ad: 1, eposta: 1, _id: 0 }                                           // SELECT
).sort({ sonSiparis: -1 }).skip(20).limit(20);

// Sık kullanılan operatörler
{ yas:  { $gte: 18, $lt: 65 } }            // aralık
{ tur:  { $in: ["web", "mobil"] } }        // IN
{ ad:   { $regex: "^Ah" } }                // önek — İNDEKS KULLANIR
{ ad:   { $regex: "ah" } }                 // içinde — indeks kullanmaz (9. ders SARGability)
{ etiketler: "vip" }                       // dizi içinde arama
{ eposta: { $exists: false } }             // alan yok (NULL'dan farklı — 6. ders)

// Güncelleme: TÜM dokümanı değil, ALANI değiştir
db.musteri.updateOne({ _id: id }, {
  $set:  { "adres.sehir": "İzmir" },
  $inc:  { girisSayisi: 1 },
  $push: { etiketler: "kampanya" },
  $currentDate: { guncelleme: true }
});

Dikkat: updateOne çağrısında $set yerine doğrudan bir belge verirsen ({ ad: "X" }) doküman tamamen değiştirilir ve diğer alanlar silinir. 15. dersteki EF Core Update tuzağının MongoDB karşılığıdır.

Toplama hattı, SQL'in GROUP BY + JOIN + pencere fonksiyonlarının karşılığıdır. Aşamalar sırayla çalışır ve sıra performansı belirler:

db.siparis.aggregate([
  { $match:  { olusturma: { $gte: ISODate("2026-01-01") } } },   // ÖNCE filtrele (indeks kullanılır)
  { $group:  { _id: "$sehir",
               toplam: { $sum: "$tutar" },
               adet:   { $sum: 1 } } },
  { $match:  { toplam: { $gt: 10000 } } },                        // HAVING karşılığı
  { $sort:   { toplam: -1 } },
  { $limit:  10 },
  { $lookup: { from: "sehir", localField: "_id",
               foreignField: "kod", as: "sehirBilgi" } }          // EN SONA bırak
]);

Kural: $match ve $project mümkün olduğunca başa, $lookup mümkün olduğunca sona. İlk aşama indeksten yararlanabilir; sonraki aşamalar bellek içinde çalışır ve varsayılan olarak aşama başına 100 MB sınırı vardır (allowDiskUse ile diske taşınır — 4. dersteki spill kavramının aynısı).

05İndeksler ve ESR kuralı

MongoDB de B-ağacı indeksleri kullanır ve 9. derste öğrendiğin her şey burada da geçerlidir: indeks yoksa koleksiyon taranır (COLLSCAN), bileşik indekste sütun sırası belirleyicidir, fonksiyonla sarılmış alan indeks kullanamaz.

db.siparis.createIndex({ musteriId: 1, olusturma: -1 });         // bileşik
db.musteri.createIndex({ eposta: 1 }, { unique: true });          // benzersiz
db.musteri.createIndex({ eposta: 1 },
      { unique: true, partialFilterExpression: { silindiMi: false } });  // kısmi (8. ders)
db.oturum.createIndex({ olusturma: 1 }, { expireAfterSeconds: 3600 });   // TTL — otomatik silme
db.urun.createIndex({ ad: "text", aciklama: "text" });            // metin araması

db.siparis.find({ musteriId: 42 }).explain("executionStats");     // plan (9. ders)

ESR kuralı MongoDB'ye özgü ve mülakatta ayırt edicidir. Bileşik indekste alan sırası şöyle olmalıdır: önce Equality (eşitlik koşulu olan alanlar), sonra Sort (sıralama alanları), en sonda Range (aralık koşulu olan alanlar). 9. dersteki "eşitlik önce, aralık sonra" kuralının, sıralamayı da içeren hâlidir; sıralamayı aralıktan önce koymazsan motor sonuçları bellekte sıralamak zorunda kalır ve 32 MB'lık bellek içi sıralama sınırına çarpabilirsin.

Çok anahtarlı (multikey) indeks: bir dizi alanını indekslersen MongoDB dizinin her elemanı için bir giriş üretir. Güçlüdür ama sınırı vardır: bir bileşik indekste birden fazla dizi alanı bulunamaz — denersen hata alırsın, çünkü kombinasyon sayısı patlar.

Diğer ayrıntılar: TTL indeksi süresi dolan dokümanları arka planda siler (oturum, geçici belirteç, günlük için ideal — ilişkisel dünyada elle yazdığın temizlik işinin karşılığı); indeksler yazma maliyeti getirir (9. ders); $regex yalnızca başa sabitlenmişse (^) indeks kullanabilir.

06Tutarlılık: atomiklik, transaction, kopyalar

Tek doküman güncellemesi atomiktir. Bu, doküman modelinin en önemli avantajıdır: ilişkisel dünyada üç tabloya yayılan ve transaction gerektiren bir işlem, doğru modellenmiş bir dokümanda tek atomik güncellemeye iner. Sipariş ve satırlarını aynı belgede tutuyorsan "sipariş eklendi ama satırlar eklenmedi" durumu oluşamaz.

Çok doküman transaction'ı MongoDB 4.0'dan (parçalı kümelerde 4.2'den) beri vardır ama üç şartı vardır ve bunlar sık atlanır: (1) kopya kümesi (replica set) gerekir — tek düğümlü kurulumda çalışmaz; (2) varsayılan olarak 60 saniye sınırı vardır; (3) performans maliyeti gerçektir. Yani MongoDB'de transaction bir kaçış kapısıdır, tasarımın merkezi değil. İhtiyacın sürekli çok doküman transaction'ıysa model yanlış kurulmuş ya da ilişkisel veritabanı daha doğru bir seçim demektir.

using var oturum = await client.StartSessionAsync();
oturum.StartTransaction();
try
{
    await hesaplar.UpdateOneAsync(oturum, h => h.Id == a, Builders<Hesap>.Update.Inc(x => x.Bakiye, -500));
    await hesaplar.UpdateOneAsync(oturum, h => h.Id == b, Builders<Hesap>.Update.Inc(x => x.Bakiye,  500));
    await oturum.CommitTransactionAsync();
}
catch { await oturum.AbortTransactionAsync(); throw; }

Kopya kümesi ve okuma tutarlılığı. Üretimde MongoDB birincil (primary) ve ikincil (secondary) düğümlerden oluşur; yazmalar birincile gider, ikincillere asenkron kopyalanır. İki ayar davranışı belirler:

AyarSeçenekSonucu
writeConcernw:1Birincil yazdı, yeter — hızlı; birincil çökerse veri kaybı mümkün
w:"majority"Çoğunluk onayladı — dayanıklı, daha yavaş (varsayılan)
readPreferenceprimaryHer zaman güncel (varsayılan)
secondaryOkuma yükü dağılır ama bayat veri gelebilir
readConcernmajority / linearizableGeri alınmayacak veriyi okuma garantisi

"Raporları ikincilden okuyalım" kararı cazip görünür ve sık verilir; bedeli kullanıcının az önce kaydettiği kaydı listede görememesidir. Bunu bilerek ve belgelendirerek yapmalısın.

Parçalama (sharding) yatay ölçeklemenin yoludur: koleksiyon, parça anahtarına göre makinelere dağıtılır. Parça anahtarı seçimi bu dersin en kritik kararıdır: monoton artan bir anahtar (zaman damgası, ObjectId) seçersen tüm yeni yazmalar tek parçaya düşer ve ölçekleme hiç işe yaramaz — buna sıcak nokta denir. İyi bir parça anahtarı yüksek kardinaliteli, düzgün dağılmış ve sorgularda filtre olarak kullanılan alandır. Değiştirmek mümkündür (5.0+ yeniden parçalama) ama pahalı ve risklidir; yani baştan doğru seçilmesi gereken kararlardandır.

07MongoDB mi, PostgreSQL jsonb mi?

13. dersin senedi. Esnek belge saklamak için ilişkisel veritabanından çıkman gerekmiyor olabilir:

KonuPostgreSQL + jsonbMongoDB
Esnek belgeVar (GIN indeksli)Var — modelin merkezi
İlişkisel kısıtlarVar — aynı veritabanında FK, CHECK, benzersizlikYok — uygulama sorumluluğu
JoinGerçek join, üç algoritma$lookup — yalnız döngüsel
Çok kayıtlı transactionDoğal ve ucuzMümkün ama kaçış kapısı
Yatay ölçeklemeZor (bölümleme, Citus gibi eklentiler)Yerleşik (sharding)
Şema evrimiGöç gerekirAlan eklemek bedava; okuma kodu uyumlu olmalı
Ekip bilgisiSQL zaten biliniyorYeni sorgu dili ve işletim modeli

Pratik karar: verinin bir kısmı esnek ama ilişkiler ve tutarlılık önemliyse PostgreSQL + jsonb çoğu ekip için daha az riskli seçimdir — tek bir veritabanı işletirsin. MongoDB'ye geçmenin gerçek gerekçeleri şunlardır: baştan yatay ölçekleme ihtiyacı, doğal olarak belge biçiminde ve birlikte okunan veri, şemanın gerçekten öngörülemez olması, çok yüksek yazma hacmi. "İlişkileri var ama şemayı değiştirmek zor geliyor" gerekçesi yeterli değildir.

Ayrıca bu ikisi rakip olmak zorunda değildir: yaygın mimari, işlemsel veriyi ilişkiselde, olay/günlük/katalog verisini doküman veritabanında tutmaktır (19. hatta çoklu depo yaklaşımının maliyetlerini de göreceksin).

08.NET tarafı

// MongoClient İŞ PARÇACIĞI GÜVENLİDİR ve bağlantı havuzunu kendi yönetir
// → SINGLETON kaydedilir. (DbContext'in Scoped olmasının tam tersi — 15. ders)
builder.Services.AddSingleton<IMongoClient>(_ => new MongoClient(dize));
builder.Services.AddScoped(sp =>
    sp.GetRequiredService<IMongoClient>().GetDatabase("kariyer"));

public class MusteriDepo
{
    private readonly IMongoCollection<Musteri> _koleksiyon;
    public MusteriDepo(IMongoDatabase db) => _koleksiyon = db.GetCollection<Musteri>("musteri");

    public Task<List<Musteri>> SehirdekiAsync(string sehir) =>
        _koleksiyon.Find(m => m.Adres.Sehir == sehir)
                   .Project(m => new Musteri { Id = m.Id, Ad = m.Ad })   // projeksiyon
                   .SortByDescending(m => m.SonSiparis)
                   .Limit(50)
                   .ToListAsync();

    public Task ArtirAsync(string id) =>
        _koleksiyon.UpdateOneAsync(m => m.Id == id,
            Builders<Musteri>.Update.Inc(m => m.GirisSayisi, 1));
}

Sürücü LINQ de destekler (AsQueryable()) ve 14. dersteki IQueryable sınırı burada da geçerlidir: çevrilemeyen ifade ya hata verir ya da istemciye veri taşır. Ayrıca [BsonElement], [BsonId] ve [BsonRepresentation] öznitelikleriyle eşleme ayarlanır — _id'yi string olarak taşıyacaksan [BsonRepresentation(BsonType.ObjectId)] yazmayı unutma, yoksa çalışma anında dönüşüm hatası alırsın.

09Mini alıştırma — Modeli iki kez kur

Yaklaşık 20 dakika · docker run -p 27017:27017 mongo yeterli

  1. Bağlan: mongosh ile bağlan; show dbs, use kariyer, db.musteri.insertOne({...}), db.musteri.find().pretty() komutlarını dene.
  2. Gömme: müşteri belgesine adres ve son üç siparişi göm; tek sorguyla tümünü getir. Aynı veriyi ilişkisel şemada kaç tablo ve kaç join ile alırdın?
  3. Referans: siparişleri ayrı koleksiyona taşı ve $lookup ile birleştir. explain çıktısını karşılaştır.
  4. 16 MB sınırı: bir belgeye döngüyle 200 000 eleman $push et. Ne zaman hata aldın? Kova desenine çevirip tekrarla.
  5. Güncelleme tuzağı: updateOne ile bir kez $set kullanarak, bir kez doğrudan belge vererek güncelle. İkincisinde diğer alanlara ne oldu?
  6. Alan yokluğu: bazı belgelerde olmayan bir alanı $exists: false ve : null ile ara — sonuçlar aynı mı? (6. dersteki null ≠ eksik.)
  7. İndeks: 200 000 belge üret, filtreli sorguyu indekssiz çalıştır ve explain("executionStats") çıktısında COLLSCAN gör. İndeks açıp IXSCAN'e geç.
  8. ESR: eşitlik + sıralama + aralık içeren bir sorgu yaz. İndeksi önce yanlış sırayla (aralık ortada), sonra ESR sırasıyla kur; SORT aşaması kayboldu mu?
  9. Çok anahtarlı: iki ayrı dizi alanını tek bileşik indekse koymayı dene ve hata mesajını oku.
  10. TTL: expireAfterSeconds ile bir oturum koleksiyonu kur ve belgelerin kendiliğinden silinmesini izle.
  11. Toplama: şehir bazlı toplam ciro hattını yaz. $match'i önce sona, sonra başa koyup explain ile karşılaştır.
  12. Atomiklik: gömülü sipariş satırı ekleme işlemini tek updateOne ile yap — transaction gerekti mi? Sonra iki ayrı koleksiyona yazan bir işlem kurup transaction'sız hata senaryosunu dene.
  13. Şema doğrulama: $jsonSchema doğrulayıcı ekleyip kurala uymayan belge eklemeyi dene.
  14. Karşılaştırma: aynı belgeyi PostgreSQL'de jsonb sütununda sakla, GIN indeksiyle sorgula ve iki yaklaşımın kod/sorgu farkını yarım sayfada yaz.

10Kendini sına

NoSQL nedir, hangi problemi çözer? İlişkiselden ne zaman vazgeçilir?

NoSQL, ilişkisel modelin iki sınırına cevap veren veritabanı aileleridir: yatay ölçeklemenin zorluğu (join ve transaction makineler arasına iyi yayılmaz) ve büyük tablolarda şema değişikliğinin pahalı olması. Dört aile vardır: doküman (MongoDB), anahtar-değer (Redis, DynamoDB), sütun ailesi (Cassandra) ve graf (Neo4j); her biri farklı bir erişim deseni için tasarlanmıştır. Karşılığında verilen ödün nettir: gerçek join, veritabanı düzeyinde kısıtlar (yabancı anahtar, benzersizlik, CHECK) ve çok kayıtlı tutarlılık ya yoktur ya pahalıdır; birçoğu ACID yerine BASE dengesini seçer — yazma her zaman kabul edilir, kopyalar bir süre sonra tutarlı olur. İlişkiselden vazgeçmenin meşru gerekçeleri: baştan yatay ölçekleme ihtiyacı, doğal olarak belge biçiminde ve birlikte okunan veri, gerçekten öngörülemez şema, çok yüksek yazma hacmi. "Şema değiştirmek zor geliyor" tek başına yeterli bir gerekçe değildir; PostgreSQL'de jsonb çoğu durumda aynı esnekliği ilişkisel garantileri kaybetmeden verir.

MongoDB'de veriyi gömmek mi referans vermek mi? Karar ölçütün ne?

Ölçütler: veriler birlikte mi okunuyor (evetse göm), ilişkinin büyüme sınırı var mı (yoksa gömme), bağımsız olarak sık güncelleniyor mu (evetse referans), ve tekrar kabul edilebilir mi. Gömmenin kazancı tek okumada tüm veriyi almak ve tek doküman güncellemesinin atomik olmasıdır — ilişkisel dünyada transaction gerektiren bir işlem doğru modellenmiş bir belgede tek atomik güncellemeye iner. Sert sınır 16 MB doküman boyutudur: "yorumları gönderiye göm" gibi kararlar popüler bir kayıtta bir gün yazma hatası verir; ayrıca dizi büyüdükçe her güncelleme belgenin tamamını yeniden yazmaya yaklaşır. Kural: sınırsız büyüyebilen hiçbir şeyi gömme. Zaman serisi ve olay verisinde kova deseni (sabit sayıda ölçümü tek belgede toplama) kullanılır. Referans verdiğinde de $lookup'ın gerçek bir join olmadığını unutma: her sol belge için sağ koleksiyonda arama yapar, hash veya merge join seçeneği yoktur — modeli join yazacağını varsayarak kurarsan MongoDB'yi yavaş bir ilişkisel veritabanı gibi kullanmış olursun.

MongoDB'de transaction var mı? Tutarlılık nasıl yönetilir?

Tek doküman üzerindeki güncelleme her zaman atomiktir; doğru modelleme çoğu ihtiyacı burada karşılar. Çok doküman transaction'ı 4.0'dan (parçalı kümelerde 4.2'den) beri vardır ama üç şartı bilinmelidir: kopya kümesi gerektirir (tek düğümde çalışmaz), varsayılan 60 saniye sınırı vardır ve gerçek bir performans maliyeti getirir. Yani transaction bir kaçış kapısıdır, tasarımın merkezi değil — sürekli ihtiyaç duyuluyorsa model yanlış kurulmuştur ya da ilişkisel veritabanı daha doğru seçimdir. Tutarlılık ayrıca iki ayarla yönetilir: writeConcern (w:1 hızlıdır ama birincil çökerse veri kaybı mümkündür, w:"majority" dayanıklıdır) ve readPreference (primary her zaman güncel, secondary okuma yükünü dağıtır ama bayat veri döndürebilir — kullanıcı az önce kaydettiğini görmeyebilir). CAP açısından doğru cevap "yapılandırmaya bağlı"dır: bölünme anında tutarlılık ile erişilebilirlik arasındaki tercihi bu ayarlarla istek bazında kaydırırsın.

MongoDB'de indeksleme ve ESR kuralı nedir?

MongoDB de B-ağacı indeksleri kullanır; indeks yoksa koleksiyon taranır (COLLSCAN) ve explain("executionStats") ile plan okunur. Bileşik indekste alan sırası belirleyicidir ve doğru sıra ESR ile özetlenir: önce Eşitlik koşullu alanlar, sonra Sıralama alanları, en sonda Range (aralık) koşullu alanlar. Sıralamayı aralıktan önce koymazsan motor sonuçları bellekte sıralamak zorunda kalır ve bellek içi sıralama sınırına çarpabilirsin. Özel indeks türleri: dizi alanlarını indeksleyen çok anahtarlı indeks (her eleman için bir giriş üretir; bir bileşik indekste birden fazla dizi alanı bulunamaz), süresi dolan belgeleri otomatik silen TTL indeksi (oturum, geçici belirteç), kısmi indeks (soft delete benzersizliği için), benzersiz indeks ve metin indeksi. SARGability kuralı burada da geçerlidir: $regex yalnızca başa sabitlenmişse (^) indeks kullanabilir.

Sharding nedir, parça anahtarı seçerken neye dikkat edilir?

Parçalama, bir koleksiyonu parça anahtarına göre birden çok makineye dağıtarak yatay ölçekleme sağlar; MongoDB'nin ilişkisel motorlara göre en belirgin üstünlüğü bunun yerleşik olmasıdır. Kritik karar parça anahtarıdır: monoton artan bir alan (zaman damgası ya da ObjectId — ki içinde oluşturulma zamanı taşır) seçilirse tüm yeni yazmalar aynı parçaya düşer, buna sıcak nokta denir ve ölçekleme hiç işe yaramaz. İyi bir parça anahtarı yüksek kardinaliteli, düzgün dağılmış ve sorguların çoğunda filtre olarak kullanılan alandır — filtrede yoksa her sorgu tüm parçalara dağılır (scatter-gather) ve gecikme artar. Anahtarı sonradan değiştirmek 5.0+ ile mümkündür ama pahalı ve risklidir, bu yüzden baştan doğru seçilmesi gereken kararlardandır.

MongoDB ile PostgreSQL jsonb arasında nasıl seçim yaparsın?

İkisi de esnek belge saklar ve ikisi de GIN benzeri indekslerle sorgulanabilir. Fark, çevresindeki garantilerdedir. PostgreSQL + jsonb: aynı veritabanında yabancı anahtar, benzersizlik ve CHECK kısıtları, gerçek join (üç algoritma), ucuz ve doğal çok kayıtlı transaction, tek bir veritabanı işletme kolaylığı ve ekibin zaten bildiği SQL. MongoDB: yerleşik yatay ölçekleme (sharding), doküman modelinin doğal olduğu ve birlikte okunan veri, alan eklemenin bedava olması, çok yüksek yazma hacmi. Pratik karar: veri kısmen esnek ama ilişkiler ve tutarlılık önemliyse jsonb çoğu ekip için daha az riskli seçimdir; MongoDB'yi baştan ölçekleme ihtiyacı, gerçekten öngörülemez şema ve belge biçiminde doğal veri gerekçelendirir. İkisi rakip olmak zorunda değildir — yaygın mimari işlemsel veriyi ilişkiselde, olay/katalog verisini doküman veritabanında tutmaktır; ama her ek depo işletim, yedekleme ve tutarlılık maliyeti getirir.

11Sınav köşesi

Yazılıda nasıl sorulur

(1) NoSQL nedir, türleri nelerdir. (2) SQL ile NoSQL karşılaştırması — en klasik soru. (3) MongoDB'de koleksiyon, doküman kavramları. (4) CAP teoremi nedir. (5) ACID ile BASE farkı. (6) Gömme (embedding) ile referans arasındaki fark. (7) MongoDB'de indeksleme. (8) Ne zaman NoSQL tercih edilir. (9) Aggregation pipeline nedir. (10) MongoDB'de transaction desteği var mıdır.

Sözlüde nasıl anlatılır — 2 dakikalık taslak

Düzen: (1) Çerçeve — "NoSQL, ilişkisel modelin yatay ölçekleme ve şema esnekliği sınırlarına verilen cevaptır; doküman, anahtar-değer, sütun ailesi ve graf olmak üzere dört ailesi vardır ve her biri farklı bir erişim deseni içindir." (2) Model — "MongoDB'de tasarımın tek gerçek kararı gömmek mi referans vermek mi olduğudur; birlikte okunan ve sınırlı büyüyen veriyi gömerim, çünkü tek doküman güncellemesi atomiktir." (3) Kıdem vuruşu — "sınırsız büyüyen diziyi asla gömmem, 16 MB doküman sınırı vardır ve dizi büyüdükçe her güncelleme belgeyi yeniden yazmaya yaklaşır; zaman serisinde kova desenini kullanırım. $lookup'ı gerçek bir join sanmam, döngüsel çalışır." (4) Tutarlılık — "çok doküman transaction'ı 4.0'dan beri var ama kopya kümesi ister, süre sınırı ve maliyeti vardır; onu kaçış kapısı olarak görürüm. İkincil düğümden okumak bayat veri demektir ve bunu bilerek yaparım." (5) Seçim — "esnek veri gerekiyor ama ilişkiler ve tutarlılık önemliyse PostgreSQL'de jsonb kullanırım; MongoDB'yi baştan sharding ihtiyacı, gerçekten öngörülemez şema ve çok yüksek yazma hacmi gerekçelendirir. Parça anahtarını monoton artan bir alandan seçmem, sıcak nokta oluşur."