İlişkisel, NoSQL, Key-Value ve Graph veri tabanlarını; komutları, kısaltmaları ve pratik kullanımı ile öğren.
Veri tabanı, düzenli biçimde depolanan ve kolayca erişilebilen, yönetilebilen yapılandırılmış veri koleksiyonudur. Ham verinin aksine, veri tabanındaki bilgiler bir schema (şema) ya da model çerçevesinde organize edilir; bu sayede sorgulama, filtreleme ve güncelleme işlemleri sistematik şekilde yapılabilir.
Temel ayrım: Veri tabanı (database) sadece verilerin depolandığı yapıdır. DBMS ise bu yapıyı yöneten, sorgular çalıştıran ve güvenliği sağlayan yazılım katmanıdır.
DBMS (Database Management System — Veri Tabanı Yönetim Sistemi), kullanıcı ile fiziksel veri arasına giren ara yazılımdır. Temel görevleri şunlardır:
Yaygın DBMS örnekleri: PostgreSQL, MySQL, Oracle DB, MS SQL Server (ilişkisel); MongoDB, Cassandra (doküman/kolon tabanlı); Redis (anahtar-değer); Neo4j (grafik).
Tablolar, satırlar, sütunlar. SQL dili. ACID garantisi.
JSON/BSON belgeler. Esnek şema. Yatay ölçeklenme.
Basit hash yapısı. Ultra hızlı. Cache senaryoları.
Düğümler ve kenarlar. İlişki sorguları için ideal.
İlişkisel modelde veriler tablolarda saklanır. Her tablo satır (row/tuple) ve sütunlardan (column/attribute) oluşur. Tablolar arasındaki ilişkiler foreign key (yabancı anahtar) ile kurulur. Tüm işlemler ACID (Atomicity, Consistency, Isolation, Durability) özelliklerine tabidir.
Ne zaman tercih edilmeli? Veriler arası güçlü ilişki varsa, veri bütünlüğü kritikse (finans, muhasebe, e-ticaret siparişleri), karmaşık JOIN sorguları gerekiyorsa ve ekip SQL biliyor ise RDBMS doğru seçimdir.
Veriler JSON benzeri dokümanlar (MongoDB'de BSON) olarak saklanır. Her doküman, kendi şemasını barındırabilir; yani aynı koleksiyondaki iki dokümanın alanları tamamen farklı olabilir. Bu esneklik, hızlı gelişim döngüleri için büyük avantaj sağlar.
Ne zaman tercih edilmeli? Veri yapısı sık değişiyorsa, JSON API'ler ile çalışılıyorsa, yatay ölçekleme (sharding) ihtiyacı varsa veya hiyerarşik iç içe veri saklanıyorsa (ürün katalogları, kullanıcı profilleri) MongoDB idealdir.
En basit veri modeli: her şey bir anahtar (key) ile eşleşen bir değerden (value) oluşur. Redis, bu modeli zengin veri yapılarıyla (String, Hash, List, Set, Sorted Set, Stream) güçlendirmiş; in-memory mimarisi sayesinde mikrosaniye mertebesinde yanıt süreleri sunar.
Ne zaman tercih edilmeli? Oturum yönetimi (session), önbellekleme (caching), gerçek zamanlı sıralamalar (leaderboard), pub/sub mesajlaşma ve rate limiting gibi hız gerektiren senaryolarda Redis birincil tercihtir.
Veriler düğüm (node) ve kenar (edge/relationship) olarak modellenir. Kenarlar da özellikler (properties) taşıyabilir. Grafik veri tabanları, özellikle derinlemesine gezinme (traversal) sorgularında ilişkisel veri tabanlarını çok geride bırakır; çünkü JOIN yerine doğrudan işaretçi (pointer) takibi kullanır.
Ne zaman tercih edilmeli? Sosyal ağlar, öneri motorları (recommendation engine), dolandırıcılık tespiti, erişim kontrol grafikleri ve bilgi grafikleri (knowledge graph) gibi ilişkilerin kendisinin birinci sınıf veri olduğu senaryolarda Neo4j rakipsizdir.
Veri tabanı dünyasında karşılaşacağın tüm temel kısaltmalar ve açılımları aşağıda listelenmiştir.
Her veri tabanının temel işlem seti olan CRUD'un farklı sistemlerdeki karşılıkları:
| CRUD | SQL (RDBMS) | MongoDB | Redis | Açıklama |
|---|---|---|---|---|
| Create | INSERT | insertOne / insertMany | SET | Yeni kayıt ekleme |
| Read | SELECT | find / findOne | GET | Veri okuma / sorgulama |
| Update | UPDATE | updateOne / updateMany | SET (üzerine yaz) | Mevcut veriyi güncelleme |
| Delete | DELETE | deleteOne / deleteMany | DEL | Veri silme |
-- Yeni bir veri tabanı oluştur CREATE DATABASE uygulama_db; -- Veri tabanını seç (MySQL) USE uygulama_db; -- PostgreSQL için bağlanma komutu (terminal) -- \c uygulama_db -- Kullanıcılar tablosu oluştur CREATE TABLE kullanicilar ( id SERIAL PRIMARY KEY, -- Otomatik artan birincil anahtar ad VARCHAR(100) NOT NULL, -- Zorunlu metin alanı email VARCHAR(255) UNIQUE NOT NULL, -- Tekil ve zorunlu olusturulma TIMESTAMP DEFAULT NOW() -- Varsayılan: şu an ); -- Mevcut tabloya yeni sütun ekle (ALTER) ALTER TABLE kullanicilar ADD COLUMN telefon VARCHAR(20); -- Tabloyu tamamen sil (veri + yapı) DROP TABLE IF EXISTS kullanicilar;
-- Tek kayıt ekle INSERT INTO kullanicilar (ad, email) VALUES ('Ahmet Yılmaz', 'ahmet@example.com'); -- Birden fazla kayıt ekle INSERT INTO kullanicilar (ad, email) VALUES ('Fatma Şahin', 'fatma@example.com'), ('Mehmet Çelik', 'mehmet@example.com'), ('Zeynep Öztürk', 'zeynep@example.com');
-- Tüm sütunları getir SELECT * FROM kullanicilar; -- Belirli sütunlar ve filtreleme SELECT ad, email FROM kullanicilar WHERE email LIKE '%@example.com' ORDER BY ad ASC LIMIT 10; -- Aggregate: kaç kullanıcı var? SELECT COUNT(*) AS toplam_kullanici FROM kullanicilar; -- İki tabloyu JOIN ile birleştir SELECT k.ad, s.baslik FROM kullanicilar AS k JOIN siparisler AS s ON s.kullanici_id = k.id WHERE k.id = 1;
-- Belirli bir kaydı güncelle (WHERE olmadan BÜTÜN kayıtlar güncellenir!) UPDATE kullanicilar SET telefon = '+90 532 000 00 00' WHERE id = 1; -- Belirli bir kaydı sil DELETE FROM kullanicilar WHERE id = 5; -- Transaction ile güvenli işlem BEGIN; UPDATE hesaplar SET bakiye = bakiye - 100 WHERE id = 1; UPDATE hesaplar SET bakiye = bakiye + 100 WHERE id = 2; COMMIT; -- Hata varsa: ROLLBACK;
// Veri tabanına geç (yoksa ilk belge eklenince oluşur) use uygulama_db // Koleksiyon oluştur (explicit) db.createCollection("kullanicilar") // Tek doküman ekle — schema-free, her alan isteğe bağlı db.kullanicilar.insertOne({ ad: "Ahmet Yılmaz", email: "ahmet@example.com", roller: ["admin", "editor"], // dizi adres: { // iç içe belge sehir: "İstanbul", posta: "34000" }, olusturulma: new Date() }) // Birden fazla doküman ekle db.kullanicilar.insertMany([ { ad: "Fatma Şahin", email: "fatma@example.com" }, { ad: "Mehmet Çelik", email: "mehmet@example.com" } ])
// Tüm belgeleri getir db.kullanicilar.find({}) // Filtreli sorgu — şehri İstanbul olanlar db.kullanicilar.find({ "adres.sehir": "İstanbul" }) // Sadece ad ve email alanlarını getir (projection) db.kullanicilar.find( { roller: "admin" }, // filtre { ad: 1, email: 1, _id: 0 } // projeksiyon ) // Sıralama, limit ve atla db.kullanicilar.find({}) .sort({ ad: 1 }) // 1: artan, -1: azalan .skip(10) .limit(5) // Aggregation pipeline örneği — şehre göre grupla db.kullanicilar.aggregate([ { $group: { _id: "$adres.sehir", sayi: { $sum: 1 } } }, { $sort: { sayi: -1 } } ])
// Tek belge güncelle — $set sadece belirtilen alanı değiştirir db.kullanicilar.updateOne( { email: "ahmet@example.com" }, { $set: { "adres.sehir": "Ankara" } } ) // Tüm eşleşen belgeleri güncelle db.kullanicilar.updateMany( { "adres.sehir": "İstanbul" }, { $set: { bolge: "Marmara" } } ) // Tek belge sil db.kullanicilar.deleteOne({ email: "silinecek@example.com" }) // Tüm eşleşen belgeleri sil db.kullanicilar.deleteMany({ aktif: false })
# String: SET / GET SET kullanici:1:ad "Ahmet Yılmaz" GET kullanici:1:ad # → "Ahmet Yılmaz" # TTL ile süre sonu belirle (60 saniye) SET oturum:abc123 "kullanici_id_42" EX 3600 TTL oturum:abc123 # Kalan saniye PERSIST oturum:abc123 # TTL'yi kaldır DEL oturum:abc123 # Anahtarı sil # Sayaç artırma / azaltma SET sayfa:anasayfa:goruntulemeler 0 INCR sayfa:anasayfa:goruntulemeler # → 1 INCRBY sayfa:anasayfa:goruntulemeler 10 # → 11 DECR sayfa:anasayfa:goruntulemeler # → 10
# HASH — nesne gibi çalışır HSET kullanici:1 ad "Ahmet" email "ahmet@example.com" yas 28 HGET kullanici:1 ad HGETALL kullanici:1 # tüm alanlar HDEL kullanici:1 yas # alan sil # LIST — sıralı liste (kuyruk / stack) LPUSH gorevler "email-gonder" "rapor-olustur" # başa ekle RPUSH gorevler "veri-temizle" # sona ekle LPOP gorevler # soldan çıkar LRANGE gorevler 0 -1 # tüm listeyi getir # SORTED SET — skor ile sıralı küme (leaderboard) ZADD liderlik 4250 "ahmet" ZADD liderlik 7800 "fatma" ZADD liderlik 5500 "mehmet" ZREVRANGE liderlik 0 2 WITHSCORES # En yüksek 3 # → fatma 7800, mehmet 5500, ahmet 4250
Cypher (CQL): Neo4j'nin kendi sorgu dili. İngilizce açılımı Cypher Query Language'dir. Grafik yapılarını ASCII sanatıyla ifade eder: (node)-[:EDGE]->(node)
// Düğüm oluştur — CREATE CREATE (a:Kisi { ad: 'Ahmet Yılmaz', yas: 28, sehir: 'İstanbul' }) CREATE (f:Kisi { ad: 'Fatma Şahin', yas: 32, sehir: 'Ankara' }) // İlişki oluştur — Ahmet, Fatma'yı TANIYORdur MATCH (a:Kisi { ad: 'Ahmet Yılmaz' }), (f:Kisi { ad: 'Fatma Şahin' }) CREATE (a)-[:TANIYOR { yil: 2018 }]->(f) // Varsa getir, yoksa oluştur — MERGE MERGE (k:Kisi { email: 'yeni@example.com' }) ON CREATE SET k.olusturulma = datetime() ON MATCH SET k.guncellendi = datetime()
// Tüm Kisi düğümlerini getir MATCH (k:Kisi) RETURN k // Ahmet'in tanıdıklarını bul MATCH (a:Kisi { ad: 'Ahmet Yılmaz' })-[:TANIYOR]->(t) RETURN t.ad, t.sehir // Derinlik-2: arkadaşın arkadaşı (2 adım) MATCH (a:Kisi { ad: 'Ahmet Yılmaz' })-[:TANIYOR*2]->(uzak) RETURN DISTINCT uzak.ad // Kısa yolu bul — Dijkstra benzeri MATCH shortestPath( (a:Kisi { ad: 'Ahmet Yılmaz' }) -[:TANIYOR*]- (f:Kisi { ad: 'Fatma Şahin' }) ) RETURN path // Güncelle — SET ile MATCH (k:Kisi { ad: 'Ahmet Yılmaz' }) SET k.sehir = 'İzmir' RETURN k // Düğüm ve tüm ilişkilerini sil MATCH (k:Kisi { ad: 'Ahmet Yılmaz' }) DETACH DELETE k // DETACH: önce ilişkileri kopar
İpucu: Neo4j'de DETACH DELETE kullanmak zorunludur; aksi hâlde düğümün ilişkileri varken silme işlemi hata verir. Bu, kazara silmeye karşı bilinçli bir tasarım kararıdır.