---
title: "Veritabanı Kilitlenmesi (Deadlock) Neden Olur, Nasıl Önlenir?"
url: "https://cilginyazilim.com/blog/veritabani-kilitlenme-deadlock-onleme"
description: "Veritabanı kilitlenmesi neden olur? Tutarsız kilit sırası, uzun transaction ve eksik indeks etkisi; InnoDB deadlock günlüğünü okuma ve pratik önleme yöntemleri."
published: "2026-09-13T10:00:00+03:00"
modified: "2026-09-13T10:00:03+03:00"
author: "superadmin"
category: "Veritabanı ve Performans"
tags: ["php", "mysql", "performans", "sql", "hata ayıklama", "transaction", "deadlock", "eşzamanlılık", "innodb", "veritabanı kilitlenme", "kilit", "veritabanı yönetimi", "üretim sorunları"]
site: "CılgınYazılım"
language: "tr"
---

# Veritabanı Kilitlenmesi (Deadlock) Neden Olur, Nasıl Önlenir?

Deadlock rastgele bir talihsizlik değildir; neredeyse her zaman tutarsız kilit sırasının sonucudur. Nedenini ve çözümünü görelim.

**Veritabanı kilitlenme** (deadlock), iki işlemin birbirinin tuttuğu kilidi beklemesi ve hiçbirinin ilerleyememesidir. Üretimde ortaya çıktığında rastgele ve öngörülemez görünür; oysa neredeyse her zaman tek bir sebebi vardır: *satırlara farklı sıralarda dokunmak*.

![Kilitlenmenin dört koşulu](https://cilginyazilim.com/uploads/blog/2026/08/veritabani-kilitlenme-deadlock-onleme-ozet.png)
*Dört şarttan yalnızca birini kırmak yeterlidir — en pratik olanı döngüsel beklemedir.*

## Klasik senaryo

İki eşzamanlı para transferi düşünün. Birinci işlem A hesabını kilitleyip B'yi bekler, ikinci işlem B'yi kilitleyip A'yı bekler. Her ikisi de sonsuza kadar bekleyecektir — veritabanı devreye girip birini kurban seçer ve geri alır.

```sql
-- Oturum 1
START TRANSACTION;
UPDATE hesaplar SET bakiye = bakiye - 100 WHERE id = 1;   -- 1 kilitlendi
UPDATE hesaplar SET bakiye = bakiye + 100 WHERE id = 2;   -- 2 bekleniyor

-- Oturum 2 (aynı anda)
START TRANSACTION;
UPDATE hesaplar SET bakiye = bakiye - 50 WHERE id = 2;    -- 2 kilitlendi
UPDATE hesaplar SET bakiye = bakiye + 50 WHERE id = 1;    -- 1 bekleniyor → DEADLOCK
```

## Çözüm 1: sabit kilit sırası

Kilitlenmenin dört şartından en kolay kırılanı döngüsel beklemedir. Tüm kod yollarında satırlara **aynı sırayla** dokunursanız döngü oluşamaz. Pratikte bu, birincil anahtara göre sıralamak demektir.

```php
public function transfer(int $kaynakId, int $hedefId, int $kurus): void
{
    // Kilitleri HER ZAMAN artan kimlik sırasında al — döngüsel bekleme imkânsız hâle gelir
    $sirali = [$kaynakId, $hedefId];
    sort($sirali);

    $db = db_connect();
    $db->transException(true)->transStart();

    foreach ($sirali as $id) {
        $db->query('SELECT bakiye FROM hesaplar WHERE id = ? FOR UPDATE', [$id]);
    }

    $db->query('UPDATE hesaplar SET bakiye = bakiye - ? WHERE id = ?', [$kurus, $kaynakId]);
    $db->query('UPDATE hesaplar SET bakiye = bakiye + ? WHERE id = ?', [$kurus, $hedefId]);

    $db->transComplete();
}
```

## Çözüm 2: transaction'ı kısa tutun

Kilit süresi ne kadar uzunsa çakışma olasılığı o kadar yüksektir. En sık görülen hata, transaction içinde yavaş bir işlem yapmaktır: e-posta göndermek, dış API çağırmak, dosya yazmak. Bunların hiçbiri kilitli bölgede olmamalıdır.

> Transaction içinde **ağ çağrısı** yapmayın. Üçüncü parti servis 8 saniye yanıt vermezse, o süre boyunca satır kilitleri tutulur ve tüm sistem yavaşlar.

## Çözüm 3: eksik indeksleri kapatın

Az bilinen ama çok yaygın bir sebep budur. İndekssiz bir `WHERE` koşuluyla güncelleme yaptığınızda InnoDB, taranan tüm satırlara kilit koyabilir. Yani tek satır güncellediğinizi sanırken binlerce satırı kilitlemiş olursunuz. [EXPLAIN çıktısını okumak](https://cilginyazilim.com/blog/indeks-var-sorgu-yavas-explain-nasil-okunur), bu durumu doğrudan görünür kılar.

## Deadlock günlüğünü okumak

Tahmin yürütmek yerine veritabanına sorun. InnoDB son kilitlenmenin tam dökümünü tutar.

```sql
SHOW ENGINE INNODB STATUS\G
-- Çıktıdaki "LATEST DETECTED DEADLOCK" bölümü şunları verir:
--   (1) TRANSACTION: hangi sorgu, hangi kilidi bekliyordu
--   (2) TRANSACTION: karşı taraf hangi kilidi tutuyordu
--   WE ROLL BACK TRANSACTION (n): hangisi kurban seçildi
```

Bu çıktıdaki iki sorguyu yan yana koyduğunuzda kilit sırası farkı genellikle ilk bakışta görünür.

Kilit süresini kısaltmanın bir başka yolu, aynı isteğin tekrar işlenmesini engellemektir; [idempotency anahtarı](https://cilginyazilim.com/blog/idempotency-key-tekrarli-istek-koruma) bu tekrarları en baştan eler.

## Yeniden deneme: kaçınılmaz olanı yönetmek

Sabit sıralamaya rağmen yoğun sistemlerde kilitlenme tamamen sıfırlanmayabilir. Bu yüzden uygulama katmanında sınırlı bir yeniden deneme mantığı bulunmalıdır — kilitlenme geri alınmış bir işlemdir, yani güvenle tekrarlanabilir.

```php
function kilitlenmeyeDayanikli(callable $is, int $maxDeneme = 3)
{
    for ($deneme = 1; ; $deneme++) {
        try {
            return $is();
        } catch (\Throwable $e) {
            // 1213: deadlock, 1205: lock wait timeout
            $kilit = str_contains($e->getMessage(), '1213') || str_contains($e->getMessage(), '1205');

            if (! $kilit || $deneme >= $maxDeneme) {
                throw $e;
            }

            usleep(random_int(20_000, 120_000) * $deneme);  // rastgele geri çekilme
        }
    }
}
```

Geri çekilme süresine rastgelelik eklenmesi kritiktir: sabit bekleme, çakışan iki işlemin aynı anda tekrar denemesine ve aynı çarpışmayı yaşamasına yol açar.

Kod tarafında kilit sırasını tutarlı tutmanın en kolay yolu, biçim ve yapı kurallarını araca bırakmaktır; [EditorConfig ve biçimlendirici](https://cilginyazilim.com/blog/editorconfig-ve-bicimlendirici-kurulumu) bu zemini kurar.

## İzolasyon seviyesiyle ilişkisi

Daha yüksek izolasyon seviyeleri daha çok kilit anlamına gelir; SERIALIZABLE altında kilitlenme olasılığı belirgin biçimde artar. Uygulamanızın gerçekten hangi seviyeye ihtiyacı olduğunu [izolasyon seviyeleri](https://cilginyazilim.com/blog/transaction-izolasyon-seviyeleri-pratikte) yazımızda karşılaştırmıştık. Kilit türlerinin resmî tanımı için [MySQL InnoDB kilitleme belgeleri](https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html) başvurulacak kaynaktır. Göç sırasında oluşan uzun kilitler için [sıfır kesintili göç](https://cilginyazilim.com/blog/veritabani-gocleri-sifir-kesintiyle-nasil-uygulanir) yaklaşımı da doğrudan ilgilidir.

## Sonuç

Kilitlenme bir talih meselesi değil, tasarım sonucudur. Dört önlem çoğu vakayı kapatır: satırlara her zaman aynı sırayla dokunun, transaction'ları kısa tutup içine ağ çağrısı koymayın, güncelleme koşullarınızın indeksli olduğundan emin olun ve kaçınılmaz durumlar için rastgele geri çekilmeli yeniden deneme ekleyin. Üretimde bir kilitlenme gördüğünüzde ilk işiniz InnoDB durum dökümünü almak olsun; iki sorguyu yan yana koyduğunuzda sıralama farkı genellikle kendini gösterir ve düzeltme birkaç satırlık bir değişikliğe iner.

## Sıkça Sorulan Sorular

### Kilitlenme veri kaybına yol açar mı?

Hayır. Veritabanı kurban seçtiği işlemi tamamen geri alır, yani yarım kalmış bir değişiklik kalmaz. Asıl risk, uygulamanın bu hatayı yakalamayıp kullanıcıya belirsiz bir hata göstermesi ya da işlemi sessizce kaybetmesidir.

### Kilitlenme ile lock wait timeout aynı şey mi?

Değil. Kilitlenmede döngüsel bekleme vardır ve veritabanı bunu anında tespit edip birini geri alır. Lock wait timeout ise tek yönlü uzun bir beklemedir; kimse döngüde değildir, sadece kilidi tutan işlem çok uzun sürmektedir. Çözümleri de farklıdır.

### Kilit sırasını kodun her yerinde nasıl garanti ederim?

Kilitleme mantığını tek bir yardımcı fonksiyonda toplamak en güvenilir yoldur. Her yerde elle sıralamaya güvenmek, yeni yazılan bir kod yolunda kolayca bozulur. Kod incelemesinde "birden fazla satır kilitleniyor mu?" sorusunu standart kontrol maddesi yapmak da işe yarar.

### Yeniden deneme sayısı kaç olmalı?

Üç genellikle yeterlidir. Daha fazlası, altta yatan tasarım sorununu gizler ve yoğunluk anında sistemi daha da yorar. Yeniden deneme sayısını ölçün: sürekli üçüncü denemeye kadar gidiliyorsa sorun kilit sırasındadır, yeniden denemede değil.

---

Kaynak: [Veritabanı Kilitlenmesi (Deadlock) Neden Olur, Nasıl Önlenir?](https://cilginyazilim.com/blog/veritabani-kilitlenme-deadlock-onleme)
