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
jsonbkarşılaştırması - .NET sürücüsü:
MongoClientömrü veDbContextile 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:
| Aile | Veri modeli | Tipik kullanım | Örnek |
|---|---|---|---|
| Doküman | İç içe JSON benzeri belgeler | Katalog, içerik, olay, kullanıcı profili | MongoDB, Couchbase |
| Anahtar-değer | Anahtar → opak değer | Önbellek, oturum, sayaç, kuyruk | Redis, DynamoDB |
| Sütun ailesi | Satır anahtarı + sütun grupları | Çok yüksek yazma hacmi, zaman serisi | Cassandra, HBase |
| Graf | Düğüm ve kenarlar | Sosyal ağ, öneri, yol bulma | Neo4j |
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çüt | Göm (embed) | Referans ver |
|---|---|---|
| Birlikte okunuyor mu | Evet — tek okumada gelsin | Hayır — ayrı sorgula |
| İlişki türü | Bire-az (adres, sipariş satırı) | Bire-çok-çok, çok-çok |
| Büyüme | Sınırlı olmalı | Sınırsız büyüyebilir |
| Güncelleme sıklığı | Ana belgeyle birlikte değişiyor | Bağımsız değişiyor |
| Tekrar | Kabul 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:
| Ayar | Seçenek | Sonucu |
|---|---|---|
writeConcern | w:1 | Birincil yazdı, yeter — hızlı; birincil çökerse veri kaybı mümkün |
w:"majority" | Çoğunluk onayladı — dayanıklı, daha yavaş (varsayılan) | |
readPreference | primary | Her zaman güncel (varsayılan) |
secondary | Okuma yükü dağılır ama bayat veri gelebilir | |
readConcern | majority / linearizable | Geri 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:
| Konu | PostgreSQL + jsonb | MongoDB |
|---|---|---|
| Esnek belge | Var (GIN indeksli) | Var — modelin merkezi |
| İlişkisel kısıtlar | Var — aynı veritabanında FK, CHECK, benzersizlik | Yok — uygulama sorumluluğu |
| Join | Gerçek join, üç algoritma | $lookup — yalnız döngüsel |
| Çok kayıtlı transaction | Doğal ve ucuz | Mümkün ama kaçış kapısı |
| Yatay ölçekleme | Zor (bölümleme, Citus gibi eklentiler) | Yerleşik (sharding) |
| Şema evrimi | Göç gerekir | Alan eklemek bedava; okuma kodu uyumlu olmalı |
| Ekip bilgisi | SQL zaten biliniyor | Yeni 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
- Bağlan:
mongoshile bağlan;show dbs,use kariyer,db.musteri.insertOne({...}),db.musteri.find().pretty()komutlarını dene. - 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?
- Referans: siparişleri ayrı koleksiyona taşı ve
$lookupile birleştir.explainçıktısını karşılaştır. - 16 MB sınırı: bir belgeye döngüyle 200 000 eleman
$pushet. Ne zaman hata aldın? Kova desenine çevirip tekrarla. - Güncelleme tuzağı:
updateOneile bir kez$setkullanarak, bir kez doğrudan belge vererek güncelle. İkincisinde diğer alanlara ne oldu? - Alan yokluğu: bazı belgelerde olmayan bir alanı
$exists: falseve: nullile ara — sonuçlar aynı mı? (6. dersteki null ≠ eksik.) - İndeks: 200 000 belge üret, filtreli sorguyu indekssiz çalıştır ve
explain("executionStats")çıktısındaCOLLSCANgör. İndeks açıpIXSCAN'e geç. - 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;
SORTaşaması kayboldu mu? - Çok anahtarlı: iki ayrı dizi alanını tek bileşik indekse koymayı dene ve hata mesajını oku.
- TTL:
expireAfterSecondsile bir oturum koleksiyonu kur ve belgelerin kendiliğinden silinmesini izle. - Toplama: şehir bazlı toplam ciro hattını yaz.
$match'i önce sona, sonra başa koyupexplainile karşılaştır. - Atomiklik: gömülü sipariş satırı ekleme işlemini tek
updateOneile yap — transaction gerekti mi? Sonra iki ayrı koleksiyona yazan bir işlem kurup transaction'sız hata senaryosunu dene. - Şema doğrulama:
$jsonSchemadoğrulayıcı ekleyip kurala uymayan belge eklemeyi dene. - Karşılaştırma: aynı belgeyi PostgreSQL'de
jsonbsü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."
