Har bir yazılım geliştiricinin kariyerinde bir an gelir: "Veritabanı bozuldu, veriler tutarsız" cümlesini duyar. İşte tam bu noktada ACID prensipleri, veritabanı işlemlerinin (transaction) güvenilirliğini sağlayan dört temel yapı taşı olarak karşımıza çıkar.
Bu yazıda, ACID kısaltmasının ardındaki Atomicity, Consistency, Isolation, ve Durability kavramlarını, teorik mantıklarından pratik kod örneklerine kadar detaylı bir şekilde inceleyeceğiz.
1. Transaction (İşlem) Nedir?
ACID'i anlamak için önce transaction kavramını anlamalıyız. Transaction, mantıksal bir bütün olarak ele alınması gereken bir veya daha fazla veritabanı işleminin (INSERT, UPDATE, DELETE) bir araya gelmesidir.
Klasik Örnek: Banka Havalesi
- A hesabından 100 TL çek.
- B hesabına 100 TL ekle.
Eğer para çekildikten sonra sistem çökerse, paranın havada kalması gerekir. İşte bu iki işlem tek bir transaction olarak ele alınmalıdır.
2. Atomicity (Atomiklik)
Mantık
Atomicity, bir transaction içindeki tüm işlemlerin "ya hep ya hiç" prensibiyle çalışmasını garantiler. İşlemlerin bir kısmı başarılı olup bir kısmı başarısız olamaz. Eğer transaction'ın herhangi bir adımı başarısız olursa, daha önce başarılı olan adımlar da geri alınır (Rollback).
Teknik Çalışma Prensibi
Veritabanı, Atomicity'i sağlamak için genellikle Write-Ahead Logging (WAL) veya Shadow Paging gibi teknikler kullanır. WAL'da, değişiklikler asıl veritabanına yazılmadan önce bir log'a (günlük) yazılır. Bir hata durumunda, bu log kullanılarak işlemler geri alınabilir.
Kod Örneği (Python + SQLAlchemy)
from sqlalchemy import create_engine, text
from sqlalchemy.exc import SQLAlchemyError
engine = create_engine('postgresql://user:pass@localhost/mydb')
connection = engine.connect()
transaction = connection.begin() # Transaction başlat
try:
# 1. Adım: Ali'nin hesabından para çek
connection.execute(
text("UPDATE hesaplar SET bakiye = bakiye - 100 WHERE isim = 'Ali'")
)
# 2. Adım: Veli'nin hesabına para ekle
connection.execute(
text("UPDATE hesaplar SET bakiye = bakiye + 100 WHERE isim = 'Veli'")
)
# Eğer her şey yolundaysa, değişiklikleri kalıcı yap
transaction.commit()
print("Havale başarılı!")
except SQLAlchemyError as e:
# Hata olursa, tüm değişiklikleri geri al
transaction.rollback()
print(f"Havale başarısız! Hata: {e}, Her şey geri alındı.")
finally:
connection.close()
3. Consistency (Tutarlılık)
Mantık
Consistency, bir transaction tamamlandığında veritabanının belirli kurallara (constraint) uygun bir durumda olmasını garantiler. Bu, transaction öncesinde geçerli olan tüm veritabanı kurallarının (Foreign Key, Unique, Check constraint, trigger vb.) transaction sonrasında da geçerli olması demektir.
Teknik Çalışma Prensibi
Consistency, uygulama geliştiricisi ve veritabanı yönetim sisteminin (DBMS) ortak sorumluluğudur. Geliştirici transaction'ı doğru yazmalı, DBMS ise tanımlı tüm kuralların uygulanmasını zorunlu kılmalıdır. Eğer bir transaction, veritabanını tutarsız bir duruma sokacaksa (örneğin, Unique bir sütuna aynı değeri iki kere eklemeye çalışırsa), transaction başarısız olur.
Kod Örneği (Mantıksal Tutarlılık)
Bir bankada bakiyelerin hiçbir zaman 0'ın altına düşmemesi gerektiğini düşünelim.
-- Tabloya bir CHECK constraint ekleyelim
ALTER TABLE hesaplar ADD CONSTRAINT bakiye_kontrol CHECK (bakiye >= 0);
-- Transaction
BEGIN;
UPDATE hesaplar SET bakiye = bakiye - 500 WHERE isim = 'Ali'; -- Ali'nin 200 TL'si var
COMMIT;
-- Sonuç: Constraint ihlali olduğu için transaction otomatik olarak ROLLBACK edilir.
-- Veritabanı hala tutarlıdır, Ali'nin bakiyesi değişmez.
4. Isolation (İzolasyon)
Mantık
Isolation, aynı anda çalışan birden fazla transaction'ın birbirini etkilemesini engeller. Bir transaction, diğeri tamamlanana kadar onun verilerini görmemelidir. İzolasyon seviyeleri, performans ve tutarlılık arasında bir denge kurar.
İzolasyon Seviyeleri ve Sorunları
- Dirty Read: Bir transaction, henüz commit edilmemiş bir transaction'ın yaptığı değişikliği okur.
- Non-repeatable Read: Bir transaction aynı veriyi iki kere okuduğunda, aradaki bir başka transaction'ın yaptığı güncelleme nedeniyle farklı sonuçlar alması.
- Phantom Read: Bir transaction aynı sorguyu iki kere çalıştırdığında, aradaki bir başka transaction'ın eklediği yeni satırlar nedeniyle farklı sonuç kümeleri görmesi.
Teknik Çalışma Prensibi (Locking)
Veritabanları, Isolation'ı sağlamak için genellikle Lock (Kilit) mekanizmaları kullanır.
- Paylaşımlı Kilit (Shared Lock - S Lock): Okumaya izin verir, yazmayı engeller.
- Özel Kilit (Exclusive Lock - X Lock): Hem okumayı hem yazmayı engeller. (Genellikle UPDATE/DELETE işlemlerinde)
Kod Örneği (İzolasyon Seviyesi Ayarlama)
Farklı izolasyon seviyelerini, aynı anda çalışan iki transaction ile inceleyelim.
Transaction 1 (Para transferi yapıyor)
-- İzolasyon seviyesini READ COMMITTED olarak ayarla (PostgreSQL varsayılanı)
BEGIN;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
SELECT bakiye FROM hesaplar WHERE isim = 'Ali'; -- 100 TL görür
-- Burada Transaction 2'nin çalıştığını varsayalım.
-- Transaction 2, Ali'nin bakiyesini 200 yapıp COMMIT etti.
SELECT bakiye FROM hesaplar WHERE isim = 'Ali'; -- 200 TL görür. (Non-repeatable Read gerçekleşti!)
COMMIT;
Transaction 2 (Bakiye güncelliyor)
BEGIN;
UPDATE hesaplar SET bakiye = 200 WHERE isim = 'Ali';
COMMIT;
Daha Sıkı İzolasyon (Repeatable Read)
-- Transaction 1
BEGIN;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SELECT bakiye FROM hesaplar WHERE isim = 'Ali'; -- 100 TL görür
-- Transaction 2 çalışıp bakiye'yi 200 yapıp COMMIT etti.
SELECT bakiye FROM hesaplar WHERE isim = 'Ali';
-- Hala 100 TL görür! (Repeatable Read sayesinde transaction anlık görüntü (snapshot) üzerinden okuma yapar)
COMMIT; -- Commit'ten sonra yeni sorgular 200'ü görecektir.
5. Durability (Kalıcılık)
Mantık
Durability, başarıyla commit edilmiş bir transaction'ın yaptığı değişikliklerin, sistem çökse veya güç gitse bile kalıcı olmasını garantiler. Veri asla kaybolmaz.
Teknik Çalışma Prensibi
- Write-Ahead Logging (WAL): Değişiklikler diskteki asıl veri dosyasına yazılmadan önce, bir log dosyasına yazılır. Bir transaction commit olduğunda, log kaydının diske fiziksel olarak yazılması beklenir. Sistem çökerse, yeniden başlatıldığında bu log dosyası okunarak kaybolan transaction'lar tekrar uygulanır (replay).
- Yedekleme: Düzenli yedeklemeler de uzun vadeli durability'nin bir parçasıdır.
Peki Ya NoSQL?
Geleneksel SQL veritabanları (PostgreSQL, MySQL, Oracle) ACID'i tam olarak uygularken, birçok NoSQL veritabanı (MongoDB, Cassandra) ölçeklenebilirlik ve performans uğruna ACID'in bazı özelliklerinden feragat eder. Bu durum BASE (Basically Available, Soft state, Eventual consistency - Temelde Erişilebilir, Yumuşak Durum, Nihai Tutarlılık) prensipleriyle açıklanır. Ancak günümüzde birçok NoSQL veritabanı da artık ACID transaction desteği sunmaya başlamıştır.
Sonuç
ACID, bir veritabanı işlemine güvenebilmemiz için olmazsa olmazdır. Bir e-ticaret sitesinde sipariş verirken, banka havalesi yaparken veya bir sosyal medyada paylaşım yaparken, arka planda ACID prensipleri verilerimizin güvende olduğunu garanti eder.
Unutmayın, doğru izolasyon seviyesini seçmek bir mimarlık kararıdır. Tutarlılık mı sizin için daha önemli, yoksa yüksek performans mı? Cevabınız, hangi izolasyon seviyesini kullanacağınızı belirleyecektir.