Deadlock, ölümcül kilit. Her sistem yöneticisinin veya Sql Adminin başına gelen bir olaydır deadlock. Deadlock hatasını alan bir kullanıcı bu hatanın düzeltilemez olduğunu düşünür ve programı kapatır açar. Aslında düzelen hiçbir şey olmamıştır. Sql üzerindeki transactionların (proseslerin) kilitlenmesine kısaca deadlock diyebiliriz.
Bir örnek vermek gerekirse; veritabanlarımızda bir güncelleme (update) işlemi yaptığımızda, güncelleme bitene kadar sql server ilgili tabloyu kilitler. Aynı sırada farklı bir transaction (işlem) tabloda bir güncelleme yapmak istediğinde bekletmeye alınır, timeout (zaman aşımı) süresi kadar kilidin açılmasını bekler. Bu süre içerisinde ilk transaction’ın bitmesini veya roll back (geri alınmasını) bekler. Bu iki transaction birbirini beklemeye başladığı sırada deadlock dediğimiz olay meydana gelir. Özetlersek, iki farklı kaynak birbirine erişmek istiyor fakat ikisi de birbirlerinin üzerindeki işlerin bitmesini bekliyor.
Deadlock’ın kesin ve net bir çözümü yok. Şunu şunu yaparsan çözümü budur diyemeyiz. Deadlock oluşmasını önleyici bir takım tedbirler ve analizler yapılabilir. Birçok deadlock’ı veritabanı dizaynı ve indeksleme sayesinde giderebiliriz. Ayrıca hata yakalama yöntemleri ile de deadlocklar önlenebilir. Bu makalemizde örnekler ile deadlock izleme ve tespit etmek için neler yapabiliriz bunları göreceğiz.
Bu makale 4 bölümden oluşmaktadır:
1- Deadlock Tespit Edilmesi
2- Deadlock İle İlgili Bigl Toplama Süreci
3- Deadlock Analizi
4- Deadlock Tipleri
Makalemizde kullanacağımız terimlere göz atalım;
Transaction – Veritabanında çalışan bir birim iş.
Kilit (lock) – Birden fazla eşzamanlı çalışan transactionları, senkronizasyon mekanizması kullanarak birbirleri arasında koruma sağlaması.
Lock mode – Transactionların kilitlendiğindeki erişim seviyesi.
Kilitlenme (blocking) – Bir transaction işlemi çalışmaya başladığında kilit talebinde bulunuyor ise, çakışan diğer diğer transactionların beklemesi ve ilk transaction bitene kadar diğerlerine izin vermemesi.
Deadlock – İki transaction birbirlerini blokladığında, her biri kaynakları üzerindeki kilidi açmaya çalışır, bu sırada transactionlar çatışma moduna düşer (conflicting mode) deadlock meydana gelir.
Bu makaleyi okumadan önce Transaction Locking ile ilgili Technet’teki makaleyi incelemenizde fayda var; http://technet.microsoft.com/en-us/library/jj856598.aspx
Deadlock Hakkındaki Yanılgılar – Yanlış Bilinenler
- Deadlocklar Sql Server’ın açığıdır.
Sql 2005 ile ortaya atılan bir söylemdir, tam anlamıyla doğru değildir. Kodlama sonuçları yüzünden oluşan bir durumdur.
- Deadlocklar önlenemez.
Evet yüzde 100 önlenemez ama önlenemez diye de çözüm arayışları bırakılmamalıdır. Deadlockları önlemek için yöntemler mevcuttur. Veritabanının tasarımını değiştirmek, T-Sql kod değişiklikleri, transactionları farklı bir sırayla veya ayrı bir şekilde çalıştırmak, deadlock önlemede en önemli faktörlerdendir.
- Select ifadesinde NOLOCK kullanımı deadlockların oluşmasını engellemede en iyi yoldur.
En sık karşılaşılan yanılgılardan biri de budur. NOLOCK kullanmak o anda işinizi görür, sorgunuzu çalıştırır, kilitlenmede olmaz. Ama değişiklikler sorgu sonucuna yansımaz, o sırada ilgili tablolarda, bir update, delete veya insert işlemi yapılıyorsa bunlar sonuçları sizin sonuçlarınıza yansımaz. Uygunsuz bir çözümsüzdür. Sadece anlık sorgularda sürekli alışkanlık haline getirenler vardır, anlık sorgu NOLOCK’ın kullanılması çokta sıkıntılı bir durum değildir.
- Her sorguyu indekslemek deadlock oluşmasını engeller.
NOLOCK ile aynı kapsamdadır. Deadlock oluşmasını büyük ölçüde azaltır fakat gereksiz yere indeksleme yapmak veritabanı boyutunu ciddi derecede arttırır. İndeksleme sorgu performansı için yapılır ama gereksiz indeksleme de performans kaybına yol açar. Bilinmeden yapılan indekslemeler kesinlikle tavsiye edilmez.
Kilitlenme birimleri
Kilitlenme sırasında karşımıza çıkacak olan temel ana birimler şunlardır;
- RID: Ram’in heap kısmında duran, satır üzerindeki kimlik no’sudur
- KEY: Satırın indeks anahtarıdır.
- PAGE: Veritabanının kilitledği herbir sayfaya page denir.
- HOBT: Heap veya indeks bölümünün kilitlenmesi.
- TABLE: Tablonun kilitlenmesi.
- METADATA: Tablo şema açıklamasının kilitlenmesi.
Birden çok birim aynı anda kilitlenmeye maruz kalabilir. Bunun amacı da, en düşük kaynak seviyesinde, tam bir koruma sağlanmak istenmesidir. Kilitlenmeler tepeden aşağıda doğru hiyerarşik şekilde olur, önce tablolar sonra satırlar.
Delete ifadesinde, tablo içinde önce 1 satır silme ile başlar. Bu sırada tablo için kilitlenme isteği oluşturulur, ardından her satır için değiştirilmeye karşı bu kilitlenme geçerli olur.
Update ifadesinde, hiçbir kayıtta güncelleme yapılmasına izin vermez. Select ile sorgular yapmaya izin verir.
1. Deadlock Tespit Edilmesi
Sql server periyodik olarak deadlock tespiti için arama işlemi başlatır. Varsayılan olarak bu periyodik monitörleme işlemi 5 saniye aralıkla yapılmaktadır. Sql server’da deadlock tespit edildiğinde bu zaman 100ms’ye kadar düşer ve deadlock lar tamamen bitene kadar 100ms periyodu ile taramaya devam eder, deadlock’lar tamamen bittikten sonra arama periyodu 5 saniyeye tekrardan çıkar.
Deadlock araması sırasında, bloke olan işlemler tespit edilir, hangi kaynağın bloke koyduğu bulunur ve bu işlem yinelemeli olarak devam eder.
Deadlock tespit edildiğinde, deadlock gören işlemlerden birisi sonlandırılır ve işlem geri alınır (roll back transaction). Kullanıcı 1205 no’lu hatayı alır. http://technet.microsoft.com/en-us/library/aa258770(v=SQL.80).aspx Roll back yapılan işlemde ilk önce Deadlock_Priority (deadlock önceliği) seviyesi düşük mü yok sa yüksek mi diye bakılır. Buna bakılması için, çalışan işlemin içinde deadlock_priority nin tanımlı olması gerekmektedir. Eğer priority ayarları default olarak bırakılmış ise (genelde bu ayar yapılmaz) hangi işlemin (transaction) roll back yapması daha hızlı ve daha kolay olacaksa, o işlem deadlock victim (deadlock kurbanı) seçilir ve roll back olur.
2. Deadlock ile İlgili Bilgi Toplama Süreci
Deadlock ile ilgili bilgileri toplarken Trace Flag’leri kullanacağız. Trace flaglar ilk önce Sql Server 2005’te kullanılmaya başlandı. Dbcc Traceon komutu ile kullanılırlar.
Dcc Traceon: http://technet.microsoft.com/en-us/library/ms187329.aspx
Trace Flaglar: http://technet.microsoft.com/en-us/library/ms188396.aspx
Dbcc Traceon komutu -1 parametresi kullanıldığında tüm oturumları kapsayacak şekilde çalışır.
Deadlocklar yakalamamız için trace flaglardan 1204 ve 1222’yi takip etmemiz gerekecektir. Aynı zamanda hatalog larına da bu kayıtların gelmesi için gerekli ayarları yapmamız gerekecektir. Aşağıda bununla ilgili örnekleri yapacağız.
Trace flag 1222’yi nasıl aktif hale getireceğimize bakalım;
Sql Server Management Studio’yu açıyoruz.
New Query diyerek, yeni bir sorgu ekranı açıyoruz.
1222 Trace flag için aşağıdaki Dbcc kodunu yazıp Execute diyoruz,
1222 flag’imiz etkin oldu, şimdi durumuna bakalım,
Status 1 oldu, ama sadece bu oturum (Session) için geçerli.
Trace flag’imizi iptal etmek içinde aşağıdaki kodu yazıyoruz.
Trace flag’in genel olarak (Global) açılması için -1 parametresini kullanıyoruz,
Status kontrolü yaptığımzda global seçeneği 1 oldu,
Trace flag için yaptığımız bu ayarlar geçici olmuş oldu. Kalıcı olarak sürekli trace flagleri izlemek istiyor isek, Sql server’ın açılışına –T parametresini girerek, sürekli açık kalmasını sağlayabiliriz.
Bunun için Sql Server Configuration Manager’ı açıyoruz.
Sql Server Services – Sql Server (instance adı) sağ tıklayıp “Properties” bölümünü açıyoruz.
“Startup Parameters” sekmesine gelip, -T1222 parametresini yazıp Add ile ekleme işlememizi tamamlıyoruz.
Apply dedikten sonra Sql server hizmetinin yeniden başlatılması gerektiğini belirten bir uyarı çıkıyor.
Sql Profiler ile İzleme Yapılması
Sql Server Profiler, xml formatında bize deadlock’ların bir grafiğini sunabiliyor. Bilgi toplama sürecinde, Deadlock’lara sebep olan sorunları bu grafiksel arayüz sayesinde daha rahat bakabiliyoruz. Xml dosyalarından deadlockları okumak ve analiz etmek daha hızlı olmakla beraber, zaman kazandıran bir özellik gibi de düşünebiliriz.
Sql Server Profiler’ı, Management Studio’da Tools bölümünden açıyoruz.
Şablon olarak boş seçiyoruz.
Events selection kısmında da “Locks” kısmından deadlock graph ı işaretliyoruz.
Deadlock Graph’ı seçtikten sonra, yan tarafta “Events Extraction Settings” yani olayımızla ilgili sonuçları kaydedebileceğimiz seçenekler çıktı.
Bu kısımda Deadlock XML’i işaretliyoruz. Deadlock ile ilgili tüm olayları xml formatında masaüstüme kaydedecek şekilde ayarladım.
Deadlock izlememiz başlamış durumda, deadlocklar oluştuğu zaman aşağıdaki ekrana gelmeye başlayacak.
Olay Bildirimleri – Event Notifications
Trace olaylarını yakalayıp bunları olay olduğunda otomatik olarak bildimelerini sağlayan yapıya Event Notifications yani Olay Bildirimleri denmektedir. Bir bildirim ve bilgi mesajı içeriğiden olabilirler. Windows event logtaki warning ve info mesajları gibi.
Olay bildirimlerini alabilmek için, olay mesajlarını bir kuyrukta yakalamak gerekmektedir. Bu yakalanan verilerin nereye yönlendireceğini ve bunu bir servis gibi çalıştırmaya şimdi bakacağız.
Deadlock durumlarını yakalamak için msdb veritananında önce bir Queue yani kuyruk oluştuyoruz.
Yeni bir servis Broker Service oluşturuyoruz. Detaylar için: http://technet.microsoft.com/en-us/library/ms190332.aspx
Servisten sonra Olay Bildirimi için Event Notification’ı oluşturmamız gerekiyor. Detaylar için: http://technet.microsoft.com/en-us/library/ms189453.aspx
Bakalım bildirim ayarlarımız olmuş mu, aşağıdaki sorguyu çalıştırıyoruz.
Servis adımızda atanmış durumda. İzleme servisimiz başarıyla oluşmuş gözüküyor.
Deadlock bilgilerini toplamak bizlere deadlockların sebepleri hakkında çok önemli bilgiler vermektedir. Bu yüzden bilgi toplama aşaması önemlidir. Görüldüğü gibi, Sql Server varsayılan olarak bize deadlockların bilgilerini sunmuyor. Şu aşamaya kadar bilgi toplama nasıl yapılır bunları gördük, şimdi bu bilgileri nasıl analiz edeceğimize bakacağız.
3. Deadlock Analizi
Deadlock analizinde, ilk bakılacak nokta, deadlock olan proseslerin hangisinin roll back olduğudur. Bunu tespit edebilmek için, proseslere – id, bekleme zamanı, öncelik v.b. ve kaynağına yani neden olan sebepleri inceleyeceğiz.
Hata logları Sql üzerinde xp_readerrorlog üzerinde tutulmaktadır, buraya 1222 nolu trace flag’imiz ile ilgili hatalarda buraya da gelecektir.
İlk olarak xml formatında deadlock isminde tanım yapıyoruz, ardından da her defasında 5 mesaj yakalayacak şekilde kodumuzu yazıyoruz.
Şu anda bir deadlock olmadığı için NULL geldi, deadlock lar olduğunda burada xml formatının linkini verecek ve bu link üzerinden hangi sorgularda deadlock olduğunu görebileceğiz.
4. Deadlock Tipleri
4.1 Update Durumundaki Kilitlenmeler
Aynı anda çalışmaya başlayan Update komutlu iki prosesimiz olsun. Proses A Tablo A’ya, Proses B de Tablo B’ye Update komutu ile işleme başlasın. Bu durumda Tabla A ve Tablo B’yi bu iki proses kitleyecektir. Proses A Tablo B’de işlem yapamaz, Proses B’de Tablo A’da işlem yapamaz.
Proses A ve B, her iki tabloyada Update yapmak istediğinde, Tablo A’nın daha büyük tablo olduğunu varsayarsak, bu esnada Proses B deadlock victim olacak ve roll back transaction yapılarak, yaptığı işlemler geri alınacaktır. Proses A hem Tablo A ya hem de Tablo B üzerinde update işlemini gerçekleştirecektir.
Sql üzerinde bir örnek ile anlatmaya çalışalım;
Deadlock isminde bir veritabanı oluşturuyoruz,
Olşturduğumuz veritabanımızda, TabloA isminde bir tablo ekleyelim.
TabloA’nın içine Insert into ile rastgele veriler kaydedelim,
Kolon 1 de Clustered Index’de oluşturalım,
Bakalım verilerimiz gelmiş mi, görüldüğü gibi rastgele veriler tabloma kayıt olmuş gözüküyor.
Daha sonra deadlock ta kullanmak için 2 satırı siliyorum,
Bir tane stored prosedür olan bir tablo daha oluşturalım, tablonun adına TabloB diyorum, tabloma ilk tablomda olduğu gibi rastgele veriler ekliyorum. Bu sefer non clustered indeksini c2’de oluşturuyorum.
Tablo A için basit bir Select sorgusu içeren Stored Procedure hazırlıyorum,
Bir de TabloA için basit bir Update Stored Procedure hazırlıyorum,
Veritabanlarımızı, tablolarımızı ve stored prosedürlerimizi hazırladık. Şimdi deadlock örneğimiz ile devam edelim.
Tablo A’da c2 kolonunda güncelleme işlemi yapıyorum. Begin trasaction diyerek, işlemi başlatıyor ve açık tutuyorum.
Şimdi de Tablo B ve Tablo A da update işlemini başlatıyorum,
Evet sürekli Executing query dönüp durmaya başladı, diğer transaction tarafından prosesimiz bloklandı.
Tablo B de c2 sutünunda güncelleme işlemi yapalım,
İkinci kere güncelleme yapmama izin vermedi ve 2. Prosesimizde deadlock hatamızı verdi, bakalım ilk proseste ne olmuş, ilk proseste Executing query sürekli çalışır halde bloklanmış durumdaydı.
Görüldüğü gibi, 2. Prosesi deadlock victim (kurban) yaparak ilk prosesimizin tamamlanmasını sağladı. Eğer bunu yapmamış olsaydı, bu işlemler biz durdurana kadar sürekli bekleyecek ve hiçbir iş yapmayacaklardı.
4.2 Insert Durumundaki Kilitlenmeler
Arda arda seri bir şekilde kayıt olan verilerde, veri kontrolü esnasında, iki veri arasında olmayan veri sorgulanmaya başlandığında, eş zamanlı olarak bir select sorgusu çalışıtı. Eksik olan verileri insert komutu ile veritabanına kayıt etmek isterken, 2 sorgudan biri deadlock victim olur ve rollback ile yaptığı işlemler geri alınır. Sql kodlarımızda bu tarz kontroller koyabiliriz, eksik olan bilgi eğer yok ise insert ile girişini yap gibi örnekler karşımıza çıkmaktadır.
İki select sorgusu ve eksik olan veriler insert edilmeye başlandığında Deadlock oluşuyor
Bu sırada ikinci proses deadlock victim olur, transaction geri alınır ve 304 nolu kayıt insert edilir.
4.3 Silme Durumundaki Kilitlenmeler
Sql Server üzerindeki bölünmüş tablolar yani partitioned tables, üzerinde oluşan deadlocklardır. Örnek olarak bölünmüş bir tabloda silme işlemini ele alalım. Partition table’ımız iki kısım olsun ve ilk kısımdan bir satır silmek istediğimizde sadece partition level kısmı kadar kilitler, yani tüm partition table’a ait tabloları kilitlemez. Ama bu silme işlemi, eş zamanlı olarak, hem birinci hem de ikinci bölümdeki alandan veri silmek istediğinde deadlock gerçekleşmiş olur.
Partition edilmiş bir tablodan eş zamanlı olarak veri silme işlemini aşağıdaki şeklimizde gösterelim,
Bu makalede sizlere, deadlock nasıl tespit edilir ve nasıl izlenmesi gerektiği hakkında bilgiler aktarmaya çalıştım. Deadlocklar her zaman olacaktır, tam anlamıyla engellenecek bir durum değildir, aynı tablo üzerinde birden fazla kişinin çalışması önlenemez bir durumdur. Deadlockları azaltmak ve/veya önlemek için T-Sql kodunda kontroller koyabiliriz, veritabanımızın mimarisine göre özelleştirilmiş önlemler alınmalıdır.