Transaction Temelleri: BEGIN, COMMIT, ROLLBACK
Giriş — Ya Hep Ya Hiç
Bankadan havale yaptığını düşün. Hesabından 5000 TL çekilecek ve alıcının hesabına 5000 TL eklenecek. Ya ikisi de olmalı, ya hiçbiri. "Senin hesabından çekildi ama alıcıya gitmedi" durumu kabul edilemez.
İşte transaction (işlem) kavramı tam olarak bunu garanti eder: birden fazla veritabanı operasyonunu tek bir atomik (bölünmez) birim olarak çalıştırır. Ya hepsi başarılı olur ve sonuçlar kalıcı olur, ya da biri bile başarısız olursa hepsi geri alınır.
🎯 Analoji: Bir sihirbazın gösteri sahnesini düşün. Sihirbaz bir dizi hile yapıyor — hepsini doğru yaparsa alkış alıyor (COMMIT). Ama bir hilede hata yaparsa, sihirbaz "sıfırlama" düğmesine basıyor ve sahne başa dönüyor (ROLLBACK). Seyirci yarım kalmış bir gösteri görmüyor — ya tam, ya hiç.
Transaction Nedir?
Transaction, mantıksal olarak bir bütün oluşturan bir veya birden fazla SQL komutunun gruplanmasıdır. Bu grubun ya tamamı başarılı olur (COMMIT) ya da hiçbiri uygulanmaz (ROLLBACK).
Transaction Yaşam Döngüsü
START TRANSACTION
│
▼
┌──────────────┐
│ SQL Komut 1 │ ← Henüz kalıcı değil (geçici)
│ SQL Komut 2 │
│ SQL Komut 3 │
└──────┬───────┘
│
┌────┴────┐
▼ ▼
COMMIT ROLLBACK
│ │
▼ ▼
Kalıcı Geri al
olur (hiçbir
şey olmadı)Temel Komutlar
START TRANSACTION (veya BEGIN)
-- Transaction başlat (iki syntax da aynı şeyi yapar)
START TRANSACTION;
-- veya
BEGIN;COMMIT — Değişiklikleri Kaydet
START TRANSACTION;
UPDATE accounts SET balance = balance - 5000 WHERE account_id = 1;
UPDATE accounts SET balance = balance + 5000 WHERE account_id = 2;
-- Her şey başarılı, değişiklikleri kalıcı yap
COMMIT;
-- Artık iki hesap da güncellendi, geri dönüş yokROLLBACK — Değişiklikleri Geri Al
START TRANSACTION;
UPDATE accounts SET balance = balance - 5000 WHERE account_id = 1;
-- Bir şeyler ters gitti...
UPDATE accounts SET balance = balance + 5000 WHERE account_id = 999;
-- account_id = 999 yok! (veya başka bir hata)
-- Tüm değişiklikleri geri al
ROLLBACK;
-- Hesap 1'in bakiyesi de eski haline döndüAutocommit Modu
MySQL'de varsayılan olarak autocommit açıktır. Bu, her SQL komutu otomatik olarak commit edilir:
-- Autocommit açıkken (varsayılan):
INSERT INTO customers (first_name, last_name, email)
VALUES ('Test', 'User', 'test@email.com');
-- Bu INSERT hemen kalıcı olur — COMMIT'e gerek yok
-- Autocommit durumunu kontrol et
SELECT @@autocommit;
-- 1 = açık (varsayılan)
-- Autocommit'i kapat
SET autocommit = 0;
-- Artık her şey explicit COMMIT gerektirir
INSERT INTO customers (first_name, last_name, email)
VALUES ('Test2', 'User2', 'test2@email.com');
-- Henüz kalıcı değil!
COMMIT; -- Şimdi kalıcı
-- veya
ROLLBACK; -- Geri al💡 İpucu:
START TRANSACTIONkullandığında autocommit otomatik olarak o transaction süresince kapatılır. COMMIT veya ROLLBACK'ten sonra autocommit tekrar eski haline döner. Bu yüzden genellikle autocommit ayarını değiştirmek yerineSTART TRANSACTIONkullanmak daha güvenlidir.
ACID Prensipleri — Transaction'ın Garantileri
Transaction'lar ACID prensiplerini uygular. İlk derste kısaca değinmiştik, şimdi örneklerle pekiştirelim:
A — Atomicity (Bölünmezlik)
START TRANSACTION;
-- Sipariş oluştur
INSERT INTO orders (customer_id, total_amount, status)
VALUES (1, 1599.98, 'pending');
SET @oid = LAST_INSERT_ID();
-- Sipariş kalemlerini ekle
INSERT INTO order_items (order_id, product_id, quantity, unit_price)
VALUES (@oid, 6, 1, 1299.99);
INSERT INTO order_items (order_id, product_id, quantity, unit_price)
VALUES (@oid, 5, 1, 299.99);
-- Stokları güncelle
UPDATE products SET stock_quantity = stock_quantity - 1 WHERE product_id = 6;
UPDATE products SET stock_quantity = stock_quantity - 1 WHERE product_id = 5;
COMMIT;
-- 5 operasyon ya hep birlikte başarılı oldu, ya hiçbiri olmayacaktıC — Consistency (Tutarlılık)
START TRANSACTION;
-- Stok kontrolü
SELECT stock_quantity INTO @stock FROM products WHERE product_id = 5;
-- Stok yeterliyse sipariş ver
IF @stock >= 1 THEN
UPDATE products SET stock_quantity = stock_quantity - 1 WHERE product_id = 5;
INSERT INTO order_items (...) VALUES (...);
COMMIT;
ELSE
ROLLBACK; -- Stok yetersiz, hiçbir şey yapma
END IF;
-- Veritabanı her zaman tutarlı durumda kalırI — Isolation (Yalıtım)
-- Kullanıcı A (oturum 1)
START TRANSACTION;
SELECT stock_quantity FROM products WHERE product_id = 5; -- 500
UPDATE products SET stock_quantity = stock_quantity - 1 WHERE product_id = 5;
-- Henüz COMMIT edilmedi
-- Kullanıcı B (oturum 2) — aynı anda
START TRANSACTION;
SELECT stock_quantity FROM products WHERE product_id = 5;
-- Isolation level'a bağlı olarak ya 500 ya da 499 görür
-- (Bu konuyu B12'de detaylıca işleyeceğiz)D — Durability (Kalıcılık)
START TRANSACTION;
INSERT INTO orders (...) VALUES (...);
COMMIT;
-- COMMIT başarılı olduysa, sunucu çökse bile bu sipariş veritabanında duracak
-- InnoDB redo log ve write-ahead logging mekanizmalarıyla bunu garanti ederSAVEPOINT — Kısmi Geri Alma
Transaction içinde "kayıt noktaları" oluşturabilir ve belirli bir noktaya kadar geri alabilirsin:
START TRANSACTION;
-- Sipariş oluştur
INSERT INTO orders (customer_id, total_amount, status)
VALUES (2, 0, 'pending');
SET @oid = LAST_INSERT_ID();
SAVEPOINT siparis_olusturuldu;
-- İlk ürünü ekle
INSERT INTO order_items (order_id, product_id, quantity, unit_price)
VALUES (@oid, 1, 1, 54999.99);
SAVEPOINT urun1_eklendi;
-- İkinci ürünü eklemeye çalış — hata oldu diyelim
INSERT INTO order_items (order_id, product_id, quantity, unit_price)
VALUES (@oid, 999, 1, 0); -- product_id 999 yok!
-- Sadece ikinci eklemeyi geri al, birinci ürün kalsın
ROLLBACK TO SAVEPOINT urun1_eklendi;
-- Farklı bir ürün ekle
INSERT INTO order_items (order_id, product_id, quantity, unit_price)
VALUES (@oid, 5, 2, 299.99);
-- Toplam tutarı güncelle
UPDATE orders SET total_amount = 54999.99 + 599.98 WHERE order_id = @oid;
-- Her şey tamam, kaydet
COMMIT;SAVEPOINT kuralları:
SAVEPOINT isim→ Kayıt noktası oluştururROLLBACK TO SAVEPOINT isim→ O noktaya kadar geri alır (sonrasını siler)RELEASE SAVEPOINT isim→ Kayıt noktasını kaldırır (artık geri alınamaz)ROLLBACK→ Tüm transaction'ı geri alır (tüm savepoint'ler dahil)COMMIT→ Tüm transaction'ı kaydeder (tüm savepoint'ler dahil)
START TRANSACTION
│
├── INSERT (sipariş)
│
├── SAVEPOINT sp1
│ │
│ ├── INSERT (ürün 1)
│ │
│ ├── SAVEPOINT sp2
│ │ │
│ │ ├── INSERT (ürün 2) ← HATA!
│ │ │
│ │ └── ROLLBACK TO sp2 ← Sadece ürün 2 geri alındı
│ │
│ └── INSERT (ürün 3) ← Yeni deneme
│
└── COMMIT → Sipariş + ürün 1 + ürün 3 kalıcıDDL ve Transaction — Önemli Uyarı
MySQL'de DDL komutları (CREATE, ALTER, DROP, TRUNCATE) implicit commit yapar — yani transaction'ı otomatik kaydeder:
START TRANSACTION;
INSERT INTO customers (first_name, last_name, email)
VALUES ('Test', 'User', 'test99@email.com');
-- DDL komutu — transaction otomatik COMMIT olur!
CREATE TABLE temp_test (id INT);
-- Artık ROLLBACK ile INSERT'i geri alamazsın!
ROLLBACK; -- Çok geç — INSERT zaten commit edildi⚠️ Dikkat: Transaction içinde asla DDL komutu çalıştırma. CREATE TABLE, ALTER TABLE, DROP TABLE, TRUNCATE gibi komutlar transaction'ı otomatik commit eder. Bu, transaction'ın geri kalan kısmının da kalıcı olmasına neden olur.
📝 PostgreSQL Notu: PostgreSQL'de DDL komutları transactional'dır — yani DDL'ler de ROLLBACK ile geri alınabilir. Bu PostgreSQL'in büyük avantajlarından biridir: ``
sql -- PostgreSQL — DDL de transaction içinde çalışır BEGIN; CREATE TABLE test (id INT); ROLLBACK; -- Tablo oluşturulmadı, geri alındı ✓``
Implicit vs Explicit Transaction
-- Implicit transaction (autocommit açık):
-- Her komut kendi başına bir transaction
INSERT INTO products (...) VALUES (...); -- Otomatik COMMIT
UPDATE products SET price = 100; -- Otomatik COMMIT
DELETE FROM products WHERE id = 5; -- Otomatik COMMIT
-- Explicit transaction:
-- Birden fazla komutu bir birim olarak yönet
START TRANSACTION;
INSERT INTO products (...) VALUES (...); -- Henüz kalıcı değil
UPDATE products SET price = 100; -- Henüz kalıcı değil
DELETE FROM products WHERE id = 5; -- Henüz kalıcı değil
COMMIT; -- Şimdi hepsi kalıcıTransaction Kilitleme (Locking)
Transaction sırasında MySQL, ilgili satırları kilitler — başka oturumların aynı anda aynı veriyi değiştirmesini engeller:
-- Oturum 1:
START TRANSACTION;
UPDATE products SET stock_quantity = stock_quantity - 1 WHERE product_id = 5;
-- product_id = 5 satırı KİLİTLENDİ
-- COMMIT veya ROLLBACK yapılana kadar kilitli kalır
-- Oturum 2 (aynı anda):
UPDATE products SET price = 999 WHERE product_id = 5;
-- BEKLİYOR... Oturum 1 COMMIT veya ROLLBACK yapana kadar
-- Oturum 1:
COMMIT;
-- Kilit açıldı, Oturum 2 devam edebilirSELECT ... FOR UPDATE — Okurken Kilitle
START TRANSACTION;
-- Stoku oku VE kilitle (başkası değiştiremesin)
SELECT stock_quantity
FROM products
WHERE product_id = 5
FOR UPDATE;
-- stock_quantity = 500 ve satır kilitlendi
-- Kontrol et ve güncelle
UPDATE products
SET stock_quantity = stock_quantity - 1
WHERE product_id = 5;
COMMIT;FOR UPDATE olmadan, iki oturum aynı anda stoku okuyup ikisi de "yeterli" diye karar verip stoktan düşebilir — race condition!
-- ❌ Race condition (FOR UPDATE olmadan):
-- Oturum 1: SELECT stock → 1
-- Oturum 2: SELECT stock → 1 (aynı anda)
-- Oturum 1: UPDATE stock = 0 → ✓
-- Oturum 2: UPDATE stock = 0 → ✓ (Ama stok 1'di, iki sipariş verildi!)
-- ✅ Güvenli (FOR UPDATE ile):
-- Oturum 1: SELECT stock FOR UPDATE → 1 (satır kilitlendi)
-- Oturum 2: SELECT stock FOR UPDATE → BEKLİYOR...
-- Oturum 1: UPDATE stock = 0, COMMIT → Kilit açıldı
-- Oturum 2: SELECT stock FOR UPDATE → 0 (artık stok yok, sipariş reddedilir)Deadlock — Karşılıklı Kilitlenme
İki oturum birbirinin kilitlediği kaynağı beklerse deadlock oluşur:
-- Oturum 1:
START TRANSACTION;
UPDATE products SET price = 100 WHERE product_id = 1; -- Satır 1 kilitlendi
-- Satır 2'yi de güncellemek istiyor...
-- Oturum 2:
START TRANSACTION;
UPDATE products SET price = 200 WHERE product_id = 2; -- Satır 2 kilitlendi
-- Satır 1'i de güncellemek istiyor...
-- Oturum 1:
UPDATE products SET price = 300 WHERE product_id = 2;
-- BEKLİYOR (Oturum 2, satır 2'yi kilitledi)
-- Oturum 2:
UPDATE products SET price = 400 WHERE product_id = 1;
-- BEKLİYOR (Oturum 1, satır 1'i kilitledi)
-- 💀 DEADLOCK! İkisi de birbirini bekliyor!
-- MySQL deadlock'ı tespit eder ve bir oturumu ROLLBACK yapar
-- ERROR 1213: Deadlock found when trying to get lock; try restarting transactionDeadlock'ı önlemek için:
Kaynaklara her zaman aynı sırada eriş (önce product_id = 1, sonra 2)
Transaction'ları kısa tut
Uygulamada deadlock hatasını yakala ve transaction'ı tekrar dene
Gerçek Dünya Örneği — E-Ticaret Sipariş Transaction'ı
DELIMITER //
-- Güvenli sipariş oluşturma prosedürü (kavramsal)
-- (Stored procedure'ları B12'de detaylıca göreceğiz)
START TRANSACTION;
-- 1. Stok kontrolü (kilitleme ile)
SELECT product_id, stock_quantity
FROM products
WHERE product_id IN (6, 5)
FOR UPDATE;
-- Uygulama katmanında kontrol:
-- Stok yeterli mi? Evet → devam et, Hayır → ROLLBACK
-- 2. Sipariş oluştur
INSERT INTO orders (customer_id, order_date, total_amount, status)
VALUES (1, NOW(), 1599.98, 'pending');
SET @order_id = LAST_INSERT_ID();
-- 3. Sipariş kalemlerini ekle
INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES
(@order_id, 6, 1, 1299.99),
(@order_id, 5, 1, 299.99);
-- 4. Stokları düşür
UPDATE products SET stock_quantity = stock_quantity - 1
WHERE product_id = 6 AND stock_quantity >= 1;
-- Etkilenen satır kontrolü
-- ROW_COUNT() = 0 ise stok yetersiz → ROLLBACK
UPDATE products SET stock_quantity = stock_quantity - 1
WHERE product_id = 5 AND stock_quantity >= 1;
-- 5. Her şey başarılı
COMMIT;
-- NOT: Gerçek uygulamada bu mantık uygulama katmanında (PHP, Python, Java)
-- kontrol edilir. Her adımdan sonra hata kontrolü yapılır ve
-- gerekirse ROLLBACK çağrılır.Transaction Best Practices
Transaction'ı kısa tut: Uzun transaction = uzun süre kilitli satırlar = diğer kullanıcılar bekler. Mümkün olduğunca hızlı COMMIT veya ROLLBACK yap.
Büyük işlemleri parçala: 1 milyon satır güncelleme yapacaksan, tek transaction yerine 10.000'erlik gruplar halinde yap.
Uygulamada hata yönetimi yap:
# Python pseudo-code
try:
cursor.execute("START TRANSACTION")
cursor.execute("INSERT INTO orders ...")
cursor.execute("INSERT INTO order_items ...")
cursor.execute("UPDATE products ...")
cursor.execute("COMMIT")
except Exception as e:
cursor.execute("ROLLBACK")
raise eDeadlock'a hazırlıklı ol: Deadlock normal bir durumdur — uygulamada yakalayıp transaction'ı tekrar dene.
READ operasyonlarında gereksiz transaction açma: Sadece
SELECTyapıyorsan genellikle explicit transaction'a gerek yok.Transaction içinde DDL kullanma: MySQL'de DDL implicit commit yapar — transaction mantığını bozar.
Sıkça Yapılan Hatalar
Transaction kapatmayı unutmak:
START TRANSACTIONdeyipCOMMITveyaROLLBACKyapmayı unutursan, transaction açık kalır, kilitler devam eder, diğer kullanıcılar bekler. Bağlantı koptuğunda MySQL otomatik ROLLBACK yapar ama o zamana kadar performans sorunu yaratır.DDL'in implicit commit yapacağını bilmemek: Transaction içinde
CREATE TABLEçalıştırdığında önceki tüm değişiklikler commit olur.Çok uzun transaction: 30 dakikalık bir transaction, ilgili tüm satırları kilitler. Diğer işlemler bu satırlara erişemez, timeout alır. Transaction'ı olabildiğince kısa tut.
ROLLBACK'i kullanmamak: Hata durumunda ROLLBACK yapmamak, yarım kalmış verilerin kalıcı olmasına yol açar (autocommit açıksa her komut ayrı ayrı commit edilir).
Deadlock'u anlamadan panik yapmak: Deadlock normal bir durumdur, hata değildir. Uygulamada yakala, transaction'ı tekrar dene, 2-3 deneme sonra gerçek hata olarak raporla.
Özet
Transaction, birden fazla SQL komutunu tek bir atomik birim olarak çalıştırır
START TRANSACTION/BEGIN→ Transaction başlatırCOMMIT→ Değişiklikleri kalıcı yaparROLLBACK→ Tüm değişiklikleri geri alırACID prensipleri: Atomicity, Consistency, Isolation, Durability
SAVEPOINTile transaction içinde kısmi geri alma yapılabilirMySQL'de DDL komutları implicit commit yapar — transaction içinde kullanma
SELECT ... FOR UPDATEile satırları okurken kilitleyebilirsin (race condition önleme)Deadlock iki oturumun birbirini beklemesidir — MySQL birini otomatik ROLLBACK yapar
Transaction'ları kısa tut, büyük işlemleri parçala, hata yönetimi uygula
AI Asistan
Sorularını yanıtlamaya hazır