PostgreSQL mi MongoDB mi: Projeniz için Doğru Veritabanı

Yıllarca hem PostgreSQL hem de MongoDB projelerinde çalıştıktan sonra şunu söyleyebilirim: bu iki veritabanı arasındaki seçim, çoğu zaman sanıldığı kadar net değil. “SQL mi NoSQL mi?” sorusu teknik bir soru olduğu kadar, ekibinizin alışkanlıkları, projenizin büyüme yönü ve veri modelinizin olgunluğuyla da doğrudan ilgili. Bu yazıda size teorik karşılaştırma değil, gerçek projelerde ne yaşandığını anlatacağım.

Temel Farkları Anlamak: Sadece Sözdizimi Değil

PostgreSQL bir ilişkisel veritabanı yönetim sistemi. Yani verileriniz tablolarda, sütunlarda yaşıyor, birbirleriyle foreign key’lerle bağlı. MongoDB ise döküman odaklı bir NoSQL veritabanı; verilerinizi JSON benzeri BSON formatında, koleksiyonlar içinde saklıyor.

Ama asıl fark burada değil. Asıl fark veri modelinize ne kadar hakimsiniz sorusunun cevabında.

Eğer veri yapınız önceden belliyle ve zamanla çok fazla değişmeyecekse, PostgreSQL’in katı şema yapısı bir kısıt değil, bir güvence. Bir e-ticaret sitesinde sipariş, ürün, müşteri ilişkileri gayet netse PostgreSQL burada mükemmel çalışır. Ama bir içerik yönetim sisteminde her makale farklı metadata alanları taşıyorsa, MongoDB’nin şemasız yapısı size gerçek bir esneklik sağlar.

# PostgreSQL kurulumu (Ubuntu/Debian)
sudo apt update
sudo apt install postgresql postgresql-contrib

# Servis durumunu kontrol et
sudo systemctl status postgresql

# PostgreSQL'e bağlan
sudo -u postgres psql
# MongoDB kurulumu (Ubuntu 22.04)
curl -fsSL https://www.mongodb.org/static/pgp/server-7.0.asc | 
   sudo gpg -o /usr/share/keyrings/mongodb-server-7.0.gpg --dearmor

echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-7.0.gpg ] 
  https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/7.0 multiverse" | 
  sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list

sudo apt update
sudo apt install mongodb-org
sudo systemctl start mongod

Veri Modeli: Projenizin DNA’sı

Birkaç yıl önce bir lojistik şirketinde çalışırken, kargo takip sistemi için MongoDB seçilmişti. Mantıklıydı; her kargo farklı özellikler taşıyabilir, belge yapısı esnek olmalı diye düşünülmüştü. Aradan altı ay geçmeden raporlama ekibinin MongoDB aggregation pipeline’larıyla boğuştuğunu gördük. Aynı sorguyu PostgreSQL’de beş satırda yazarken MongoDB’de yirmi satır aggregation yazıyorduk.

Öte yandan, bir medya şirketinde makale ve kullanıcı aktivitesi verilerini MongoDB’de tutuyorduk ve bu mükemmel çalışıyordu. Çünkü her makalenin farklı blok yapısı vardı, nested dökümanlar tam ihtiyacımıza karşılık geliyordu.

PostgreSQL veri modeli örneği:

-- İlişkisel yapı: siparişler ve ürünler
CREATE TABLE customers (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    email VARCHAR(255) UNIQUE NOT NULL,
    created_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE orders (
    id SERIAL PRIMARY KEY,
    customer_id INTEGER REFERENCES customers(id),
    total_amount DECIMAL(10,2),
    status VARCHAR(50) DEFAULT 'pending',
    created_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE order_items (
    id SERIAL PRIMARY KEY,
    order_id INTEGER REFERENCES orders(id),
    product_name VARCHAR(200),
    quantity INTEGER,
    unit_price DECIMAL(10,2)
);

-- Müşterinin tüm siparişlerini getir
SELECT 
    c.name,
    COUNT(o.id) as siparis_sayisi,
    SUM(o.total_amount) as toplam_harcama
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
GROUP BY c.id, c.name
ORDER BY toplam_harcama DESC;

MongoDB döküman modeli örneği:

// Aynı veriyi MongoDB'de gömmek (embedding)
db.orders.insertOne({
  customer: {
    name: "Ahmet Yılmaz",
    email: "[email protected]"
  },
  items: [
    { product: "Laptop", quantity: 1, unit_price: 15000 },
    { product: "Mouse", quantity: 2, unit_price: 250 }
  ],
  total_amount: 15500,
  status: "pending",
  created_at: new Date()
});

// Aggregation ile rapor almak
db.orders.aggregate([
  { $group: {
    _id: "$customer.email",
    siparis_sayisi: { $sum: 1 },
    toplam_harcama: { $sum: "$total_amount" }
  }},
  { $sort: { toplam_harcama: -1 } }
]);

MongoDB’de veriyi gömmek (embedding) okuma performansını artırır, çünkü tek bir döküman tüm ilgili bilgiyi taşır. Ama bu veriyi güncellemeniz gerektiğinde, özellikle müşteri bilgisi değiştiğinde, tüm sipariş dökümanlarını güncellemeniz gerekebilir. İşte bu noktada PostgreSQL’in referans bütünlüğü altın değer taşır.

Performans: Hangi Senaryoda Kim Kazanır?

Performans karşılaştırmaları çoğu zaman yanıltıcı. “MongoDB daha hızlı” ya da “PostgreSQL daha güvenilir” gibi genellemeler bağlamdan kopuk değerlendirmeler. Gerçek performans, kullanım senaryonuza göre değişir.

Yüksek hacimli yazma işlemleri: MongoDB burada avantajlı. Özellikle zaman serisi verileri, log kayıtları, kullanıcı aktivite takibi gibi sürekli yazma gerektiren senaryolarda MongoDB’nin esnek şema yapısı ve yatay ölçeklenme kapasitesi öne çıkar.

Karmaşık sorgu ve raporlama: PostgreSQL burada rakipsiz. JOIN’ler, window fonksiyonları, CTE’ler (Common Table Expressions) ile son derece karmaşık iş zekası sorguları yazabilirsiniz.

-- PostgreSQL'de güçlü window fonksiyonu örneği
-- Her müşterinin aylık sipariş büyümesini hesapla
WITH monthly_orders AS (
    SELECT 
        customer_id,
        DATE_TRUNC('month', created_at) as ay,
        SUM(total_amount) as aylik_toplam
    FROM orders
    GROUP BY customer_id, DATE_TRUNC('month', created_at)
)
SELECT 
    customer_id,
    ay,
    aylik_toplam,
    LAG(aylik_toplam) OVER (PARTITION BY customer_id ORDER BY ay) as onceki_ay,
    ROUND(
        (aylik_toplam - LAG(aylik_toplam) OVER (PARTITION BY customer_id ORDER BY ay)) /
        NULLIF(LAG(aylik_toplam) OVER (PARTITION BY customer_id ORDER BY ay), 0) * 100, 2
    ) as buyume_orani
FROM monthly_orders
ORDER BY customer_id, ay;

Bu sorguyu MongoDB’de yazmaya çalışın, sonra bana haber verin.

ACID Uyumu ve Veri Tutarlılığı

Banka, finans, sağlık sektörü gibi alanlarda çalışıyorsanız bu başlık sizin için kritik. PostgreSQL tam ACID uyumludur; bu, transaction’ların atomik olduğu, tutarlı kaldığı, izole çalıştığı ve kalıcı olduğu anlamına gelir.

MongoDB 4.0 sürümünden itibaren çok-döküman transaction’larını desteklemeye başladı, ama bu özellik hâlâ PostgreSQL’in olgunluğuna ve performansına ulaşabilmiş değil. Finans uygulamalarında MongoDB transaction kullandım, çalışıyor ama yapılandırması daha karmaşık ve performans overhead’i göz ardı edilemez.

# PostgreSQL'de transaction örneği
psql -U postgres -d finans_db << 'EOF'
BEGIN;

-- Hesap 1'den para çek
UPDATE accounts 
SET balance = balance - 1000 
WHERE account_id = 'ACC001' AND balance >= 1000;

-- Hesap 2'ye para ekle
UPDATE accounts 
SET balance = balance + 1000 
WHERE account_id = 'ACC002';

-- İşlemi kaydet
COMMIT;
EOF
// MongoDB'de transaction (4.0+)
const session = client.startSession();
session.startTransaction();

try {
  await db.collection('accounts').updateOne(
    { account_id: 'ACC001', balance: { $gte: 1000 } },
    { $inc: { balance: -1000 } },
    { session }
  );
  
  await db.collection('accounts').updateOne(
    { account_id: 'ACC002' },
    { $inc: { balance: 1000 } },
    { session }
  );
  
  await session.commitTransaction();
} catch (error) {
  await session.abortTransaction();
  throw error;
} finally {
  session.endSession();
}

Her iki kod da çalışır, ama PostgreSQL’deki ifade hem daha kısa hem de yıllarca test edilmiş bir altyapının üzerinde duruyor.

Ölçeklenme Stratejileri

Bu, ekipler arasında en çok tartışılan konu. “MongoDB yatay ölçeklenir, PostgreSQL dikey ölçeklenir” şeklinde bir inanış var. Bu doğru ama eksik.

MongoDB ölçeklenme avantajları:

  • Sharding native olarak desteklenir
  • Replica set kurulumu görece basit
  • Yatay ölçekleme için tasarlanmış

PostgreSQL ölçeklenme araçları:

  • Citus ile yatay sharding
  • Patroni ile yüksek erişilebilirlik
  • pgBouncer ile connection pooling
  • Read replica’larla okuma yükü dağıtımı
# PostgreSQL için pgBouncer connection pooling kurulumu
sudo apt install pgbouncer

# pgbouncer.ini temel yapılandırma
cat > /etc/pgbouncer/pgbouncer.ini << 'EOF'
[databases]
myapp = host=127.0.0.1 port=5432 dbname=myapp

[pgbouncer]
listen_addr = 0.0.0.0
listen_port = 6432
auth_type = md5
auth_file = /etc/pgbouncer/userlist.txt
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 20
reserve_pool_size = 5
log_connections = 1
EOF

sudo systemctl restart pgbouncer

Günümüzde büyük PostgreSQL kurulumları ciddi ölçekte çalışabiliyor. Instagram, Notion gibi ürünler PostgreSQL kullanıyor ve milyonlarca kullanıcıya hizmet veriyor. “Ölçeklenemez” argümanı artık geçerliliğini büyük ölçüde yitirdi.

Gerçek Dünya Karar Kriterleri

Bir projeye başlarken şu soruları sormak işinizi kolaylaştırır:

PostgreSQL’i tercih edin eğer:

  • Veri şemanız baştan belli ve stabil
  • Karmaşık ilişkiler ve JOIN operasyonları sık kullanılıyor
  • Finansal veya kritik iş verileri işliyorsunuz
  • Güçlü ACID garantisi şart
  • Ekibiniz SQL’e hakimse
  • Raporlama ve analitik ihtiyaçlarınız yoğunsa

MongoDB’yi tercih edin eğer:

  • Veri yapısı dinamik, zamanla çok değişiyor
  • Hızlı prototipleme yapıyorsunuz
  • Hiyerarşik, nested veri yapıları ağırlıklı
  • Yatay ölçeklemeyi native olarak istiyorsunuz
  • Döküman odaklı içerik yönetimi yapıyorsunuz
  • Coğrafi dağıtık sistemler kuruyorsunuz

Her iki durumda da işe yarayan senaryo: Gerçek projelerde ikisini birlikte kullanmak oldukça yaygın. Ana iş verisi PostgreSQL’de, kullanıcı aktivite logları veya önbellek verileri MongoDB’de tutulabilir. Mikroservis mimarilerinde her servisin kendi veritabanı seçimi yapması artık norm haline geldi.

Yönetim Kolaylığı ve Operasyonel Maliyet

Sistem yöneticisi gözüyle bakınca, her iki veritabanının da kendine özgü operasyonel zorlukları var.

PostgreSQL’de vacuum işlemleri, bloat yönetimi, connection limit’leri başlı başına bir uzmanlık alanı. Özellikle yüksek yazma trafiğinde autovacuum ayarlarını iyi yapılandırmazsanız tablolarınız şişer ve performans düşer.

# PostgreSQL bloat analizi
psql -U postgres -d myapp -c "
SELECT 
    schemaname,
    tablename,
    pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) as boyut,
    n_dead_tup as olu_satirlar,
    n_live_tup as canli_satirlar,
    ROUND(n_dead_tup::numeric / NULLIF(n_live_tup + n_dead_tup, 0) * 100, 2) as olum_orani
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC
LIMIT 10;
"

# Manuel vacuum
psql -U postgres -d myapp -c "VACUUM ANALYZE orders;"

MongoDB’de ise şema migrasyonları dikkat ister. Şemasız yapı geliştirme sürecinde esneklik sağlasa da, üretim ortamında eski ve yeni döküman formatlarının bir arada bulunması (mixed schema) ciddi baş ağrısına dönüşebilir. Her zaman şema versiyonlaması ve migrasyon stratejinizi baştan planlayın.

# MongoDB replica set sağlık kontrolü
mongosh --eval "rs.status()" | grep -E '"name"|"stateStr"|"health"'

# MongoDB index kullanım istatistikleri
mongosh myapp --eval "
db.orders.aggregate([
  { $indexStats: {} },
  { $project: { name: 1, 'accesses.ops': 1, 'accesses.since': 1 } }
])
"

Lisanslama ve Maliyet

PostgreSQL tamamen açık kaynak ve ücretsiz, BSD lisansıyla dağıtılıyor. Hiçbir ticari kısıt yok.

MongoDB’nin durumu biraz farklı. Community sürümü SSPL lisansıyla ücretsiz sunuluyor, ancak bu lisans bazı bulut sağlayıcılar tarafından sorunlu bulunan kısıtlamalar içeriyor. Enterprise özellikleri için MongoDB Atlas veya MongoDB Enterprise ücretli. Büyük ölçekli kurulumlar için bu maliyetler ciddi rakamlara ulaşabilir.

Eğer tamamen açık kaynak ve vendor lock-in’den uzak durmak istiyorsanız, PostgreSQL bu açıdan da öne çıkıyor.

İki Veritabanı Birlikte: Hibrit Yaklaşım

Bir SaaS platformunda çalışırken benimsediğimiz mimariyi paylaşayım. Ana iş verileri (kullanıcılar, abonelikler, faturalar) PostgreSQL’de tutuluyordu. Kullanıcı davranış logları, arama geçmişi ve kişiselleştirme verileri MongoDB’de. Bu hibrit yaklaşım her iki veritabanının güçlü yanlarından yararlanmamızı sağladı.

Önemli olan, bu ayrımı rastgele değil, veri erişim paternlerine göre yapmak. Transactional ve ilişkisel veri bir tarafa, doküman odaklı ve yüksek hacimli yazma gerektiren veri öte yana.

# Python ile iki veritabanına bağlantı (örnek yapı)
# requirements.txt
# psycopg2-binary==2.9.9
# pymongo==4.6.1

cat > db_connections.py << 'EOF'
import psycopg2
from pymongo import MongoClient

# PostgreSQL bağlantısı - iş verileri için
def get_pg_connection():
    return psycopg2.connect(
        host="localhost",
        database="myapp",
        user="appuser",
        password="gizli_sifre",
        port=5432
    )

# MongoDB bağlantısı - aktivite logları için
def get_mongo_client():
    return MongoClient(
        "mongodb://localhost:27017/",
        username="appuser",
        password="gizli_sifre",
        authSource="myapp_logs"
    )

# Kullanıcı kaydı: PostgreSQL'e yaz
def create_user(email, name):
    conn = get_pg_connection()
    cur = conn.cursor()
    cur.execute(
        "INSERT INTO users (email, name) VALUES (%s, %s) RETURNING id",
        (email, name)
    )
    user_id = cur.fetchone()[0]
    conn.commit()
    cur.close()
    conn.close()
    return user_id

# Aktivite logu: MongoDB'ye yaz
def log_activity(user_id, action, metadata):
    client = get_mongo_client()
    db = client.myapp_logs
    db.activities.insert_one({
        "user_id": user_id,
        "action": action,
        "metadata": metadata,
        "timestamp": __import__('datetime').datetime.utcnow()
    })
    client.close()
EOF

Sonuç

Doğru soru “PostgreSQL mi MongoDB mi?” değil, “Bu proje için hangi veritabanı daha uygun?” sorusu. Ve bu sorunun cevabı çoğu zaman veri modelinizin olgunluğuna, ekibinizin bilgi birikimine ve projenizin büyüme yönüne bağlı.

Eğer başlangıçta hangi veritabanını seçeceğinizden emin değilseniz, şunu söyleyebilirim: PostgreSQL ile başlayın. Neden? Çünkü PostgreSQL’den MongoDB’ye geçmek zorunda kalabilirsiniz, ama MongoDB’deki karmaşık ilişkisel ihtiyaçları çözmeye çalışmak çok daha acı verici bir deneyim. PostgreSQL aynı zamanda JSONB veri tipiyle döküman benzeri verileri de tutabilir, yani bir miktar esneklik sunuyor.

Öte yandan, döküman odaklı, şeması sürekli değişen, yüksek yazma trafikli ve native yatay ölçekleme gerektiren projelerde MongoDB gerçekten doğru seçim. Bu kriterlerin birden fazlası projeniz için geçerliyse, MongoDB’ye yönelmekten çekinmeyin.

Sonuç olarak, her iki teknoloji de olgun, üretim ortamlarında kanıtlanmış ve aktif topluluklara sahip araçlar. Teknolojiyi seçtikten sonra en kötü senaryo bile aşılabilir; ama başta doğru kararı vermek aylarca kazandırır.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir