Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124

Bu yazıda NoSQL veritabanlarının neden popüler olduğunu, hangi problemlere iyi çözüm sunduğunu ve hangi durumlarda SQL/RDBMS tarafının hâlâ daha doğru tercih olabileceğini öğreneceksin. Özellikle belge tabanlı veri modeli, yatay ölçekleme, Cassandra tarzı dağıtık mimari, quorum mantığı ve SSTable/compaction yapısını sade bir akışla ele alacağız.
Bu yazıda NoSQL veritabanlarının neden popüler olduğunu, hangi problemlere iyi çözüm sunduğunu ve hangi durumlarda SQL/RDBMS tarafının hâlâ daha doğru tercih olabileceğini öğreneceksin. Özellikle belge tabanlı veri modeli, yatay ölçekleme, Cassandra tarzı dağıtık mimari, quorum mantığı ve SSTable/compaction yapısını sade bir akışla ele alacağız.
SQL veritabanlarında veri çoğunlukla tablolar arasında ilişkilendirilerek tutulur. Yani şöyle düşün: bir müşterinin temel bilgileri ayrı tabloda, teslimat adresleri ayrı tabloda, siparişleri başka bir tabloda olabilir. Bu yapı düzenlidir, ilişki kurmak kolaydır ve veri tutarlılığı güçlüdür.
NoSQL tarafında ise veri çoğu zaman bir bütün olarak, belge veya anahtar-değer formatında saklanır. Bunu şöyle hayal edebilirsin: bir e-ticaret kullanıcısının profil bilgisi, varsayılan adresi, tercihleri ve bazı ayarları tek bir JSON benzeri belge içinde durabilir. Uygulama bu bilgiyi genellikle birlikte yazıyor ve birlikte okuyorsa, bu model oldukça pratik hale gelir.
flowchart LR
subgraph SQL["SQL / İlişkisel Model"]
U["Kullanıcı Tablosu"]
A["Adres Tablosu"]
O["Sipariş Tablosu"]
U -->|"foreign key"| A
U -->|"foreign key"| O
end
subgraph NoSQL["NoSQL / Belge Modeli"]
D["Tek Kullanıcı Belgesi
profil + adres + tercihler"]
endBu diyagram SQL tarafında verinin ilişkilerle bölündüğünü, NoSQL tarafında ise ilgili alanların tek bir belge içinde tutulabildiğini gösterir. NoSQL burada özellikle birlikte okunan ve birlikte yazılan veriler için daha doğal bir model sunar.
Aslında mesele sadece “NoSQL hızlıdır” demek değildir. Önemli olan, verinin uygulamanın kullanım şekline uygun saklanmasıdır. Eğer sistem bir kullanıcı profilini her okuduğunda profilin çoğu alanına ihtiyaç duyuyorsa, o alanların tek belgede durması okuma ve yazma tarafında avantaj sağlayabilir.
Teknik olarak buna denormalizasyon denir. Yani şöyle düşün: ilişkisel modelde aynı bilgi farklı tablolardan join ile toplanırken, NoSQL modelinde bu bilgi önceden aynı belgeye yerleştirilmiş olabilir. Bu da bazı sorguları basitleştirir, fakat verinin tekrar etmesi veya güncelleme maliyetinin artması gibi bedelleri vardır.
flowchart TD
R["Uygulamadan gelen kayıt isteği"] --> C{"Veri birlikte mi kullanılıyor?"}
C -->|"Evet"| N["Tek belge olarak sakla"]
C -->|"Hayır"| S["İlişkileri ayrı modelle"]
N --> Q["Basit okuma ve yazma"]
S --> J["Join veya ilişki tabanlı sorgu"]Bu akış, NoSQL kararının veri erişim şekline bağlı olduğunu anlatır. Veri birlikte yazılıp birlikte okunuyorsa belge modeli sadeleşir; veri sık sık farklı ilişkilerle sorgulanıyorsa SQL modeli daha güçlü kalır.
NoSQL veritabanlarının güçlü taraflarından biri esnek şemadır. Schema flexibility, yani şöyle düşün: her kaydın birebir aynı kolonlara sahip olması gerekmez. Bir kullanıcıda “favori kategori” alanı varken başka bir kullanıcıda bu alan hiç bulunmayabilir.
Bu, ürünün hızlı değiştiği dönemlerde avantaj sağlar. Yeni bir alan eklemek için büyük tablo migrasyonları yapmak yerine, yeni kayıtlar bu alanla yazılmaya başlanabilir. Fakat bu rahatlığın bir bedeli vardır: uygulama kodu farklı belge versiyonlarını doğru yorumlamak zorundadır.
flowchart LR
V1["Eski Belgeler
ad, e-posta"]
V2["Yeni Belgeler
ad, e-posta, tercih"]
APP["Uygulama Kodu"]
V1 --> APP
V2 --> APP
APP --> C{"Alan var mı?"}
C -->|"Var"| USE["Kullan"]
C -->|"Yok"| DEF["Varsayılan davranış uygula"]Bu diyagram esnek şemanın uygulama tarafına sorumluluk verdiğini gösterir. Veritabanı alanı zorunlu tutmadığında, eksik veya eski formatlı belgelerle baş etmeyi uygulama üstlenir.
Hayır. Ölçeklenebilirlik otomatik olarak NoSQL demek değildir. Büyük SQL sistemleri de yatay veya dikey olarak çok ciddi ölçeklere çıkabilir. NoSQL’in farkı, birçok ürünün en baştan dağıtık çalışma, partitioning ve replication gibi ihtiyaçlara göre tasarlanmış olmasıdır.
Partitioning, yani şöyle düşün: tüm veriyi tek makineye koymak yerine, anahtara göre farklı makineler arasında paylaştırırsın. Cassandra gibi sistemlerde bu yaklaşım mimarinin temelidir.
flowchart LR
K["Kayıt Anahtarı"] --> H["Hash Fonksiyonu"]
H --> R{"Hash Aralığı"}
R -->|"0-99"| N1["Node 1"]
R -->|"100-199"| N2["Node 2"]
R -->|"200-299"| N3["Node 3"]
R -->|"300-399"| N4["Node 4"]
R -->|"400-499"| N5["Node 5"]Bu diyagram bir anahtarın hash fonksiyonundan geçerek kümedeki uygun node’a yönlendirilmesini gösterir. Hash dağılımı dengeliyse yük de node’lar arasında daha dengeli yayılır.
Cassandra yalnızca veriyi bölmekle kalmaz, aynı verinin kopyalarını farklı node’larda da tutabilir. Replication factor, yani şöyle düşün: bir kaydın kaç farklı makinede saklanacağını belirleyen sayıdır.
Eğer replication factor 3 ise, bir veri birincil node’a yazıldıktan sonra iki farklı node’da daha kopyalanır. Böylece tek bir node arızalandığında veri tamamen kaybolmaz ve okuma istekleri başka kopyalardan karşılanabilir.
flowchart LR REQ["Yazma İsteği"] --> H["Hash ile Hedef Node Bul"] H --> N3["Node 3"] N3 --> N4["Node 4 Replica"] N4 --> N5["Node 5 Replica"] READ["Okuma İsteği"] --> H2["Aynı Anahtarı Hashle"] H2 --> N3 H2 -.-> N4 H2 -.-> N5
Bu diyagram Cassandra benzeri bir yapıda verinin hem hedef node’a hem de ardışık replica node’lara yazılmasını gösterir. Amaç hem erişilebilirliği artırmak hem de node kaybında veriyi korumaktır.
Dağıtık sistemlerde en zor konulardan biri, farklı node’ların aynı anda aynı veriyi görmeyebilmesidir. Quorum, yani şöyle düşün: bir cevabı doğru kabul etmek için kaç node’un aynı sonuca katılması gerektiğini belirleyen eşiktir.
Replication factor 3 olan bir sistemde quorum 2 seçilirse, üç kopyadan ikisinin cevabı yeterli görülebilir. Bu yaklaşım çoğu durumda pratik ve hızlıdır, fakat mutlak tutarlılık garantisi vermez. Daha yüksek quorum daha güçlü tutarlılık sağlar, ama hata anında sistemin cevap verememe ihtimalini artırır.
sequenceDiagram participant App as Uygulama participant N1 as Node 1 participant N2 as Node 2 participant N3 as Node 3 App->>N1: Veriyi oku App->>N2: Veriyi oku App->>N3: Veriyi oku N1-->>App: Sürüm A N2-->>App: Sürüm A N3-->>App: Sürüm B App->>App: Quorum sağlandıysa Sürüm A kabul edilir
Bu sequence diyagramı quorum kararının nasıl çalıştığını gösterir. Üç node’dan ikisi aynı veriyi döndürüyorsa sistem bu değeri kabul edebilir; ancak bu tercih tutarlılık ile erişilebilirlik arasında bilinçli bir dengedir.
SQL veritabanlarının güçlü olduğu alanlardan biri ACID özellikleridir. ACID, yani şöyle düşün: para transferi gibi kritik bir işlemde ya tüm adımlar başarılı olur ya da sistem hiçbir şey olmamış gibi geri döner. Atomicity, Consistency, Isolation ve Durability bu güvenliğin temel parçalarıdır.
NoSQL sistemlerinin bir kısmı transaction desteği sunsa da, çoğu dağıtık NoSQL tasarımı klasik ilişkisel veritabanı transaction modelinden farklı önceliklere sahiptir. Özellikle finansal kayıtlar, muhasebe hareketleri veya güçlü ilişki tutarlılığı isteyen sistemlerde SQL hâlâ çok güçlü bir tercihtir.
flowchart TD
TX["Kritik İşlem"] --> C{"Güçlü tutarlılık gerekli mi?"}
C -->|"Evet"| SQL["SQL / ACID Transaction"]
C -->|"Hayır, erişilebilirlik daha önemli"| NOSQL["NoSQL / Eventual Consistency"]
SQL --> OK1["İşlem ya tamamen olur ya geri alınır"]
NOSQL --> OK2["Sistem çoğu durumda hızlı ve erişilebilir kalır"]Bu diyagram veritabanı seçiminin teknik moda göre değil, iş kuralının tutarlılık ihtiyacına göre yapılması gerektiğini gösterir. Her iki model de doğru yerde güçlüdür.
NoSQL belge modeli, tüm belgeyi birlikte okumak için kullanışlıdır; fakat sadece tek bir alanı milyonlarca kayıt üzerinden analiz etmek istediğinde her zaman en iyi seçenek olmayabilir. Bunu şöyle hayal edebilirsin: her klasörün içinden yalnızca bir satırı almak istiyorsun ama önce klasörlerin tamamını açman gerekiyor.
Ayrıca ilişkiler ve join işlemleri SQL tarafındaki kadar doğal değildir. NoSQL’de ilişkisel bağlantılar çoğu zaman uygulama mantığıyla yönetilir. Bu da basit veri erişiminde hız kazandırırken, karmaşık raporlama ve ilişki sorgularında sistemi zorlayabilir.
flowchart TD
Q["Sorgu İhtiyacı"] --> R{"İlişki ve join yoğun mu?"}
R -->|"Evet"| SQL["SQL daha uygun"]
R -->|"Hayır"| D{"Belge bütün olarak mı kullanılıyor?"}
D -->|"Evet"| NOSQL["NoSQL uygun olabilir"]
D -->|"Hayır"| M["Modeli tekrar düşün"]Bu diyagram NoSQL’in her sorgu tipi için ideal olmadığını gösterir. Özellikle ilişkisel sorgular ve karmaşık join ihtiyaçları arttıkça SQL modeli daha anlamlı hale gelir.
Cassandra’nın dikkat çekici taraflarından biri yazma performansına odaklanan depolama modelidir. Gelen yazılar önce bellekteki yapıya ve log benzeri sıraya alınır, sonra disk üzerindeki immutable SSTable dosyalarına aktarılır.
Immutable, yani şöyle düşün: dosya oluşturulduktan sonra içeriği değiştirilmez. Yeni güncellemeler eski dosyayı düzenlemek yerine yeni kayıt olarak yazılır. Bu yazmayı hızlandırır, fakat zamanla aynı anahtara ait birden fazla sürüm oluşabilir. Compaction süreci bu dosyaları birleştirerek eski sürümleri temizler ve depolama kullanımını azaltır.
flowchart TD
W["Yazma İsteği"] --> L["Commit Log"]
W --> M["Memtable"]
M --> F{"Bellek doldu mu?"}
F -->|"Evet"| S1["Yeni SSTable Oluştur"]
F -->|"Hayır"| M
S1 --> C["Compaction"]
S2["Eski SSTable"] --> C
S3["Başka SSTable"] --> C
C --> S4["Birleştirilmiş SSTable"]Bu diyagram Cassandra’nın yazıları önce hızlıca kabul edip daha sonra disk üzerindeki SSTable dosyalarını compaction ile düzenlediğini gösterir. Yazma performansı bu tasarımın merkezindedir.
Cassandra gibi sistemlerde silme işlemi çoğu zaman veriyi hemen fiziksel olarak yok etmek anlamına gelmez. Tombstone, yani şöyle düşün: sistem kaydın üzerine “bu veri artık geçersiz” işareti koyar. Daha sonra compaction çalıştığında bu işaret ve eski veri temizlenebilir.
Bu yaklaşım dağıtık sistemlerde önemlidir çünkü verinin kopyaları farklı node’larda bulunabilir. Silme bilgisinin de tıpkı veri gibi diğer kopyalara yayılması gerekir.
stateDiagram-v2 [*] --> Aktif Aktif --> Guncellenmis: Yeni sürüm yazılır Guncellenmis --> Silinmis: Tombstone eklenir Silinmis --> Temizlenmis: Compaction çalışır Temizlenmis --> [*]
Bu state diyagramı bir kaydın aktif durumdan güncellenmiş duruma, sonra tombstone ile silinmiş kabul edilen duruma geçmesini gösterir. Fiziksel temizlik ise compaction aşamasında gerçekleşir.
NoSQL özellikle veri modeli doğal olarak belge, anahtar-değer, kolon ailesi veya geniş dağıtık kayıt yapısına uyuyorsa güçlüdür. Büyük hacimli yazma trafiği, yatay ölçekleme ihtiyacı, esnek şema ve yüksek erişilebilirlik önceliği varsa Cassandra, Amazon DynamoDB, Elasticsearch gibi sistemler ciddi avantaj sağlayabilir.
Ama sen de fark etmişsindir ki bu tercih “büyük sistem = NoSQL” kadar basit değildir. Veri ilişkileri yoğunsa, transaction güvenliği kritikse, raporlama ve join sorguları merkeze oturuyorsa SQL/RDBMS daha doğru olabilir. İyi mimari, popüler olanı değil, ihtiyaca en uygun olanı seçer.
NoSQL veritabanları, veriyi uygulamanın erişim biçimine yakın şekilde sakladığında çok güçlü hale gelir. Esnek şema, yatay partitioning, replication, quorum ve yazma odaklı depolama modeli sayesinde büyük ve dağıtık sistemlerde önemli avantajlar sunar. Buna karşılık ACID transaction, güçlü ilişkiler, join yoğun sorgular ve katı tutarlılık gerektiren alanlarda SQL veritabanları hâlâ çok sağlam bir zemindir. Doğru seçim, sistemin nasıl veri yazdığına, nasıl okuduğuna, ne kadar tutarlılık istediğine ve hata anında neyi feda edebileceğine bağlıdır.
Bu yazı Introduction to NoSQL databases videosundan ilham alınarak yazılmıştır.