Eğer backend dünyasında bir süredir kod yazıyorsanız, şu durumu mutlaka yaşamışsınızdır: Bir "Sil" butonu yaparsınız, arkasına basit bir DELETE sorgusu koyarsınız ve iş biter. Ancak proje büyüyüp veriler karmaşıklaştıkça, o masum silme işlemi başınıza bela olmaya başlar.
Yanlışlıkla silinen bir müşteri kaydı geri getirilebilir mi? Silinen bir kategorinin altındaki ürünler raporda görünecek mi? Peki ya yasal zorunluluklar (KVKK/GDPR) veriyi gerçekten yok etmemizi isterse?
Bir junior geliştirici için silme işlemi "veriyi yok etmekten" ibarettir. Bir sistem mimarı için ise silme işlemi, verinin "durumunu değiştirmek" ve sistemin bütünlüğünü korumaktır. Yanlış kurgulanmış bir silme stratejisi, ileride performans sorunlarına, "unique constraint" hatalarına, N+1 problemlerine ve tutarsız raporlara yol açar.
Bu yazıda, veritabanı mimarisinde kullanılan temel silme stratejilerini, ORM seviyesindeki gizli maliyetleri ve production ortamında karşılaşacağınız sorunlara çözüm üreten pratik desenleri paylaşıyorum.
1. Hard Delete (Fiziksel Silme)
The Terminator Approach
SQL dünyasındaki DELETE FROM table WHERE id = 1 komutunun ta kendisidir. Veri diskten fiziksel olarak kaldırılır ve geri dönüşü (yedekler hariç) yoktur. Genellikle "asla veri silme" mantrasından dolayı korkulan bir yöntem olsa da, doğru yerde kullanıldığında sistemin sağlığı için elzemdir.
Bu yaklaşım şunları sağlar:
- KVKK ve GDPR Uyumu: Kullanıcı "Unutulma Hakkı"nı kullandığında, veriyi saklamaya devam etmek yasal bir risk oluşturabilir. Hard delete, en temiz çözümdür.
- Maliyet ve Performans: Milyarlarca satırlık log, session veya geçici cache tablolarını saklamanın bir anlamı yoktur. Bu verileri fiziksel olarak silmek, index boyutunu küçültür ve sorguları hızlandırır.
- Cascade Riski: Hard Delete kullanırken en büyük risk
ON DELETE CASCADEyapısıdır. Bir ana kaydı sildiğinizde, veritabanı arka planda ona bağlı binlerce alt kaydı da silebilir. Bu, veritabanında kilitlenmelere (deadlock) ve sistemin anlık olarak kilitlenmesine yol açabilir. Bu yüzden Hard Delete işlemleri genellikle transaction yönetimi açısından dikkat gerektirir.
2. Soft Delete (Mantıksal Silme)
The Safety Net
Veriyi silmek yerine, tabloya bir deleted_at (timestamp) veya is_deleted (boolean) kolonu ekleyerek işaretleme yöntemidir. Veri fiziksel olarak oradadır ama uygulama katmanı (WHERE deleted_at IS NULL diyerek) onu yok sayar.
Bu yaklaşım şunları sağlar:
- Hata Toleransı: "Pardon, yanlışlıkla sildim!" diyen bir kullanıcıyı kurtarmak, sadece tek bir
UPDATEsorgusuna bakar. - İlişkisel Bütünlük (Referential Integrity): Bir siparişi sildiğinizde, o siparişe bağlı fatura veya kargo kayıtlarının "yetim" (orphan) kalmasını engeller. Geçmişe dönük raporlamalarda veri tutarlılığını korur.
- Sorgu Karmaşıklığı: Soft Delete'in gizli maliyeti sorgulardadır. Yazdığınız her
JOINişleminde silinme durumunu kontrol etmek zorundasınız (users JOIN orders ON ... AND users.deleted_at IS NULL). Bunu unuttuğunuz tek bir sorgu, silinmiş kullanıcıların sisteme sızmasına neden olur.
3. Partial Index (Kısmi İndeks) Stratejisi
Soft Delete'in Kurtarıcısı
Soft Delete kullanırken en sık düşülen tuzak "Unique Constraint" problemidir. Örneğin, bir kullanıcı ([email protected]) hesabını sildi (soft delete). Bir ay sonra aynı mail ile tekrar kaydolmak istediğinde veritabanı "Bu mail zaten var" hatası verir. Çünkü veri silinmemiştir, sadece gizlenmiştir.
Bu strateji şunları sağlar:
- Benzersizlik Sorununun Çözümü: İndeksi
WHERE deleted_at IS NULLkoşuluyla oluşturarak, sadece aktif kullanıcıların maillerinin benzersiz olmasını sağlarsınız. Silinmiş kullanıcıların mailleri boşa çıkar. - İndeks Performansı: İndeksleriniz sadece aktif kayıtları içerir. 10 milyon silinmiş, 1 milyon aktif kaydınız varsa, indeksiniz sadece 1 milyonluk boyutta olur ve sorgularınız hızlanır.
- Mimari Esneklik: Yazılım tarafında "kirli" kontroller (
checkIfEmailExists) yapmadan, veri bütünlüğünü veritabanı seviyesinde garanti altına alırsınız.
4. Audit Logging & Archive Pattern
The Clean Slate
Eğer Soft Delete kullanma sebebiniz sadece "geçmişi görmek" veya "denetim" (audit) ise, ana operasyonel tablonuzu kirletmek yanlış bir tercihtir. Bu desende, veri ana tablodan gerçekten silinir (Hard Delete), ancak silinmeden hemen önce bir kopyası audit_logs veya users_archive tablosuna taşınır.
Bu yaklaşım şunları sağlar:
- Maksimum Performans: Ana tablonuz (
users,orders) her zaman küçük ve temiz kalır. Sorgularınızda sürekliWHERE is_deleted = 0yazmak zorunda kalmazsınız. - Modern CDC (Change Data Capture) Entegrasyonu: Büyük ölçekli sistemlerde bu işlemi veritabanı trigger'ları ile yapmak yerine Debezium veya Kafka gibi CDC araçlarıyla yapmak daha yaygındır. Veritabanındaki silme işlemi (DELETE log) okunur ve asenkron olarak bir veri ambarına (Data Warehouse) taşınır. Bu sayede ana uygulamanız hiç performans kaybetmez.
5. ORM ve Framework Tuzakları (Hibernate / Entity Framework)
The Hidden Cost
Birçok backend geliştirici, Soft Delete işini framework'lere (Spring Boot, Django, .NET vb.) bırakır. Örneğin Hibernate'te @SQLDelete ve @Where(clause="deleted_at is null") anotasyonları çok popülerdir. Ancak bu kolaycılığın ciddi bedelleri olabilir:
- Native Query Riski: Eğer projenizde performans için saf SQL (Native Query) yazarsanız, ORM'in otomatik filtrelemesi devre dışı kalır. Silinmiş verileri yanlışlıkla raporlara dahil edebilirsiniz.
- İlişkisel Yükleme (Lazy Loading): Soft delete yapılmış bir kayda, başka bir entity üzerinden erişmeye çalıştığınızda (örneğin silinmiş bir kullanıcının siparişlerini listelerken), ORM'ler bazen
EntityNotFoundExceptionfırlatabilir veya beklenmedikNULLhataları üretebilir. Bu durum, kodun içinde gereksiztry-catchbloklarına yol açar.
6. Hibrit Yaklaşım: Ertelenmiş Silme (Delayed Hard Delete)
The Trash Can Model
Soft Delete ve Hard Delete'in en iyi yönlerini birleştirir. Tıpkı işletim sisteminizdeki "Çöp Kutusu" veya Apple Photos'daki "Son Silinenler" mantığı gibi çalışır. Veri önce Soft Delete ile işaretlenir, ancak belirli bir süre (örneğin 30 gün) sonra bir zamanlanmış görev (Cron Job) tarafından Hard Delete ile tamamen silinir.
Bu yaklaşım şunları sağlar:
- Kullanıcı Güveni: Kullanıcıya pişman olması için bir zaman penceresi (grace period) tanırsınız.
- Sürdürülebilirlik: Veritabanınız sonsuza kadar şişmez; kendi kendini temizleyen bir mekanizmaya sahip olur.
- Yasal ve Operasyonel Denge: Hem veriyi kurtarma şansınız olur hem de uzun vadede "gereksiz veri saklama" riskinden kurtulursunuz. Özellikle GDPR gereği veriyi belli bir süre sonra tamamen yok etmeniz gerektiğinde bu model hayat kurtarır.
Son Söz
Veritabanı tasarımı yaparken "Her tabloya bir is_deleted kolonu ekleyelim, güvenli olsun" demek, genellikle tembel bir mimari yaklaşımdır.
Verinin doğasına göre karar vermelisiniz:
- Finansal ve Yasal Kayıtlar (Fatura, Sipariş) için Soft Delete + Audit,
- Geçici ve Önemsiz Veriler (Log, Session, Cache) için Hard Delete,
- Benzersizlik Gerektiren Hesaplar (Kullanıcılar, E-postalar) için Partial Index destekli çözümler,
- Kullanıcı İçeriği ve "Geri Alma" İhtiyacı (Notlar, Fotoğraflar, Taslaklar) için Delayed Hard Delete,
- Büyük veri ve analiz için CDC tabanlı Arşivleme en doğrusudur.
Unutmayın; kodunuzu her gün değiştirebilirsiniz, framework'leri güncelleyebilirsiniz ama veritabanı mimarisi bir binanın temeli gibidir. Yanlış atılan bir temel, bina yükseldikçe düzeltilmesi imkansız çatlaklara yol açar. "DELETE" tuşuna basarken (veya basmazken) bu prensipleri göz önünde bulundurun.
