Makale Özeti

Bir transactionın bir kaynağı kullanımına alması ve diğer transactionın birinci transactionın işini bitirmesini beklemesi işlemine kilitleme (locking) diyoruz. DeadLock ise iki yada daha fazla transaction'ın rol aldığı, karşılıklı olarak birbirlerinin kilitlediği kaynakları beklemesi durumudur.

Makale

SQL Server 2000 DeadLock Yönetimi

Kilitleme

SQL Server veritabanı bütünlüğü ve transactional bütünlük için kilitleme yapar. Kilitleme ile veri üzerinde değişiklik yaparken diğer kullanıcıların veriyi okumasını ve aynı anda farklı kullanıcıların veriyi değiştirmesini önler. Kilitleme işlemi SQL Server tarafından otomatik sağlansa da farklı yöntemler ile (hint,isolation level) kilitleme işlemi üzerinde farklı kontroller yapılabilir.

LOCK_TIMEOUT

Bir transaction ile bir kaynak kilitlendiğinde o kaynağı kullanmak isteyen başka bir transaction SQL Server tarafından bekletilir. İstemci uygulamanın yapılandırmasına göre bir süre sonra hata verip timeout olabilir. Ancak SQL Server ikinci transactionı sürekli bekletir, ta ki kaynağı kilitlemiş olan transaction kaynağı serbest bırakana kadar.

Ancak eğer istenirse ise connection seviyesinde kilitleme için zaman aşımı süresi belirlenebilir.

SET LOCK_TIMEOUT 2000

Bu komutun çalıştırıldığı connection içerisinde başka bir transaction tarafından kilitlenmiş kaynakların serbest bırakılması için 2000 milisaniye beklenecektir. 

Eğer belirtilen süre dolmasına rağmen kaynak halen serbest bırakılmadıyda SQL Server kaynağın serbest kalmasını bekleyen transactiona 1222 nolu hata mesajı ile geriye döner.

Yazdığınız kod içerisinde LOCK_TIMEOUT değerini değiştirdiyseniz, 1222 nolu hata mesajını beklemeniz ve bu durumda yapacağınız işlemi (çalıştırılamayan sorgunun içinde yer aldığı tüm transactionı geri almak gibi) gerçekleştirmeniz veri bütünlüğünüz için uygun olacaktır.

1222 nolu hatanın oluşup oluşmadığını anlamak için hata vermesi muhtemel komut satırlarının hemen altındaki satırda @@ERROR değerini kontrol edebilirsiniz. Bu değişken bir önceki satırda oluşan hatanın kodunu geriye döner.

IF @@ERROR=1222
BEGIN
  'yapılmasını istediğiniz işlem (rollback gibi)
END

@@LOCK_TIMEOUT değişkeni ile connection seviyesindeki kilitleme zaman aşımı süresini öğrenebilirsiniz.

SELECT @@LOCK_TIMEOUT

LOCK_TIMEOUT değeri varsayılan olarak -1 değerine sahiptir ve bu değer SQL Server'ın ikinci transactiona hata vermeden diğer transactionın kaynağı serbest bırakmasını beklemesini sağlar.

DeadLock

Bir transactionın bir kaynağı kullanımına alması ve diğer transactionın birinci transactionın işini bitirmesini beklemesi işlemine kilitleme (locking) diyoruz.

DeadLock ise iki yada daha fazla transaction'ın rol aldığı, karşılıklı olarak birbirlerinin kilitlediği kaynakları beklemesi durumudur.

Daha basit olarak özetlersek; ilk transaction ikinci transactionın elindeki bir kaynağın serbest kalmasını beklerken, ikinci transactionın birinci transactionın elindeki bir kaynağın serbest kalmasını beklemesi durumudur. Çıkmaz sokak!!

Bu durum çok sık karşılaşılabilecek bir durum olmasa da veritabanın işlevlerine göre karşılaşılması söz konusu olabilir.

 

Resim Kaynağı: SQL Server 2000 Books Online

Örnek Senaryo:

SQL Server Query Analyzer aracında iki sorgu penceresi açın.

Aşağıdaki kodları yazın;

BEGIN TRAN
UPDATE Categories
SET CategoryName=CategoryName
WHERE CategoryID=1
---
UPDATE Products
SET ProductName=ProductName
WHERE ProductID=1
---
COMMIT TRAN
 
BEGIN TRAN
UPDATE Products
SET ProductName=ProductName
WHERE ProductID=1
---
UPDATE Categories
SET CategoryName=CategoryName
WHERE CategoryID=1
---
COMMIT TRAN


Aşağıdaki şekilde gözüktüğü şekilde Query Analyzer üzerinde sorguları kare içine alınmış bölümleri seçerek çalıştırın.

Yukarıdaki şekilde sorguları adım adım çalıştırdığınızda;

  • 3.adımda birinci transaction, ikinci transactionın Products tablosunu serbest bırakmasını beklemeye başlar.

  • 4.adımda ise ikinci transaction, birinci transactionın Categories tablosunu serbest bırakmasını beklemeye başlar.

Bu durumda iki transactionda birbirini beklemeye başlar ve iş içinden çıkılmaz bir hal alır.

SQL Server bu işin içinden çıkmak için transactionlardan birisini kurban (victim) seçer ve o transaction içerisinde yapılan tüm işlemleri geri alır ve transactionı iptal eder.

SQL Server transactionlardan birisini kurban seçip transactionı geri alırken hata mesajı olarak 1205 nolu hata mesajını türetir.

Bu hata mesajını uygulamanız içerisinde yakalayarak transaction içerisindeki işlemleri tekrar çalıştırmayı deneyebilirsiniz.

Ayrıca benim yukarıda yazdığım kodlarda DeadLock oluşmasını sağlama amacı vardır. Böyle bir senaryoda iki transactiondaki tablo erişimlerini de aynı sırada (önce Categories, sonra Products, veya tam tersi gibi) yapmak uygun bir çözüm olabilir.

SQL Server hangi Transactionı kurban olarak seçer?

Aksini belirtmediğimiz takdirde hangi transactionın kurban seçileceği SQL Server'ın insafına kalmıştır. SQL Server rollback için daha az işlem yapacağını hesapladığı transactionı seçer.

SET DEADLOCK_PRIORITY

Bu komut ile deadlock durumunda hangi transactionın kurban seçileceğini belirleyebilirsiniz.

SET DEADLOCK_PRIORITY LOW

Senaryonuz içerisinde çalışacak transactionlardan bir tanesinin daha az önem arzettiğini düşünerek istediğiniz transactionın kurban olarak seçilebileceğini SQL Server'a bildirmek için kullanılır. Varsayılan değer NORMAL'dir.

Uygulamalarınızda sık sık bu hata ile karşılaşıyorsanız SQL Profiler ile deadlock durumlarını izleyip, hangi kodlarınızda deadlock oluşmasına müsait bir tasarım olduğunu tespit edebilirsiniz.

Bir sonraki makalemizde bir deadlock senaryosunun ADO.NET ile gerçekleştirilmesini inceleyeceğiz.

Cengiz HAN
Microsoft ASP.NET MVP
cengiz@cengizhan.com

Script Dosyaları