Giriş: Veritabanı Neden Önemli?
Modern yazılım geliştirmede veritabanı, uygulamanızın kalbidir. Kullanıcı verileri, ürün katalogları, log kayıtları - her şey veritabanında saklanır ve yönetilir. Doğru veritabanı seçimi ve kullanımı, uygulamanızın performansını, güvenilirliğini ve ölçeklenebilirliğini doğrudan etkiler.
Veritabanı dünyasında bilmeniz gereken iki temel kavram vardır:
- ACID (Atomicity, Consistency, Isolation, Durability): İlişkisel veritabanlarının temel garantisidir. Her işlemin ya tamamen gerçekleşeceğini ya da hiç gerçekleşmeyeceğini garanti eder. Banka transferlerinde paranın kaybolmaması için kritiktir.
- CAP Teoremi (Consistency, Availability, Partition Tolerance): Dağıtık sistemlerde bu üç özellikten en fazla ikisini aynı anda garanti edebilirsiniz. NoSQL veritabanları genellikle yüksek erişilebilirlik ve bölünme toleransını tercih eder.
İki Dünya: SQL vs NoSQL
Veritabanı seçimi, proje gereksinimlerinize göre yapılmalıdır. İşte iki ana yaklaşım:
SQL (İlişkisel) Veritabanları
Ne zaman kullanmalı? Verileriniz yapılandırılmış ve ilişkisel ise (örn: e-ticaret, muhasebe, kullanıcı yönetimi).
- Sıkı şema (schema) yapısı - veri bütünlüğü garantisi
- ACID özellikleri - transaction desteği
- Karmaşık sorgular (JOIN) yapabilme
- Örnekler: PostgreSQL, MySQL, SQLite
NoSQL (İlişkisel Olmayan) Veritabanları
Ne zaman kullanmalı? Hızlı ölçekleme, esnek şema veya büyük veri (big data) ihtiyacınız varsa.
- Esnek şema - her kayıt farklı yapıda olabilir
- Yatay ölçekleme - daha fazla sunucu ekleyerek büyüme
- Yüksek performans - basit okuma/yazma işlemleri için
- Örnekler: MongoDB (döküman), Redis (key-value), Cassandra (sütun-tabanlı)
"Doğru araç doğru iş için" prensibi veritabanı seçiminde de geçerlidir. Bazen aynı projede hem SQL hem NoSQL birlikte kullanılır (polyglot persistence).
Temel Kavramlar
1. Normalizasyon
Normalizasyon, veri tekrarını önlemek ve veri bütünlüğünü korumak için veritabanı tasarım sürecidir. En yaygın formlar:
- 1NF (First Normal Form): Her hücrede tek bir değer, her satır benzersiz
- 2NF: 1NF + tüm alanlar birincil anahtara bağlı
- 3NF: 2NF + geçişli bağımlılık yok
2. İndeks (Index)
İndeksler, sorguları hızlandıran veri yapılarıdır - kitabın sonundaki dizin gibi. Doğru indeks kullanımı sorgu süresini saniyelerden milisaniyelere indirebilir.
-- email sütununa indeks eklemek
CREATE INDEX idx_users_email ON users(email);
-- Kompozit indeks (birden fazla sütun)
CREATE INDEX idx_orders_user_date ON orders(user_id, created_at);
3. Transaction ve ACID
Transaction, birden fazla veritabanı işlemini tek bir birim olarak yönetir. Ya hepsi başarılı olur, ya hiçbiri:
BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT; -- Her ikisi de başarılıysa kaydet
-- Hata varsa ROLLBACK ile geri al
Basit Sorgu Optimizasyonu
EXPLAIN ve ANALYZE
PostgreSQL'de sorgunuzun nasıl çalıştığını görmek için:
EXPLAIN ANALYZE
SELECT u.name, COUNT(o.id) as order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.created_at > '2024-01-01'
GROUP BY u.id;
Bu komut, sorgunun hangi indeksleri kullandığını, kaç satır taradığını ve ne kadar sürdğünü gösterir.
N+1 Problemi
ORM kullanırken sık yapılan hata: Her bir kayıt için ayrı sorgu çalıştırmak.
const users = await User.findAll();
for (const user of users) {
user.orders = await Order.findAll({ where: { userId: user.id } });
}
const users = await User.findAll({
include: [{ model: Order }]
});
Local Development: Hızlı Kurulum
PostgreSQL ile Docker
# Docker ile PostgreSQL başlatma
docker run --name dev-postgres \
-e POSTGRES_PASSWORD=mysecret \
-e POSTGRES_DB=myapp \
-p 5432:5432 \
-d postgres:16
# Bağlantı testi
psql -h localhost -U postgres -d myapp
MongoDB ile Docker
# MongoDB başlatma
docker run --name dev-mongo \
-p 27017:27017 \
-d mongo:7
# Mongo Shell ile bağlan
mongosh mongodb://localhost:27017
Küçük Örnek Proje: CRUD API
Node.js + Express + PostgreSQL ile basit bir kullanıcı API'si:
import express from 'express';
import { Pool } from 'pg';
const app = express();
const pool = new Pool({
host: 'localhost',
database: 'myapp',
user: 'postgres',
password: 'mysecret',
port: 5432,
});
app.use(express.json());
app.get('/users', async (req, res) => {
const result = await pool.query('SELECT id, name, email FROM users');
res.json(result.rows);
});
app.post('/users', async (req, res) => {
const { name, email } = req.body;
const result = await pool.query(
'INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *',
[name, email]
);
res.status(201).json(result.rows[0]);
});
app.put('/users/:id', async (req, res) => {
const { id } = req.params;
const { name, email } = req.body;
const result = await pool.query(
'UPDATE users SET name = $1, email = $2 WHERE id = $3 RETURNING *',
[name, email, id]
);
res.json(result.rows[0]);
});
app.listen(3000, () => console.log('Server running on port 3000'));
İyi Uygulamalar
1. Migration Kullanımı
Veritabanı şeması değişikliklerini kod olarak yönetin:
# Migration oluştur
npx prisma migrate dev --name add_users_table
# Production'a uygula
npx prisma migrate deploy
2. Seed Data
Geliştirme ortamında test verileri ile çalışın:
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
await prisma.user.createMany({
data: [
{ name: 'Ali Veli', email: 'ali@example.com' },
{ name: 'Ayşe Yılmaz', email: 'ayse@example.com' },
],
});
}
main();
3. Connection Pooling
Her istek için yeni bağlantı açmak yerine, connection pool kullanın. Yukarıdaki pg.Pool örneği bunu yapar.
4. Çevresel Değişkenler (Environment Secrets)
Veritabanı bilgilerini asla koda yazmayın:
# .env dosyası
DATABASE_URL="postgresql://user:password@localhost:5432/mydb"
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
});
Kaynaklar ve İleri Seviye Okuma
- PostgreSQL Resmi Dökümanı: En güncel ve detaylı kaynak
- Use The Index, Luke: SQL indeks optimizasyonu rehberi
- MongoDB University: Ücretsiz NoSQL kursları
- Prisma Docs: Modern ORM kullanımı
Veritabanı bilgisi, her yazılımcının temel araç setindedir. Bu temelleri öğrendikten sonra, cache stratejileri, replikasyon ve sharding gibi ileri seviye konulara geçebilirsiniz. Unutmayın: Doğru veritabanı tasarımı, uygulamanızın geleceğini belirler.
