Firebird'da deadlock'ları nasıl takip edersiniz
Alexey Kovyazin, 08-May-2019, (c) IBSurgeon
Güncelleme çakışmaları (genellikle «deadlock» olarak adlandırılır), yoğun eşzamanlı veri güncellemeleri gerçekleştiren Firebird uygulamalarında ortaya çıkar.
« Firebird’de İşlemler» makalesinden de görebileceğiniz gibi, «deadlock» anahtar kelimesini içeren 3 tür hata vardır:
deadlock
-update conflicts with concurrent update
-concurrent transaction number is NNN
lock time-out on wait transaction
-deadlock
-update conflicts with concurrent update
-concurrent transaction number is NNN
lock conflict on no wait transaction
-deadlock
-update conflicts with concurrent update
-concurrent transaction number is NNN
Her 3 güncelleme çakışması türünün ortak noktası, güncelleme çakışmasının genellikle aynı kaydı değiştirmeye çalışan 2 değişiklik işleminin sonucu olmasıdır.
Olası seçenekler şunlardır: UPDATE, DELETE SELECT WITH LOCK, MERGE, UPDATE OR INSERT.
Hata mesajını alan «kurban» işlemi takip etmek nispeten kolaydır - sadece try… catch içine alın ve hatada başarısız olan sorguda kullanılan parametreleri günlüğe kaydedin.
Ancak bu yaklaşımla, «kazananı» takip etmek zordur - yani, güncellemenin hatasız başarıyla tamamlandığı eşzamanlı işlemi.
Geliştiricilerin güncelleme çakışmalarını araştırmasına yardımcı olmak için Firebird, hata mesajlarına eşzamanlı işleme referansı koyar - yani, eşzamanlı güncellemenin henüz commit edilmediği işleme. Trace API ile birlikte bu, bize her iki çakışan işlemi de takip etme yeteneği verir.
Bunu nasıl yapacağımıza dair pratik adımları ele alalım.
Öncelikle, izlemeyi yapılandırmamız gerekiyor. Bunun için, aşağıdaki içerikle bir metin dosyası oluşturun (bizim durumumuzda C:\Temp\mytrace.conf):
database
{
enabled = true
include_filter="(UPDATE%)|(DELETE%)|(MERGE%)|(SELECT%FROM%WITH LOCK)|(UPDATE OR INSERT%)"
log_statement_finish = true
log_errors = true
include_gds_codes="deadlock"
log_initfini = false
time_threshold = 0
max_sql_length = 65000
}
Yukarıdaki yapılandırma dosyası tüm veritabanları için izlemeyi etkinleştirir, bu nedenle birden fazla aktif veritabanınız varsa, yapılandırma dosyasında veritabanının adını belirtmek daha iyidir, yani:
Database=mydatabase.fdb
{
enabled = true
…
}
Yapılandırma dosyasındaki en önemli parametreleri inceleyelim:
Include filter parametresi
include_filter="(UPDATE%)|(DELETE%)|(MERGE%)|(SELECT%FROM%WITH LOCK)|(UPDATE OR INSERT%)"
Bu, yalnızca UPDATE ve DELETE ifadelerini izlemek ve select’leri yok saymak istediğimiz anlamına gelir. Mümkün olduğunca çok ifadeyi filtrelemek gerekir, çünkü yüksek yük altındaki üretim sisteminde metin günlüğündeki kayıt sayısı analiz etmek için çok yüksek olabilir.
Genellikle, hangi tablonun güncelleme çakışmasına katıldığını biliriz, analiz edilecek güncelleme sayısını azaltmak için onu filtreye koymak iyi bir fikir olacaktır:
include_filter="(UPDATE T1%)|(DELETE FROM T1%)|(MERGE INTO T1%)|(SELECT% FROM T1%WITH lock%)|(UPDATE OR INSERT INTO T1%)"
Peki ya güncellemeler ve silmeler saklı yordamlar tarafından yapılıyorsa? Bu, tüm süreci daha zor hale getirir: eşzamanlı güncellemelere dahil olabilecek tüm saklı yordamları filtreye ekleyin ve ayrıca yordamların günlüğe kaydedilmesini etkinleştirin:
include_filter="(UPDATE T1%)|(DELETE FROM T1%)|(MERGE INTO T1%)|(SELECT% FROM T1%with lock%)|(UPDATE OR INSERT INTO T1%)|(%SP_UPDATE_T1%)|(%SP_DEL_T1%)"
log_procedures=true
Hata günlüğü parametreleri
Ardından, hata günlüğünü belirtmemiz ve bunu deadlock’larla sınırlamamız gerekir.
log_errors = true
include_gds_codes="deadlock"
Lütfen dikkat: include_gds_codes parametresi yalnızca Firebird 3.0.2’de ortaya çıkmıştır, bu nedenle 2.5 veya 3.0.0 veya 3.0.1 kullanıyorsanız, yalnızca deadlock hatalarını filtrelemek mümkün değildir, bu nedenle tüm hatalar günlüğe yazdırılır.
Gösterim
Genel süreci göstermek için test veritabanını hazırlayalım - basitlik için, 1 birincil sütunu olan 1 tablo olacaktır.
C:\HQbird\Firebird30>isql
Use CONNECT or CREATE DATABASE to specify a database
SQL> create database "c:\temp\testdeadlock.fdb" user "SYSDBA" password "masterkey";
SQL> create table t1(i1 integer not null primary key);
SQL> insert into t1(i1) values(1);
SQL> commit; exit;
Bundan sonra, trace oturumunu başlatmamız gerekiyor:
fbtracemgr.exe -se localhost:service_mgr -start -conf "C:\temp\mytrace.conf" -user SYSDBA -pass masterkey
Bu durumda, fbtracemgr günlüğü ekrana yazdırır, ancak üretimde elbette dosyaya yönlendirmemiz gerekir.
Başarılı başlatmadan sonra fbtracemgr şöyle bir şey yazdırır:
Trace session ID 4 started
Ardından, 2 isql oturumu başlatalım ve güncelleme çakışmasını simüle etmeye çalışalım. Aşağıdaki tabloda 2 isql oturumu için 2 sütun vardır. Her satır, komutların gerçekleştirildiği bir anı temsil eder.
| Zaman | isql oturumu 1 | isql oturumu 2 |
| T1 | <br>isql -user SYSDBA -pass masterkey localhost:c:\temp\testdeadlock.fdb<br>Database: localhost:c:\temp\testdeadlock.fdb, User: SYSDBA<br>SQL> set transaction wait;<br>Commit current transaction (y/n)?y<br>Committing.<br>SQL> select current_transaction from rdb$database;<br> CURRENT_TRANSACTION<br>=====================<br> 15<br>SQL> select * from t1;<br> I1<br>============<br> 2<br>SQL><br> |
|
| T2 | <br>isql -user SYSDBA -pass masterkey localhost:c:\temp\testdeadlock.fdb<br>Database: localhost:c:\temp\testdeadlock.fdb, User: SYSDBA<br>SQL> set transaction wait;<br>Commit current transaction (y/n)?y<br>Committing.<br>SQL> select current_transaction from rdb$database;<br> CURRENT_TRANSACTION<br>=====================<br> 20<br>SQL> select * from t1;<br> I1<br>============<br> 2<br>SQL><br> |
|
| Yani, başlangıç noktasında, 2 işlemin başlatıldığını görebiliriz, #15 ve #20. Ardından, aynı kaydın eşzamanlı güncellemesini gerçekleştirmeyi deneyelim: | ||
| T3 | <br>SQL> UPDATE T1 SET i1=5 where i1=2;<br> |
|
| T4 | <br>SQL> UPDATE T1 SET i1=100 where i1=2;<br> |
|
| T5 | <br>SQL> commit;<br>SQL><br> |
<br>Statement failed, SQLSTATE = 40001<br>deadlock<br>-update conflicts with concurrent update<br>-concurrent transaction number is 15<br>SQL><br> |
Oturum 1’de UPDATE başlattık ancak commit etmedik.
Oturum 2’de, UPDATE «takıldı» - eşzamanlı işlemin sonunu bekledi ve oturum 1’de UPDATE’i commit ettiğimizde, oturum 2 hatayı aldı:
Statement failed, SQLSTATE = 40001
deadlock
-update conflicts with concurrent update
-concurrent transaction number is 15
SQL>
Yani, bekleme işlemiyle güncelleme çakışması durumunda tipik bir durumumuz var.
Güncelleme çakışmasının kanıtını bulmak için trace günlüğünü inceleyelim.
Bu durumda, trace günlüğü çok kısadır, ancak üretim sistemi durumunda çok daha uzun olabilir, ancak deadlock’ları bulma yaklaşımı aynıdır.
fbtracemgr.exe -se localhost:service_mgr -start -conf "C:\temp\fbtrace.conf" -user SYSDBA -pass masterkey
Trace session ID 4 started
2019-05-07T11:28:43.9850 (2692:00000000018C1940) EXECUTE_STATEMENT_FINISH
C:\TEMP\TESTDEADLOCK.FDB (ATT_12, SYSDBA:NONE, NONE, TCPv6:::1/52938)
C:\HQbird\Firebird30\isql.exe:8828
(TRA_15, CONCURRENCY | WAIT | READ_WRITE)
Statement 52:
-------------------------------------------------------------------------------
UPDATE T1 SET i1=5 where i1=2
0 records fetched
0 ms, 2 read(s), 21 fetch(es), 3 mark(s)
2019-05-07T11:29:45.0190 (2692:00000000018C0040) FAILED EXECUTE_STATEMENT_FINISH
C:\TEMP\TESTDEADLOCK.FDB (ATT_13, SYSDBA:NONE, NONE, TCPv6:::1/52968)
C:\HQbird\Firebird30\isql.exe:7032
(TRA_20, CONCURRENCY | WAIT | READ_WRITE)
Statement 55:
-------------------------------------------------------------------------------
UPDATE T1 SET i1=100 where i1=2
0 records fetched
40242 ms, 19 fetch(es), 2 mark(s)
2019-05-07T11:29:45.0190 (2692:00000000018C0040) ERROR AT JStatement::execute
C:\TEMP\TESTDEADLOCK.FDB (ATT_13, SYSDBA:NONE, NONE, TCPv6:::1/52968)
C:\HQbird\Firebird30\isql.exe:7032
335544336 : deadlock
335544451 : update conflicts with concurrent update
335544878 : concurrent transaction number is 15
Gördüğünüz gibi, günlükte hem başarılı hem de başarısız her iki güncelleme hakkında kayıtlar ve deadlock hakkında bir hata mesajı var.
Günlüğün nasıl analiz edileceğini ele alalım:
Hata mesajında, işlemin tanımlayıcısını bulalım:
2019-05-07T11:29:45.0190 (2692:00000000018C0040) ERROR AT JStatement::execute
C:\TEMP\TESTDEADLOCK.FDB (ATT_13, SYSDBA:NONE, NONE, TCPv6:::1/52968)
C:\HQbird\Firebird30\isql.exe:7032
335544336 : deadlock
335544451 : update conflicts with concurrent update
335544878 : concurrent transaction number is 15
Hata kaydının yakınında (genellikle hemen üstünde), başarısız ifadeyi göreceksiniz:
2019-05-07T11:29:45.0190 (2692:00000000018C0040) FAILED EXECUTE_STATEMENT_FINISH
C:\TEMP\TESTDEADLOCK.FDB (ATT_13, SYSDBA:NONE, NONE, TCPv6:::1/52968)
C:\HQbird\Firebird30\isql.exe:7032
(TRA_20, CONCURRENCY | WAIT | READ_WRITE)
Statement 55:
-------------------------------------------------------------------------------
UPDATE T1 SET i1=100 where i1=2
0 records fetched
40242 ms, 19 fetch(es), 2 mark(s)
Yürütme süresinin çok büyük olduğuna dikkat edin (~40 saniye) - bu, işlemin eşzamanlı işlemin sonucunu beklediği süreyi gösterir.
Ve son olarak, başarılı UPDATE işlemiyle işlem numarasını arayın: bunun için, NNN işlem numarası olmak üzere TRA_NNN’yi bulun. Bizim durumumuzda, TRA_15 olacaktır:
2019-05-07T11:28:43.9850 (2692:00000000018C1940) EXECUTE_STATEMENT_FINISH
C:\TEMP\TESTDEADLOCK.FDB (ATT_12, SYSDBA:NONE, NONE, TCPv6:::1/52938)
C:\HQbird\Firebird30\isql.exe:8828
(TRA_15, CONCURRENCY | WAIT | READ_WRITE)
Statement 52:
-------------------------------------------------------------------------------
UPDATE T1 SET i1=5 where i1=2
0 records fetched
0 ms, 2 read(s), 21 fetch(es), 3 mark(s)
İşte bu kadar - güncelleme çakışmasının her iki tarafını da bulduk - başarılı ve başarısız olanı.
Deadlock’ları belirleme araçları
Gördüğünüz gibi, kilit çakışmasının/deadlock’un kök kaynağını belirlemek oldukça zor olabilir: çakışmanın her zaman 2 tarafı olduğundan ve bir taraf “kazandığından” (hata mesajları olmadan tamamlanır), kazananı belirlemek zor olabilir. Örneğin, çakışma EXECUTE STATEMENT içinde, iç içe geçmiş saklı yordamda, tetikleyicide veya bunların bir kombinasyonu olabilir.
Deadlock’ları belirlemek için IBSurgeon, trace günlüğünü analiz eden ve deadlock’lara yol açan çağrıların ayrıntılarıyla anlaşılması kolay diyagramlar oluşturan “Deadlock Analyzer” aracını geliştirmiştir. Bu araç Enterprise Subscription’ın bir parçası olarak mevcuttur (deneme sürümü vardır) - cc.ib-aid.com adresine kaydolun.
Ayrıca, HQbird, yalnızca değişiklikleri ve değişiklik girişimlerini (yani başarısız işlemleri ve kilit çakışmalarını ve deadlock’ları) kaydetmek üzere yapılandırılabilen fbtrace eklentisinin genişletilmiş bir sürümünü sunar.
Bu konuda daha fazla bilgi almak için IBSurgeon desteğiyle iletişime geçin: [email protected].
Deadlock’lardan nasıl kaçınılır?
Şimdi, uygulamanız için deadlock’lardan kaçınmak için en iyi çözümü belirlemeniz gerekir - bu, işlem parametrelerinin değiştirilmesi olabilir veya ek hata işleme eklemek ve işlemi tekrarlamak veya uygulamanın iş düzeyinde ek kontroller yapmak mümkündür.