MariaDB ve MySQL’de TRANSACTION, COMMIT ve ROLLBACK Kullanımı
Veritabanı yönetiminde yapılan en büyük hatalardan biri, kritik işlemleri bir güvenlik ağı olmadan doğrudan çalıştırmaktır. Yanlış bir UPDATE ya da DELETE sorgusu, geri dönüşü olmayan veri kayıplarına yol açabilir. İşte tam bu noktada transaction yönetimi devreye girer. MariaDB ve MySQL’de TRANSACTION, COMMIT ve ROLLBACK mekanizmalarını doğru kullanmak, hem veri bütünlüğünü korur hem de felaket senaryolarında sizi kurtarır. Bu yazıda bu kavramları gerçek dünya senaryolarıyla, bol kod örneğiyle ele alacağız.
Transaction Nedir ve Neden Önemlidir?
Transaction, bir ya da birden fazla SQL sorgusunu tek bir mantıksal birim olarak ele alan bir mekanizmadır. Ya hepsi başarıyla tamamlanır ya da hiçbiri uygulanmaz. Bu özellik, veritabanı dünyasında ACID prensiplerine dayanır.
- Atomicity (Atomiklik): İşlemler bölünemez bir bütündür, ya tamamen gerçekleşir ya da hiç gerçekleşmez.
- Consistency (Tutarlılık): İşlem tamamlandığında veritabanı tutarlı bir durumda olmalıdır.
- Isolation (İzolasyon): Eş zamanlı çalışan işlemler birbirini etkilemez.
- Durability (Kalıcılık): Bir kez commit edilen veri, sistem çökmesi dahil her koşulda kalıcıdır.
Günlük sysadmin hayatında transaction kullanmadan kritik sorgular çalıştırmak, paraşütsüz atlayışa benzer. Özellikle üretim ortamlarında bunu asla yapmamalısınız.
InnoDB ve MyISAM Farkı
Transaction kullanabilmek için tablo motorunuzun InnoDB olması gerekir. MyISAM, transaction desteklemez. MariaDB ve MySQL’de varsayılan motor genellikle InnoDB olarak gelir ama kontrol etmek iyi bir alışkanlıktır.
-- Tablo motorunu kontrol et
SHOW TABLE STATUS WHERE Name = 'siparisler';
-- Tüm tablolerin motorlarini listele
SELECT TABLE_NAME, ENGINE
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'veritabani_adi';
-- Bir tabloyu InnoDB'ye cevir
ALTER TABLE siparisler ENGINE = InnoDB;
Eğer tablonuz MyISAM ise ROLLBACK çalışmaz ve veriler anında yazılır. Üretim ortamında mutlaka InnoDB kullanın.
Temel Transaction Söz Dizimi
MariaDB ve MySQL’de transaction yapısı oldukça basittir. Üç temel komut vardır.
- START TRANSACTION: Yeni bir transaction başlatır.
- COMMIT: Yapılan tüm değişiklikleri kalıcı olarak veritabanına yazar.
- ROLLBACK: Transaction başlangıcından bu yana yapılan tüm değişiklikleri geri alır.
-- Temel transaction yapisi
START TRANSACTION;
UPDATE hesaplar SET bakiye = bakiye - 1000 WHERE hesap_id = 1;
UPDATE hesaplar SET bakiye = bakiye + 1000 WHERE hesap_id = 2;
COMMIT;
Bu örnekte banka transferi simüle edilmiştir. İki UPDATE işlemi ya birlikte tamamlanır ya da ikisi de uygulanmaz. Birinci UPDATE başarılı olup ikincisi başarısız olursa, ROLLBACK devreye girerek bakiyeler eski haline döner.
AUTOCOMMIT Ayarı
MySQL ve MariaDB varsayılan olarak AUTOCOMMIT modunda çalışır. Bu, her SQL sorgusunun otomatik olarak commit edildiği anlamına gelir. Transaction kullanmak istiyorsanız bunu kapatmanız ya da START TRANSACTION kullanmanız gerekir.
-- Mevcut autocommit durumunu goruntule
SHOW VARIABLES LIKE 'autocommit';
-- Autocommit'i oturum bazinda kapat
SET autocommit = 0;
-- Autocommit'i tekrar ac
SET autocommit = 1;
-- Autocommit kapali oldugunda manuel commit gerekir
SET autocommit = 0;
INSERT INTO urunler (ad, fiyat) VALUES ('Laptop', 15000);
INSERT INTO urunler (ad, fiyat) VALUES ('Mouse', 250);
COMMIT; -- Bu olmadan degisiklikler kalici olmaz
Dikkat: autocommit’i kapatıp COMMIT yapmayı unutursanız, oturumu kapattığınızda değişiklikler otomatik olarak ROLLBACK’e uğrar. Bu genellikle başlangıçta şaşırtıcı bir durum olarak karşımıza çıkar.
Gerçek Dünya Senaryosu 1: E-Ticaret Sipariş İşlemi
Bir e-ticaret sisteminde sipariş verildiğinde birden fazla tablonun güncellenmesi gerekir. Stok azaltılır, sipariş oluşturulur, ödeme kaydedilir. Bu işlemlerden herhangi biri başarısız olursa tüm işlem geri alınmalıdır.
START TRANSACTION;
-- Stok kontrolu ve guncelleme
SELECT stok_adet FROM urunler WHERE urun_id = 42 FOR UPDATE;
-- Stok yeterliyse devam et
UPDATE urunler
SET stok_adet = stok_adet - 3
WHERE urun_id = 42 AND stok_adet >= 3;
-- Etkilenen satir yoksa stok yetersiz demektir
-- Uygulama katmaninda bu kontrol yapilir
INSERT INTO siparisler (musteri_id, toplam_tutar, durum, tarih)
VALUES (1001, 4500.00, 'beklemede', NOW());
-- Son eklenen siparis ID'sini al
SET @siparis_id = LAST_INSERT_ID();
INSERT INTO siparis_detay (siparis_id, urun_id, adet, birim_fiyat)
VALUES (@siparis_id, 42, 3, 1500.00);
INSERT INTO odeme_kayitlari (siparis_id, tutar, odeme_yontemi, tarih)
VALUES (@siparis_id, 4500.00, 'kredi_karti', NOW());
COMMIT;
Eğer bu işlemler sırasında bir hata oluşursa, aşağıdaki gibi ROLLBACK kullanılır.
START TRANSACTION;
UPDATE urunler SET stok_adet = stok_adet - 3 WHERE urun_id = 42;
INSERT INTO siparisler (musteri_id, toplam_tutar) VALUES (1001, 4500.00);
-- Bir hata olustu, her seyi geri al
ROLLBACK;
-- Artik ne stok degisti ne de siparis olusturuldu
SAVEPOINT Kullanımı
SAVEPOINT, transaction içinde belirli noktalara işaret koymanıza olanak tanır. Böylece tüm transaction’ı değil, sadece belirli bir noktaya kadar olan işlemleri geri alabilirsiniz.
- SAVEPOINT isim: Bir kayıt noktası oluşturur.
- ROLLBACK TO SAVEPOINT isim: Belirtilen kayıt noktasına kadar geri alır.
- RELEASE SAVEPOINT isim: Kayıt noktasını siler.
START TRANSACTION;
INSERT INTO kategoriler (ad) VALUES ('Elektronik');
SAVEPOINT kategori_eklendi;
INSERT INTO urunler (ad, kategori_id, fiyat) VALUES ('Tablet', LAST_INSERT_ID(), 8000);
SAVEPOINT urun_eklendi;
INSERT INTO urun_ozellikleri (urun_id, ozellik, deger)
VALUES (LAST_INSERT_ID(), 'RAM', '8GB');
-- Ozellik ekleme basarisiz oldu, sadece bu kismi geri al
ROLLBACK TO SAVEPOINT urun_eklendi;
-- Kategori ve urun hala transaction icinde mevcut
-- Sadece ozellikleri eklemekten vazgectik
COMMIT; -- Kategori ve urun kalici olarak kaydedildi
SAVEPOINT özellikle uzun ve karmaşık işlemlerde çok işe yarar. Sysadmin olarak büyük veri göçlerinde bu yapıyı sıkça kullanırsınız.
Gerçek Dünya Senaryosu 2: Toplu Kullanıcı Silme İşlemi
Üretim ortamında toplu silme işlemi yapmadan önce mutlaka transaction kullanın. Önce test edin, sonra commit edin.
-- Once kac kayit etkilenecegini gor
SELECT COUNT(*) FROM kullanicilar
WHERE son_giris < '2022-01-01' AND aktif = 0;
-- Transaction baslat
START TRANSACTION;
-- Iliskili kayitlari sil
DELETE FROM kullanici_oturumlari
WHERE kullanici_id IN (
SELECT kullanici_id FROM kullanicilar
WHERE son_giris < '2022-01-01' AND aktif = 0
);
DELETE FROM kullanici_tercihleri
WHERE kullanici_id IN (
SELECT kullanici_id FROM kullanicilar
WHERE son_giris < '2022-01-01' AND aktif = 0
);
DELETE FROM kullanicilar
WHERE son_giris < '2022-01-01' AND aktif = 0;
-- Kac kayit silindi kontrol et
SELECT ROW_COUNT(); -- Son sorgudan etkilenen satir sayisi
-- Her sey dogru gorunuyorsa
COMMIT;
-- Bir seylerde yanlis olduysa
-- ROLLBACK;
Bu yaklaşım, canlı sistemlerde yapılan toplu silme işlemlerinde hayat kurtarır. Commit etmeden önce etkilenen satır sayısını kontrol etmek iyi bir pratiktir.
Transaction İzolasyon Seviyeleri
Transaction’lar çalışırken diğer eş zamanlı bağlantıların bu işlemleri nasıl gördüğünü izolasyon seviyeleri belirler.
- READ UNCOMMITTED: En düşük izolasyon. Commit edilmemiş değişiklikler dahi okunabilir. “Dirty read” problemi oluşabilir.
- READ COMMITTED: Sadece commit edilmiş veriler okunur. Çoğu üretim sisteminde makul bir seçimdir.
- REPEATABLE READ: Aynı transaction içinde aynı sorgu her çalıştırıldığında aynı sonucu döndürür. MariaDB ve MySQL varsayılanıdır.
- SERIALIZABLE: En yüksek izolasyon. Transaction’lar seri olarak çalışır gibi davranır. Performans maliyeti yüksektir.
-- Mevcut izolasyon seviyesini goruntule
SELECT @@transaction_isolation;
-- Oturum bazinda izolasyon seviyesi ayarla
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- Global olarak ayarla (sunucu yeniden baslayana kadar gecerli)
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- Tek bir transaction icin ayarla
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
START TRANSACTION;
-- sorgular...
COMMIT;
Çoğu uygulama için varsayılan olan REPEATABLE READ yeterlidir. Yüksek eş zamanlılık gerektiren sistemlerde READ COMMITTED’ı tercih edebilirsiniz.
Deadlock Yönetimi
İki transaction birbirinin kilitlediği kaynakları beklediğinde deadlock oluşur. MariaDB ve MySQL bu durumu otomatik olarak algılar ve bir transaction’ı otomatik ROLLBACK’e uğratır.
-- Deadlock loglarini goruntule
SHOW ENGINE INNODB STATUSG
-- Deadlock bilgisini filtrele
SHOW ENGINE INNODB STATUSG -- Ciktiyi incele, LATEST DETECTED DEADLOCK bolumune bak
-- Deadlock bekleme zaman asimi ayari (saniye)
SHOW VARIABLES LIKE 'innodb_lock_wait_timeout';
-- Zaman asimini ayarla
SET innodb_lock_wait_timeout = 30;
Deadlock durumunda uygulama katmanında transaction’ı yeniden denemek standart yaklaşımdır. Sysadmin olarak sık karşılaşılan deadlock’ları loglara bakarak analiz edebilirsiniz.
Gerçek Dünya Senaryosu 3: Muhasebe Kapatma İşlemi
Ay sonu muhasebe kapanışı gibi kritik toplu işlemlerde transaction yapısı vazgeçilmezdir.
START TRANSACTION;
-- Ay sonu bakiyelerini hesap ozeti tablosuna yaz
INSERT INTO hesap_ozeti (hesap_id, ay, yil, kapanıs_bakiyesi, olusturma_tarihi)
SELECT
h.hesap_id,
MONTH(NOW()),
YEAR(NOW()),
h.bakiye,
NOW()
FROM hesaplar h
WHERE h.aktif = 1;
-- Islem sayisini kontrol et
SET @eklenen_kayit = ROW_COUNT();
-- Beklenen hesap sayisi ile karsilastir
SET @beklenen_hesap = (SELECT COUNT(*) FROM hesaplar WHERE aktif = 1);
-- Eger sayilar eslesiyorsa devam et
-- Bu kontrolu uygulama katmaninda yapmaniz onerilir
-- Kapanmis donemi guncelle
UPDATE donem_kontrol
SET durum = 'kapali',
kapanis_tarihi = NOW(),
kapanis_yapan = CURRENT_USER()
WHERE ay = MONTH(NOW()) AND yil = YEAR(NOW()) AND durum = 'acik';
SAVEPOINT ozet_tamamlandi;
-- Bir sonraki donem acilisini olustur
INSERT INTO donem_kontrol (ay, yil, durum, acilis_tarihi)
VALUES (
MONTH(DATE_ADD(NOW(), INTERVAL 1 MONTH)),
YEAR(DATE_ADD(NOW(), INTERVAL 1 MONTH)),
'acik',
NOW()
);
COMMIT;
Stored Procedure İçinde Transaction Kullanımı
Gerçek dünya uygulamalarında transaction’lar çoğunlukla stored procedure içinde kullanılır. Bu sayede uygulama katmanından bağımsız, tekrar kullanılabilir iş mantığı oluşturulur.
DELIMITER //
CREATE PROCEDURE para_transferi(
IN kaynak_hesap INT,
IN hedef_hesap INT,
IN transfer_tutari DECIMAL(15,2)
)
BEGIN
DECLARE hata_olustu INT DEFAULT 0;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET hata_olustu = 1;
START TRANSACTION;
UPDATE hesaplar
SET bakiye = bakiye - transfer_tutari
WHERE hesap_id = kaynak_hesap AND bakiye >= transfer_tutari;
IF ROW_COUNT() = 0 THEN
SET hata_olustu = 1;
END IF;
IF hata_olustu = 0 THEN
UPDATE hesaplar
SET bakiye = bakiye + transfer_tutari
WHERE hesap_id = hedef_hesap;
IF ROW_COUNT() = 0 THEN
SET hata_olustu = 1;
END IF;
END IF;
IF hata_olustu = 0 THEN
INSERT INTO transfer_log (kaynak, hedef, tutar, tarih)
VALUES (kaynak_hesap, hedef_hesap, transfer_tutari, NOW());
COMMIT;
SELECT 'Transfer basarili' AS sonuc;
ELSE
ROLLBACK;
SELECT 'Transfer basarisiz, islem geri alindi' AS sonuc;
END IF;
END //
DELIMITER ;
-- Proseduru cagir
CALL para_transferi(1001, 1002, 500.00);
Bu stored procedure örneği, hata yönetimini de kapsayan tam bir transaction yapısı sunar. CONTINUE HANDLER, SQLEXCEPTION durumunda procedure’un devam etmesini ve sonunda karar vermesini sağlar.
Transaction Performans Notları
Transaction kullanımında dikkat edilmesi gereken bazı performans noktaları vardır.
- Uzun transaction’lardan kaçının: Ne kadar uzun sürerse o kadar çok kilit tutulur ve diğer işlemler bekler.
- Transaction içinde kullanıcı etkileşimi beklemeyin: Web uygulamalarında “kullanıcı onayı bekle” gibi durumlar transaction içinde olmamalıdır.
- Büyük toplu işlemleri parçalara bölün: Milyonlarca kaydı tek transaction’da güncellemek yerine binlik gruplar halinde işleyin.
- FOR UPDATE ile satır kilitleme: Sadece gerçekten güncelleyeceğiniz satırları kilitleyin.
- Index kullanımına dikkat edin: Kilitleme, index üzerinden yapılan sorgularda satır bazında olur. Index yoksa tablo kilidi oluşabilir.
-- Buyuk guncellemeyi parcalara bolme ornegi
SET @toplam = (SELECT COUNT(*) FROM urunler WHERE fiyat_guncellendi = 0);
SET @parca_boyutu = 1000;
SET @offset = 0;
-- Bu donguyu uygulama katmanindan calistirin
WHILE @offset < @toplam DO
START TRANSACTION;
UPDATE urunler
SET fiyat = fiyat * 1.10, fiyat_guncellendi = 1
WHERE fiyat_guncellendi = 0
LIMIT 1000;
COMMIT;
SET @offset = @offset + @parca_boyutu;
END WHILE;
Yaygın Hatalar ve Çözümleri
Deneyimli sysadmin’lerin bile zaman zaman düştüğü tuzaklar vardır.
- COMMIT unutmak: Transaction başlatıp commit etmeden oturumu kapatmak otomatik ROLLBACK’e yol açar. Uzun süreli oturumlarda bu risk artar.
- DDL komutları transaction’ı otomatik commit eder: CREATE TABLE, ALTER TABLE, DROP TABLE gibi DDL komutları çalıştırıldığında otomatik implicit commit gerçekleşir. Bu komutları transaction içine almayın.
- LOCK TABLES transaction’ı sonlandırır: LOCK TABLES kullanmadan önce mevcut transaction commit edilir.
- Nested transaction yoktur: MariaDB ve MySQL iç içe transaction’ı desteklemez. START TRANSACTION, açık bir transaction’ı commit eder ve yenisini başlatır.
-- DDL implicit commit ornegi - DIKKAT
START TRANSACTION;
INSERT INTO log_tablosu VALUES ('test');
-- Asagidaki satir otomatik commit tetikler!
CREATE TABLE gecici_tablo (id INT);
-- Artik INSERT geri alinamaz!
ROLLBACK; -- Bu hicbir sey yapmaz, commit zaten gerceklesti
Sonuç
Transaction yönetimi, MariaDB ve MySQL ile çalışan her sysadmin ve veritabanı yöneticisinin derinlemesine bilmesi gereken temel bir konudur. START TRANSACTION, COMMIT ve ROLLBACK üçlüsünü doğru kullanmak, üretim ortamlarında veri bütünlüğünü ve tutarlılığını garantiler.
Özellikle şu durumlarda transaction kullanmayı alışkanlık haline getirin: birden fazla tabloyu etkileyen toplu güncellemeler, finansal ve stok işlemleri, ay sonu veya yıl sonu kapanış operasyonları ve üretim ortamında yapılan bakım işlemleri. SAVEPOINT ile karmaşık işlemleri kısmi geri alma imkanına kavuşursunuz, stored procedure içindeki hata yönetimi ile ise bu yapıyı uygulamadan bağımsız hale getirirsiniz.
Son olarak şunu söyleyeyim: Üretim ortamında kritik bir sorguyu çalıştırmadan önce “Bu işlemi geri alabilir miyim?” sorusunu kendinize sorun. Cevabınız “hayır” ise, mutlaka transaction içinde çalışın. Bu alışkanlık, bir gün büyük bir felaketten sizi kurtaracaktır.
