MongoDB'de write concern bir yazmanın kaç üye tarafından ve hangi kalıcılık düzeyinde onaylanacağını, read concern bir okumanın ne kadar kalıcı ve tutarlı veri döndüreceğini, read preference ise okumanın replica set'in hangi üyesine gideceğini belirler. Replica set ve sharded kümelerde varsayılan yazma onayı çoğu durumda { w: "majority" }, varsayılan okuma ise primary üzerinden ve "local" read concern ile yapılır. Bu üç ayarı birlikte seçerek tutarlılık, gecikme ve erişilebilirlik arasında denge kurarsınız.

Bu yazıda her bir ayarın seçeneklerini, "çoğunluk" sayısının nasıl hesaplandığını, arbiter içeren kümelerdeki tuzakları, wtimeout davranışını, read concern seviyelerini ve read preference modlarını örneklerle anlatıyoruz. Replica set kurulumunu henüz yapmadıysanız önce MongoDB replica set kurulumu yazısını okuyun. Serinin diğer yazıları MongoDB öğrenme rehberinde listelenir.

AyarNeyi belirlerTipik değer
Write concernYazmanın kaç üyede ve diskte onaylanacağıw: majority
Read concernOkunan verinin kalıcılık ve yalıtım düzeyilocal veya majority
Read preferenceOkumanın hangi üyeye yönlendirileceğiprimary

Write Concern: w, j ve wtimeout

Write Concern: w, j ve wtimeout: "majority": Hesaplanan çoğunluk kadar veri tutan ve oy veren üyenin değişikliği kendi oplog'una kalıcı olarak..., 1: Yalnızca standalone sunucunun...

Write concern üç alandan oluşan bir belgedir: w, j ve wtimeout. w yazmanın kaç mongod örneğine ya da hangi etiketli üyelere ulaştığının onaylanmasını ister. j yazmanın diskteki journal'a yazıldığının onaylanmasını ister. wtimeout ise onayı sonsuza kadar beklememek için milisaniye cinsinden bir süre sınırı koyar. Bir yazma işlemine write concern'i seçenek olarak verirsiniz:

javascript
db.siparisler.insertOne(
  { musteri: "ayse", tutar: 250 },
  { writeConcern: { w: "majority", j: true, wtimeout: 5000 } }
)

Yazma başarılı olduğunda belgelerdeki insertOne yanıtı acknowledged: true ve insertedId alanlarını içerir. Write concern'in fark yarattığı nokta, bu yanıtın ne zaman döndüğüdür.

w değerleri

  • w: "majority": Hesaplanan çoğunluk kadar veri tutan ve oy veren üyenin değişikliği kendi oplog'una kalıcı olarak yazdığının onaylanmasını ister. Çoğu kurulumda varsayılan budur. Gizli (hidden), gecikmeli (delayed) ve priority 0 üyeler oy hakları varsa bu onaya katkı verebilir; gecikmeli bir üye ancak yapılandırılan secondaryDelaySecs süresi dolduktan sonra onay verir.
  • w: 1: Yalnızca standalone sunucunun veya primary'nin onayını ister. Primary, yazma secondary'lere ulaşmadan geri çekilirse bu yazma geri alınabilir (rollback).
  • w: 0: Hiç onay istemez; yine de soket ve ağ hataları uygulamaya bildirilebilir. Aynı anda j: true verirseniz j: true baskın gelir ve primary'den onay istenir.
  • w: 2, w: 3 gibi sayılar: Primary ile birlikte gereken sayıda veri tutan üyenin onayını ister. Bu üyelerin oy veren üye olması gerekmez. Üç üyeli bir kümede w: 3 tüm üyeleri bekler.
  • Özel bir ad: settings.getLastErrorModes içinde tanımlanan, etiketlere dayalı bir yazma kuralıdır ve örneğin farklı veri merkezlerinden onay istemek için kullanılır.

Çoğunluk Nasıl Hesaplanır? Arbiter Tuzağı

"majority" için gereken üye sayısı iki değerden küçük olanıdır: arbiter'lar dahil tüm oy veren üyelerin çoğunluğu ve veri tutan oy veren üyelerin toplam sayısı. rs.status() çıktısındaki writeMajorityCount alanı bu hesaplanmış değeri doğrudan gösterir.

DüzenOy veren üyelerin çoğunluğuVeri tutan oy veren üyeHesaplanan çoğunluk
Primary + 2 secondary (PSS)232
Primary + secondary + arbiter (PSA)222

Tablo ilk bakışta aynı sonucu veriyor gibi görünür ama anlamları çok farklıdır. PSS düzeninde çoğunluk için primary ve herhangi bir secondary yeterlidir; bir secondary kapansa bile yazmalar onaylanır. PSA düzeninde arbiter veri tutmadığı için iki onayın ikisi de veri tutan üyelerden gelmek zorundadır. Secondary kapandığında "majority" yazmaları zaman aşımına uğrar ya da hiç onaylanmaz. Belgeler bu nedenle mümkünse arbiter yerine veri tutan bir üye kullanmanızı önerir.

Arbiter içeren kümeler için örtük varsayılan da değişir. Oy veren çoğunluk, oy veren üye sayısının yarısının aşağı yuvarlanıp bir eklenmesiyle bulunur. Arbiter varsa ve veri tutan üye sayısı bu çoğunluktan büyük değilse örtük varsayılan write concern { w: 1 } olur; diğer tüm durumlarda { w: "majority" } olur. Belgelerdeki iki örnek bunu netleştirir:

Veri tutanArbiterOy verenOy çoğunluğuÖrtük varsayılan
2132w: 1
4153w: majority

j Seçeneği ve Journal

j: true, w ile belirtilen üyelerin yazmayı diskteki journal'a kaydettiğinin onaylanmasını ister ve anında bir journal eşitlemesine neden olur. Belgeler, tek başına j: true değerinin primary failover kaynaklı geri almaya karşı garanti vermediğini vurgular; dayanıklılık için çoğunluk onayı da gerekir.

j belirtilmediğinde w: "majority" davranışını replica set yapılandırmasındaki writeConcernMajorityJournalDefault ayarı belirler. Bu ayar varsayılan olarak true olduğundan çoğunluk yazmaları pratikte j: true gibi diske eşitlenmiş olarak onaylanır. w: 1 veya sayısal değerlerde ise j verilmemişse onay bellekteki yazmadan sonra gelir. Journal kapalı çalışan bir mongod örneğine j: true göndermek hata üretir.

wtimeout Ne Yapar, Ne Yapmaz?

wtimeout, yazma primary'de başarıyla uygulandıktan sonra yeterli sayıda üyeye yayılması için beklenecek süredir; w değeri 1 veya daha küçükse hiç uygulanmaz. Süre dolarsa MongoDB bir write concern hatası döndürür. Belgelerdeki insertOne örneğinde bu hata kodu 64 ve waiting for replication timed out mesajıyla gelir.

Burada en sık yanlış anlaşılan nokta şudur: zaman aşımı yazmayı geri almaz. Primary'de yapılan değişiklik yerinde kalır ve büyük olasılıkla birazdan diğer üyelere de ulaşır. Uygulamanız bu hatayı "yazma olmadı" diye yorumlayıp işlemi tekrarlarsa aynı kaydı iki kez eklemeye çalışabilir; bu da benzersiz bir indeks varsa E11000 duplicate key hatasına yol açar. wtimeout hiç verilmezse ve istenen onay düzeyine ulaşılamıyorsa yazma süresiz bekler; 0 değeri de seçeneği hiç vermemekle aynıdır. Primary üzerindeki işlemin kendisine süre koymak için ise maxTimeMS() kullanılır.

javascript
try {
  db.siparisler.insertOne(
    { musteri: "mehmet", tutar: 90 },
    { writeConcern: { w: "majority", wtimeout: 2000 } }
  )
} catch (e) {
  // write concern hatası: veri primary'de yazılmış olabilir, körü körüne tekrar etmeyin
  print(e)
}

MongoDB 8.0 Sonrası: Çoğunluk Onayı ve Secondary Okumaları

Belgelere göre MongoDB 8.0'dan itibaren { w: "majority" } yazmaları, veri tutan üyelerin çoğunluğu oplog girdisini kalıcı olarak yazdığında onaylanır; üyeler değişikliği daha sonra eşzamansız olarak uygular. Önceki sürümler onay vermeden önce uygulamanın bitmesini beklerdi. Bunun pratik sonucu şudur: onaydan hemen sonra bir secondary'den okuma yaparsanız, o secondary değişikliği henüz uygulamamış olabilir. Secondary'lerden okuyup kendi yazdıklarınızı hemen görmeniz gerekiyorsa işlemleri causally consistent (nedensel tutarlı) bir oturumda çalıştırın. Belgeler, nedensel tutarlılığın ancak okumalarda "majority" read concern ve yazmalarda "majority" write concern kullanıldığında garanti edildiğini belirtir.

Read Concern Seviyeleri

Read Concern Seviyeleri: local, available, majority, linearizable

Read concern bir okumanın döndürdüğü verinin ne kadar kalıcı olduğunu belirler. Beş seviye vardır:

SeviyeDöndürdüğü veriNot
localÖrnekteki en güncel veri, çoğunluğa yazıldığı garanti değilPrimary ve secondary okumalarında varsayılan
availablelocal gibi, geri alınabilir veriNedensel oturum ve transaction içinde kullanılamaz
majorityÇoğunluk tarafından onaylanmış veriArıza olsa bile geri alınmaz
linearizableOkuma başlamadan önce tamamlanan tüm çoğunluk yazmalarını yansıtırYalnızca primary üzerinde
snapshotBelirli bir anın çoğunluk onaylı görüntüsüTransaction ve bazı okuma komutlarında

"available" sharded kümelerde en düşük gecikmeyi verir ama belgelerin uyardığı gibi sharded bir koleksiyondan sahipsiz (orphaned) belgeler döndürebilir; bu riski istemiyorsanız "local" kullanın. "majority", üyenin bellekteki çoğunluk onay noktasındaki görüntüsünden okur ve belgelere göre diğer seviyelerle karşılaştırılabilir bir performansa sahiptir; replica set'in WiredTiger kullanması gerekir. "linearizable" eşzamanlı yazmaların çoğunluğa yayılmasını bekleyebileceği için, üyelerin çoğunluğu erişilemez durumdayken süresiz beklememesi adına her zaman maxTimeMS ile birlikte kullanılmalıdır.

mongosh içinde bir find() sorgusu için seviyeyi imleç (cursor) yöntemiyle verirsiniz. Destekleyen komutlarda ise readConcern: { level: ... } seçeneği kullanılır:

javascript
db.siparisler.find({ musteri: "ayse" }).readConcern("majority")

db.siparisler.aggregate(
  [ { $match: { tutar: { $gte: 100 } } } ],
  { readConcern: { level: "majority" } }
)

Read Preference Modları

Read preference, istemcinin okuma işlemini hangi üyeye göndereceğini seçer. Varsayılan mod primary'dir. primary dışındaki tüm modlar, replikasyon eşzamansız olduğu için eski (stale) veri döndürebilir; uygulamanızın buna dayanabildiğinden emin olun.

ModDavranış
primaryYalnızca primary. Primary yoksa okuma hata verir.
primaryPreferredPrimary varsa ondan, yoksa uygun secondary üyelerden okur.
secondaryYalnızca secondary üyelerden okur. Uygun secondary yoksa hata verir.
secondaryPreferredUygun secondary varsa ondan, yoksa primary üyeden okur.
nearestAğ gecikmesi kabul edilebilir pencere içindeki herhangi bir üyeden okur.

Read preference'a etiket kümeleri (tag set) ve maxStalenessSeconds eklenebilir. maxStalenessSeconds bir secondary'nin ne kadar geride kalmışsa artık okuma için seçilmeyeceğini belirtir; en az 90 saniye olmalıdır, daha küçük bir değer hata üretir. Bu iki ayar primary moduyla birlikte kullanılamaz. mongosh içinde imleç üzerinde readPref() çağrılır:

javascript
db.raporlar.find({ yil: 2026 }).readPref("secondary")

db.raporlar.find({}).readPref("secondary", [ { bolge: "ist" }, { } ])

İkinci örnekte önce bolge: "ist" etiketli secondary'ler aranır; bulunamazsa boş belge tüm uygun üyelerle eşleşir. Read preference verinin görünürlüğünü değiştirmez: primary'den okurken bile çoğunluğa henüz yayılmamış yazmaları görebilirsiniz. Bunu engelleyen ayar read preference değil read concern'dir.

Bağlantı Dizesinde ve Küme Genelinde Ayarlamak

Bu ayarları her işlemde tek tek vermek yerine bağlantı dizesinde verebilirsiniz. Belgelerdeki seçenek adları w, wtimeoutMS, journal, readConcernLevel, readPreference, maxStalenessSeconds ve readPreferenceTags şeklindedir. Bağlantı dizesi yapısı için connection string URI rehberine bakın.

bash
mongodb://db1.ornek.lan,db2.ornek.lan,db3.ornek.lan/?replicaSet=rs0&w=majority&wtimeoutMS=5000&readConcernLevel=majority

Küme genelindeki varsayılanı değiştirmek için setDefaultRWConcern yönetim komutu kullanılır. Komut admin veritabanında; replica set'te primary üzerinde, sharded kümede bir mongos üzerinde çalıştırılır. Varsayılan write concern ayarlarken w alanını mutlaka belirtmelisiniz. Geçerli değerleri görmek için getDefaultRWConcern komutunu kullanın; yanıtta defaultWriteConcern, defaultReadConcern ve bunların kaynağını gösteren alanlar bulunur.

javascript
db.adminCommand({
  setDefaultRWConcern: 1,
  defaultWriteConcern: { w: "majority", wtimeout: 5000 },
  defaultReadConcern: { level: "majority" }
})

db.adminCommand({ getDefaultRWConcern: 1 })

Hangi Kombinasyonu Seçmeli?

Hangi Kombinasyonu Seçmeli? Kendi yazdığını primary'den okumak, Raporlama gibi gecikmeye toleranslı okumalar, Kayıp kabul edilebilir yüksek hacimli loglar, Çoğunluktan büyük...
  • Kendi yazdığını primary'den okumak: Belgeler "majority" read concern ile { w: "majority" } write concern'i önerir. Seçim sırasında eski primary'ye giden bir okumanın, yeni primary'nin kabul ettiği çoğunluk onaylı bir yazmayı içermeyebileceğini de not eder.
  • Raporlama gibi gecikmeye toleranslı okumalar: secondaryPreferred veya secondary ile birlikte maxStalenessSeconds kullanmak primary üzerindeki yükü azaltır.
  • Kayıp kabul edilebilir yüksek hacimli loglar: w: 1 düşük gecikme sağlar ama failover'da geri alınabilir; finansal veya sipariş verisi için uygun değildir.
  • Çoğunluktan büyük sayısal w: Belgeler, w değerini hesaplanan çoğunluktan büyük seçerseniz varsayılan ayarlarda j: true eklemenizi önerir; aksi halde yazma çoğunlukta kalıcı olmadan onay alabilirsiniz.

local veritabanı write concern desteklemez; bu veritabanındaki koleksiyonlara yapılan işlemlerde verdiğiniz write concern sessizce yok sayılır.

MongoDB 9.0 Notu: Change Stream ve Read Preference

MongoDB 9.0 uyumluluk notlarına göre readPreference değeri primary veya secondary olan bir change stream, seçim sonrasında bağlı olduğu üye artık bu tercihe uymuyorsa getMore çağrısında yeniden başlatılabilir InterruptedDueToReplStateChange hatasını (kod 11602) döndürür. Önceki sürümlerde imleç aynı üyeden sonuç döndürmeye devam ediyordu. Uyumlu sürücüler ve mongos son resume token'dan otomatik devam eder; elle yazılmış getMore döngüleri ve mongosh ise resumeAfter ile devam etmelidir. Ayrıntılar için change streams ve MongoDB 9.0 yenilikleri yazılarına bakın.

Sık Yapılan Hatalar

  • PSA kümesinde "majority" kullanıp wtimeout vermemek: secondary bakımdayken yazmalar süresiz bekler.
  • wtimeout hatasını yazmanın başarısız olduğu şeklinde yorumlayıp işlemi tekrar etmek.
  • Transaction içindeki tek tek işlemlere write concern veya read concern vermek.
  • secondary okumasından hemen önceki yazmanın sonucunu görmeyi beklemek.
  • maxStalenessSeconds değerini 90 saniyeden küçük vermek.
  • Hangi üyenin primary olacağını oy sayısıyla ayarlamaya çalışmak; bunun için priority kullanılır.

Sık Sorulan Sorular

Standalone sunucuda write concern işe yarar mı?

Kısmen. Standalone bir sunucu yazmayı bellekte veya journal'da onaylar. Belgelerdeki tabloya göre w: 1 ile j belirtilmezse onay bellekteki yazmadan sonra gelir, j: true ile diskteki journal'dan sonra gelir. Çoğunluk ve secondary kavramları ise yalnızca replica set'te anlam taşır.

Varsayılan write concern neden bazen w: 1 oluyor?

Kümenizde arbiter varsa ve veri tutan üye sayısı oy çoğunluğundan büyük değilse örtük varsayılan { w: 1 } olur. Tipik PSA kümesi bu durumdadır. setDefaultRWConcern ile açık bir varsayılan belirleyebilirsiniz.

Read concern "majority" yavaş mıdır?

Belgeler, "majority" seviyesinin bellekteki çoğunluk onay noktasından okuduğunu ve performansının diğer read concern seviyeleriyle karşılaştırılabilir olduğunu belirtir. Sorguyu yavaşlatmaz, yalnızca istemciye dönen veriyi değiştirir: çoğunlukta kalıcı olmayan bir değişikliği göstermez, bu yüzden en son yazmayı henüz göstermeyebilir.

Secondary'den okumak primary'nin yükünü her zaman azaltır mı?

Okuma yükünü dağıtır ama veri eskiliği maliyetiyle gelir ve secondary'ler de aynı yazma yükünü oplog üzerinden uygular. Yalnızca eski veriye toleranslı okumaları secondary'lere yönlendirin.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: MongoDB öğrenme rehberi · Replica set kurulumu · Transaction kullanımı · Sharding ve shard key · Change streams · Connection string URI