Merhabalar! Bugün sizlerle çoğu geliştiricinin bir noktada karşılaştığı bir konuyu ele alacağız: SQLite’tan PostgreSQL’e geçiş. İlk projenize başlarken “kurulum derdi yok, tek dosya, harika” diyerek SQLite seçtiniz, uygulamanız çalıştı, her şey güzeldi. Ama sonra kullanıcı sayısı arttı, eş zamanlı yazma istekleri çoğaldı ve bir gün loglarda database is locked hatasını görmeye başladınız. İşte o an, “artık taşınma zamanı geldi” demiş oldunuz. Bu yazıdaongoose olarak nasıl adım adım geçiş yapacağınızı, hangi araçları kullanacağınızı ve hangi tuzaklara dikkat etmeniz gerektiğini anlatacağım.
SQLite Neden Yeterli Gelmez?
Öncelikle “neden geçeyim ki?” sorusunu cevaplayalım. SQLite mükemmel bir veritabanıdır — union, hızlı, kurulum derdi yok, tek dosya. Prototip geliştirme, tek kullanıcılı uygulamalar, test ortamı için biçilmiş kaftan. Ama şu durumlarda size yetmez:
- Eş zamanlı yazma: SQLite tüm veritabanı için tek bir yazma kilidi kullanır. Yani bir kullanıcı yazarken diğerleri bekler. PostgreSQL ise MVCC (Multi-Version Concurrency Control) sayesinde 100+ eş zamanlı bağlantının yazmasını sağlar — kimse kimseyi beklemez.
- Çoklu sunucu: İkinci bir uygulama sunucusu açtınız mı? SQLite bir dosyaya yazdığı için iki sunucu aynı dosyaya erişemez. PostgreSQL ise merkezi bir sunucu olarak çalışır, tüm uygulama sunucuları ona bağlanır.
- Veri boyutu: SQLite ~10GB’a kadar iyi performans verir. Üzerine çıkınca sorgu süreleri uzamaya başlar. PostgreSQL terabaytlarca veriyi verimli yönetir.
- Tip güvenliği: SQLite’da bir INTEGER kolonuna string yazabilirsiniz — “tip yakınlığı” (type affinity) denen sistem bunu sessizce kabul eder. PostgreSQL ise katı tip denetimi yapar ve hatalı veriyi reddeder. Üretim ortamında bu fark çok kritiktir.
- Gelişmiş özellikler: Tam metin arama, JSON operasyonları, satır seviyesinde güvenlik (row-level security), custom fonksiyonlar… PostgreSQL’in esnekliği burada öne çıkar.
Loglarda sqlite3.OperationalError: database is locked hatasını haftada bir kez görüyorsanız, geçiş zamanı gelmiştir.
1. Ön Hazırlık: PostgreSQL Kurulumu
Geçişe başlamadan önce hedef veritabanını hazır etmemiz lazım. PostgreSQL’i kurun — yerel makinenize veya bir sunucuya. macOS’ta Homebrew ile:
brew install postgresql@16
brew services start postgresql@16
Ubuntu/Debian’da:
sudo apt update && sudo apt install postgresql postgresql-contrib -y
sudo systemctl enable --now postgresql
Kurulum bittikten sonra veritabanı ve kullanıcı oluşturun:
sudo -u postgres psql
CREATE USER uygulama_kullanici WITH PASSWORD 'guvenli_sifre';
CREATE DATABASE uygulama_db OWNER uygulama_kullanici;
GRANT ALL PRIVILEGES ON DATABASE uygulama_db TO uygulama_kullanici;
\q
Bağlantıyı test edin:
psql -h localhost -U uygulama_kullanici -d uygulama_db
Şifre soracak, girdikten sonra psql komut satırına düşerseniz tamamdır.
2. Şema Çevirisini Anlamak
SQLite ve PostgreSQL arasında tip farkları var. Bu farkları bilmek şemayı doğru kurmak için kritik:
-- SQLite şeması
CREATE TABLE users (
id INTEGER PRIMARY KEY,
email TEXT,
created_at TEXT,
balance REAL
);
-- PostgreSQL eşdeğeri
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
balance NUMERIC(10,2)
);
Dikkat edin:
INTEGER PRIMARY KEY→SERIAL PRIMARY KEY(PostgreSQL’de otomatik artım)TEXTtarih alanları →TIMESTAMPveyaTIMESTAMPTZREAL→NUMERIC(10,2)(para için daha hassas)- SQLite’da
BOOLEANyoktur (0/1 tamsayı kullanılır), PostgreSQL’de gerçekBOOLEANtipi vardır
Eğer ORM (SQLAlchemy, Django ORM, Prisma vb.) kullanıyorsanız, şema çevirisi otomatik yapılır.Bu durumda bu adımı atlayabilirsiniz.
3. Yöntem 1: pgloader ile Otomatik Geçiş
En pratik yöntem pgloader kullanmaktır. pgloader, SQLite veritabanınızı okur, tipleri çevirir ve PostgreSQL’e aktarır. Tek komutta her şey halleder.
Önce pgloader’ı kurun:
# macOS
brew install pgloader
# Ubuntu/Debian
sudo apt install pgloader
Şimdi geçişi yapın. uygulama.db adında bir SQLite dosyanız olduğunu varsayalım:
pgloader --verbose \
sqlite:///path/to/uygulama.db \
postgresql:///uygulama_kullanici:guvenli_sifre@localhost/uygulama_db
pgloader şu işlemleri yapar:
- Tüm tabloları oluşturur (şema çevirisi ile)
- Tüm veriyi kopyalar
- Indexleri ve constraint’leri oluşturur
- İstatistikleri yeniler
Çıktıda Total translation time ve Total table size göreceksiniz. Hata sayısı (errors) 0 olmalı. Eğer hata varsa çıktıyı inceleyin — genelde tip uyuşmazlığı veya boş veri sorunlarıdır.
Önemli: pgloader çalışmadan önce uygulamanızı durdurun. Geçiş sırasında yeni yazma gelirse veri tutarsızlığı oluşur.
4. Yöntem 2: Dump ve Restore ile Manuel Geçiş
Eğer pgloader çalışmazsa veya daha fazla kontrol istiyorsanız, klasik dump-restore yöntemini kullanabilirsiniz. Bu yöntem biraz daha uğraşır ama her adımı siz kontrol edersiniz.
Adım 1: SQLite’dan dump alın:
sqlite3 uygulama.db .dump > dump.sql
Adım 2: Dump dosyasını PostgreSQL’e uygun hale getirin. Birkaç değişiklik gerekir:
# SQLite-specific kelimeleri PostgreSQL'e çevir
sed -i 's/INTEGER PRIMARY KEY AUTOINCREMENT/SERIAL/g' dump.sql
sed -i 's/AUTOINCREMENT//g' dump.sql
sed -i "s/\"/\\'/g" dump.sql
Sed komutları basit ama tehlikeli — büyük ve karmaşık şemalarda her satırı elle kontrol etmeniz gerekebilir. Daha güvenli bir yöntem: şemayı ayrı, veriyi ayrı taşımak.
Adım 3: Şemayı PostgreSQL’de elle oluşturun (yukarıdaki tip çevirisini uygulayarak). Sonra veriyi yükleyin:
psql -h localhost -U uygulama_kullanici -d uygulama_db < dump.sql
Adım 4: Veriyi doğrulayın. Her iki veritabanında satır sayılarını kontrol edin:
-- SQLite
SELECT COUNT(*) FROM users;
-- PostgreSQL
SELECT COUNT(*) FROM users;
Sayılar eşit olmalı. Eğer eşit değilse, dump dosyasında eksik tablo veya hatalı satır olabilir.
5. ORM ile Çalışan Projeler İçin: Django ve SQLAlchemy
Eğer Django kullanıyorsanız işiniz daha kolay. Django’nun migration sistemi veriyi taşımanın temiz bir yolunu sunar:
Adım 1: settings.py’da PostgreSQL’i ikincil veritabanı olarak ekleyin:
DATABASES = {
'default': {
'ENGINE': 'mssql', # mevcut SQLite config
'NAME': 'uygulama.db',
},
'postgres': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'uygulama_db',
'USER': 'uygulama_kullanici',
'PASSWORD': 'guvenli_sifre',
'HOST': 'localhost',
'PORT': '5432',
}
}
Adım 2: PostgreSQL’de migration’ları çalıştırın (tabloları oluşturmak için):
python manage.py migrate --database=postgres
Adım 3: Veriyi kopyalayın. Django’da bunun için bir script yazabilirsiniz:
from django.db import connections
from django.apps import apps
for model in apps.get_models():
qs = model.objects.using('default').all()
objects = list(qs)
for obj in objects:
obj.save(using='postgres')
Bu yöntem küçük-orta ölçekli veritabanları için iyi çalışır. Yüzbinlerce satırınız varsa bulk_create kullanın veya pgloader’ı tercih edin.
SQLAlchemy’de ise alembic ile benzer bir yaklaşım izleyebilirsiniz: önce PostgreSQL’de şemayı oluşturun, sonra INSERT INTO ... SELECT sorguları veya Python script’leri ile veriyi taşıyın.
6. Veri Doğrulama ve Tip Düzeltmeleri
Geçişten sonra bazı tip sorunları ortaya çıkabilir. En yaygın olanlar:
Boolean tipi: SQLite’da boolean’lar 0/1 olarak saklanır. pgloader genelde bunu otomatik çevirir ama manuel geçişte düzeltmeniz gerekir:
-- Eğer boolean kolonlar hala integer ise
ALTER TABLE users ALTER COLUMN is_active TYPE boolean
USING (is_active <> 0);
Timestamp tipi: SQLite’da tarihler TEXT olarak saklanır. PostgreSQL’de bunları TIMESTAMP’a çevirin:
ALTER TABLE users ALTER COLUMN created_at TYPE TIMESTAMP
USING TO_TIMESTAMP(created_at, 'YYYY-MM-DD HH:MI:SS');
Yabancı anahtar (foreign key) kontrolü: SQLite’da yabancı anahtar denetimi opsiyoneldir. Bu yüzden “yetim” kayıtlar oluşmuş olabilir. PostgreSQL’de bu kayıtlar hata verir. Önce kontrol edin:
-- Yetim kayıtları bul
SELECT * FROM orders o
LEFT JOIN users u ON o.user_id = u.id
WHERE u.id IS NULL;
Eğer bu sorgu satır dönerse, geçişten önce bu kayıtları temizleyin veya ON DELETE CASCADE ekleyin.
7. Uygulama Config’ini Güncelleme
Veri taşındı ve doğrulandı. Sıra uygulamayı yeni veritabanına yönlendirmeye geldi. Uygulamanızın config dosyasında bağlantı dizesini (connection string) değiştirin:
Django:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'uygulama_db',
'USER': 'uygulama_kullanici',
'PASSWORD': 'guvenli_sifre',
'HOST': 'localhost',
'PORT': '5432',
}
}
SQLAlchemy:
SQLALCHEMY_DATABASE_URI = "postgresql://uygulama_kullanici:guvenli_sifre@localhost/uygulama_db"
Prisma:
DATABASE_URL="postgresql://uygulama_kullanici:guvenli_sifre@localhost:5432/uygulama_db"
Uygulamayı başlatın ve kritik fonksiyonları test edin — giriş, kayıt, veri listeme, yazma. Her şey beklendiği gibi çalışıyorsa tebrikler, PostgreSQL’e geçtiniz!
8. Yaygın Sorunlar ve Çözümleri
Sorun 1: pgloader “type conversion error” veriyor
SQLite’da bir kolonda karışık tipte veri var (örneğin INTEGER kolonda bazı satırlarda string). pgloader bu satırları atar. Çözüm: önce SQLite’da veriyi temizleyin:
-- Hatalı satırları bulun
SELECT * FROM users WHERE typeof(age) != 'integer' AND age IS NOT NULL;
Temizleyin veya NULL yapın, sonra pgloader’ı tekrar çalıştırın.
Sorun 2: “permission denied for schema public”
PostgreSQL 15+ sonrasında public şemasının izinleri değişti. Çözüm:
GRANT ALL ON SCHEMA public TO uygulama_kullanici;
GRANT ALL ON ALL TABLES IN SCHEMA public TO uygulama_kullanici;
Sorun 3: Dil karakter sorunu (UTF-8 / Latin5)
SQLite dump’ında Türkçe karakterler bozuk çıkıyorsa, dump alırken encoding belirtin:
sqlite3 uygulama.db ".dump" | iconv -f UTF-8 -t UTF-8 > dump.sql
PostgreSQL veritabanının da UTF-8 olduğundan emin olun:
SHOW server_encoding;
-- UTF-8 olmalı
Sorun 4: Performance düşüşü
Geçişten sonra sorgular yavaşladıysa, muhtemelen indexler eksik. PostgreSQL’de index sorgusu çalıştırın:
SELECT schemaname, relname, indexrelname, idx_scan
FROM pg_stat_user_indexes
WHERE idx_scan = 0;
idx_scan = 0 olan indexler hiç kullanılmamış demektir. Eğer kritik kolonlarda index yoksa ekleyin:
CREATE INDEX idx_users_email ON users(email);
Sonuç
SQLite’tan PostgreSQL’e geçiş, ilk başta gözünü korkutsa da doğru araçlarla aslında oldukça düz bir süreç. pgloader ile tek komutta taşıyabilir, dump-restore ile tam kontrol sağlayabilir, veya ORM’in migration sistemi ile veriyi kopyalayabilirsiniz. Önemli olan: geçişten önce verinizi temizleyin, tip farklarını anlayın ve geçişten sonra mutlaka doğrulama yapın.
Birkaç yıl önce benim de bir projemde “database is locked” hatası her gece 03:00’da çıkardı — gece gelen batch job’lar gündüz biriken yazma işlemleriyle çarpışırdı. PostgreSQL’e geçince o hata bir daha hiç gelmedi. Aynı kod, aynı sorgular, ama altında artık sağlam bir motor vardı. Yani değer mi? Kesinlikle değer.
Okuduğunuz için teşekkür ederim! Geçiş sürecinde karşılaştığınız sorunları veya kendi deneyimlerinizi yorum kısmında paylaşın. Bir sonraki yazıda görüşmek üzere, iyi kodlamalar! 🚀