30 Ocak 2021 Cumartesi

Blok ve Bloklanan İşler

 İdeal olarak veritabanı uygulamanızın veritabanı kullanıcı sayısı ile doğrusal olarak ölçeklenmesi gerekir. Ancak kullanıcı sayısı arttıkça kullanıcı sayısı performansı düşürdüğü yaygın bir durumdur. Artan ölçekle ilişkili olan bozulmanın nedeni engellemedir.

SQL Serverda engellemenin temelleri;

*ACID özellikleri

*Veritabanı kilit ayrıntı düzeyi, yükseltme, modlar ve uyumluluk.

*ANSI izolasyon seviyeleri

*Dizinlerin kilitlenme üzerindeki etkisi

*Engellemeyi analiz etmek için gerekli bilgiler

*Engellemeyi önlemek için kararlar ve öneriler

*Engelleme algılama ve bilgi toplama süreçlerinin otomatikleştirme teknikleri,


Atomiklik, Tutarlılık, İzolasyon, Dayanıklılık.


USE AdventureWorks2012;
GO
IF (SELECT OBJECT_ID('dbo.ProductTest')

    ) IS NOT NULL
      DROP TABLE dbo.ProductTest;
GO
CREATE TABLE dbo.ProductTest (
ProductID INT CONSTRAINT ValueEqualsOne CHECK (ProductID = 1));
GO


--All ProductIDs are added into t1 as a logical unit of work
INSERT INTO dbo.ProductTest
          SELECT p.ProductID
          FROM Production.Product AS p;

GO
SELECT *
FROM dbo.ProductTest; --Returns 0 rows
SQL Server önceki INSERT deyimini mantıksal çalışma birimi olarak ele alır. Sütundaki CHECK kısıtı ProductTest tablosunun ProductID sinini yanlızca 1 değerine izin verir. Bu nedenle ProductTest tablosuna hiç bir kayıt eklemeyecektir. CHECK nedeniyle hata oluşacaktır. Bu SQL Server tarafından otomatik olarak sağlanır.


BEGIN TRAN

 --Start: Logical unit of work

--First:

INSERT INTO dbo.ProductTest

            SELECT p.ProductID

            FROM Production.Product AS p;

--Second:

INSERT INTO dbo.ProductTest

VALUES (1);

COMMIT --End: Logical unit of work

GO

SQL Serverda önceki ekleme görevinden birden çok INSERT çalıştığını düşünerek daha büyük bir işin çalıştığını düşünürsek;  yukarıdaki gibi daha büyük mantıksal çalışma birimi oluşacaktır. Önceki komut dosyası zaten oluşmuş olan ProductTest tablosu ile BEGIN TRAN ve COMMIT çifti ile tüm ifadelerin atomik olması gerektiğini öneren mantıksal çalışma birimini tanımlar. ilk INSERT ifadesi başarısız olacak oysa ikinci INSERT makuldur ve varsayılan davranış ikinci INSERT ifadesinden SQL Server yürütmesine izin verecektir.

Kullanıcı tanımlı işlemin atomikliği 2 yolla sağlanır.

* SET XACT_ABORT ON
* Explicit rollback


SET XACT_ABORT ON


SET XACT_ABORT ON deyimini kullanarak işlemin atomikliği değiştirilebilir.

SQL Server komuttaki bir işlemin başarısız olduğunda otomatik olarak geri dönüş yapıp yapmayacağının karar vermesini sağlar. INSERT ifadesindeki birinci aşamadaki işlem başarısız olursa otomatik olarak ikinci INSERT ifadesini yürütmeyecektir. SET XACT_ABORT bağlantı düzeyinde yenide yapılandırılıncaya kadar yada bağlantı kurulana kadar geçerli kalır. Varsayılan olarak bu özellik kapalıdır.


SET XACT_ABORT ON;
GO
BEGIN TRAN
 --Start: Logical unit of work
--First:
INSERT INTO dbo.ProductTest

            SELECT p.ProductID
            FROM Production.Product AS p;
--Second:
INSERT INTO dbo.ProductTest
VALUES (1);
COMMIT
 --End: Logical unit of work GO
SET XACT_ABORT OFF;
GO

Explicit rollback

TRY/CATCH hata yakalama mekanizmasını kullanarak kullanıcı tanımlı bir işlemin atomikliğini yönetebilirsiniz. SQL Server içerisinde TRY kod bloğu içinde ifade hata alırsa kodu CATCH bloğuna hatayı paslayarak tüm işlemler geri alınabilir ve sonraki ifadelerin yürütülmesi engellenebilir.


BEGIN TRY
      BEGIN TRAN
      --Start: Logical unit of work
      --First:
      INSERT INTO dbo.ProductTest

                  SELECT p.ProductID
                  FROM Production.Product AS p
      Second:
      INSERT INTO dbo.ProductTest
                  (ProductID)
      VALUES (1)
      COMMIT --End: Logical unit of work
END TRY
BEGIN CATCH
      ROLLBACK
      PRINT 'An error occurred'
      RETURN
END CATCH


23 Ocak 2021 Cumartesi

Kilit İçeriğini En Aza İndirgeme

 

  Kaynaklardan birinde kilit isteğinden kaçınarak kilitlenme çözülebilir. Kaynağa yanlızca veri okumak için erişilir. Bir kaynak üzerinde değişiklik yapmak her zaman özel bir kilit kazanacaktır. Kaynağın tutarlılığını korumak için kilitlenme durumunda kaynak erişimlerini tanımlanması gerekir. Salt okunur, kirli okuma özelliklerini kullanarak karşılıklı gelen kilit isteklerinden kaçınmaya çalışın.


Kaynak kilitlerinden kaçınmak için aşağıdaki tetikler kullanılır.

* Satır versiyonlama Uygulamak

* İzolasyon seviyesini arttırmak

* Kilitlenme ipuçlarının kullanılması.


Satır versiyonlama Uygulamak

Katı kilitlenme şeması kullanarak kaynaklara erişim engelleye çalışmak yerine READ_COMMITTED_SNAPSHOT izlosayon seviyesi veya SNAPSHOT izolasyon seviyesi ile satır versiyonlama. Satır versiyonlama izolasyon seviyelerini engellemeyi azaltmak için kullanılıyor. 


ALTER DATABASE AdventureWorks2012

SET READ_COMMITTED_SNAPSHOT ON;


Yukarıdaki T-SQL ile tempdb de bulunan satırların bir sürümüne sahip olabilirsiniz potansiyel olarak önceki kilitlenme senaryosundaki kilitlenmenin neden olduğu detayı görebilirsiniz. Bu okumalar farklı bir sürümde olduğu için kilit çekişmesine neden olmadan gerekli tüm okumalara izin verecektir. Satır sürümleme ek yük getirecektir. Özellikle tempdb de sorguda kullanılan tablo, dizinler. Artan değiş tokuş azaltılmış kilitlenmeler ve artan eşzamanlılık avantajına karşı ek yük ve maliyet değerlendirilebilir.


İzolasyon seviyesini arttırmak

Bazen bir SELECT ifadesi  tarafından istenen (S) kilidi dairesel engellemenin oluşumuna katkıda bulunacaktır.  SELECT deyimi içeren işlemin yalıtım düzeyini azaltarak bu tür döngüsel engellemelerden kaçınılabilir. SELECT ifadesindeki (s) kilidi istenmeden verilerin okunmasına ve böylece dairesel engellemelerden kaçınmasına yarar sağlayacaktır.  Ancak öngörülmeyen verilerin okunması kötü verileri döndürerek ciddi sorunlara sebep verecektir. 

Ayrıca bağlantıların kendilerini SERİLEŞTİRİLEBİLiR olarak ayarlayıp ayarlamadığını kontrol etmeniz gerekir. Bazen çevrimiçi bağlantı dizisi oluşturucuları bu seçeneği içerecek ve geliştiricilerinde bunu kullanması sonucunda istenmeyen sonuçlara yol açabilecektir. MSDTC varsayılan olarak serileştirilebilir kullanılır ancak bu değiştirilebilir.


Kilitlenme ipuçlarının kullanılması.

READ UNCOMMITTED izolasyon seviyesi gibi NOLOCK veya READUNCOMMITTED kilitlenme ipucu (s) kilitlerini önleyecektir. Belirli bir oturum tarafından istenir, böylece dairesel blokaj oluşumunu engeller.


Kilitleme ipucu etkisi sorgu düzeyindedir. Uygulandığı tablo ve dizinlerle sınırlıdır. NOLOCK ve  READUNCOMMITTED kilitlenme ipuçları yanlızca SELECT deyimlerinden ve veri seçim bölümlerinde izin verilir.

INSERT, DELETE, UPDATE

Kirli okuma sayfa bölümleri nedeniyle eksik veya fazla satırları içerebilir. Bu teknik sadece düşük kaliteli verilerin olduğu durumlarda kullanılabilir.


16 Ocak 2021 Cumartesi

DeadLock işlemlerinden Kaçınma


Kilitlenmeyi önleyebileceğimiz teknikler;

1- Kaynaklara aynı fiziksel sırayla erişim,

2-Erişilen kaynakların sayısının azaltılması,

3-Kilit çelişkisinin azaltılması.



1- Kaynaklara Aynı Fiziksel Sıra ile Erişim 

Aynı fiziksel düzendeki kaynaklara her işlemin eriştiğinden emin olunması gerekiyor. Her işlem kaynaklara aynı fiziksel sırada erişirse ilk işlem başarılı gerçekleşecek ikinci işlem tarafından engellenmeden işini bitirecektir. ikinci işlem işine devam edebilecektir.  İlk işlem işini bitirmeden ikinci işlem kaynağa erişime çalışırsa bu sefer birinci işlem işlemi bloke edecektir. Buda dairesel bir engellemeye yol açacaktır. 


İşlemlerden;

Transaction-1 [Access Resource 1] + [Access Resource 2]

Transaction-2 [Access Resource 2] + [Access Resource 1]

Resource 1, hobtid=72057594046578688: This is the index row within index
      PK_ PurchaseOrderDetail_PurchaseOrderId_PurchaseOrderDetailId on the
      Purchasing.PurchaseOrderDetail table.

Resource 2, hobtid=72057594046644224: This is the row within clustered index
PK_PurchaseOrderHeader_PurchaseOrderId on the Purchasing.PurchaseOrderHeader table.


Yukarıdaki mesaj kilitlenme senaryosuna örnek mesajı teşkil etmektedir. 

2- Erişilen Kaynak Sayısının Azaltılması

Bir kilitlenme en az iki kaynak içerir. Bir otorum bir kaynağı tutar ve ardından ikinci kaynağı ister. Diğer oturum ikinci kaynağı tutar ve ilk kaynağı ister. Çıkmaza dahil olan kaynaklardan birine erişirseniz kilitlenmeyi önleyebilirsiniz. Dirençli bir çözüm olarak uygulama yeniden tasarlanarak bu önlenebilir. Ancak, Uygulama tasarımı değiştirilmeden SQL Server aşağıdaki özellikleri kullanmayın düşünebilirsiniz.

* Kümelenmemiş bir dizinin kümelenmiş bir dizine dönüştürülmesi.
* SELECT ifadesi için bir kaplama index kullanılması.

* Kümelenmemiş bir dizinin kümelenmiş bir dizine dönüştürülmesi.

Kümelenmiş ve kümelenmemiş dizinin sayfaları birbirlerinden farklıdır. Kümelenmemiş dizin iki kilit alır, biri taban (küme veya yığın) diğeri kümelenememiş dizin. Kümelenmemiş dizinde yaprasak sayfaları ve dizinin veri sayfaları tabloda aynıdır. Tek kilit gerektirir, bu kilit hem kümelenmiş dizini hemde tabloyu korur. Sebebi yaprak sayfaları ve veri sayfaları aynıdır. Bu aynı sorgu ile erişilecek kaynakların sayısı azaltılır. (Ancak kümelenmemiş bir dizine karşı bu geçerlidir.) Bu işin çalışması için tamamen bunun uygun kümelenmiş index olmasına bağlıdır.

* SELECT ifadesi için bir kaplama index kullanılması.

SELECT ifadesi kaplama dizinin kendisinden her şeyi alabilir. Teme tabloya erişmesine gerek yoktur. SELECT deyiminin temel tabloya erişimini durdurarak temel  tabloyu başka bir oturum tarafından kilitlenir.

3 Ocak 2021 Pazar

DeadLock Analiz #2



Öncelikle kilitlenme bayrağı 1222 ve xml_deadlock_report olaylarının kullanıma açıldığından emin olmamız gerekiyor.

Tek bir bağlantıda aşağıdaki komut çalıştırılabilir;

BEGIN TRAN
UPDATE Purchasing.PurchaseOrderHeader
SET Freight = Freight * 0.9 -- 10% discount on shipping
WHERE PurchaseOrderID = 1255;

ikinci bağlantıda aşağıdaki komut çalıştırılabilir.

BEGIN TRANSACTION
UPDATE Purchasing.PurchaseOrderDetail
SET OrderQty = 4
WHERE ProductID = 448
AND PurchaseOrderID = 1255;

Yukarıdaki komutlar çalıştırıldığında her biri bir işlem açar ve verileri işlemeye başlar. Ancak ne kadar zamanda işlemi yapacağını ve geri alacağını bilemiyoruz.

Birinci komuta gidip aşağıdaki işlemi çalıştırılalım;

UPDATE Purchasing.PurchaseOrderDetail
SET OrderQty = 2
WHERE ProductID = 448
AND PurchaseOrderID = 1255;

İlk bağlantı büyük ihtimalle bir kaç saniye sonra bir kilitlenme oluşacaktır. 

Msg 1205, Level 13, State 51, Line 1
Transaction (Process ID 52) was deadlocked on lock resources with another process and has been
chosen as the deadlock victim. Rerun the transaction.

Önce izleme olayı aracılıyla toplanan kilitlenme grafiğini incelememiz ardından xml_deadlock_report olay için bu pencerede verilen grafiği incelememiz gerekir. 

Ancak daha detaya inerek kilitlenme olayının tam olarak nerede olduğu hangi süreçlerin buna neden olduğu hangi nesnelerin dahil edildi bilgileri XML dosyasını doğrudan genişletilmiş olay grafiğini değerlerinden bulabiliriz.


Örnek tabloda uPurchaseOrderDetail adlı bir tetikleyici bulunuyor. Tüm bu bilgiler hangi kod parçalarının kilitlenmeye yol açtığını belirlememizde yardımcı olacaktır. Ayrıca SQL Handle  gibi bilgileri almak, DMO lar ile birlikte ifadeleri almak kilitlenme ile ilgili bilgilerde bize yardımcı olacaktır.

İzleme bayrağı 1222 tarafından toplanan veriler ile XML içerisindeki bilgiler hemen hemen aynıdır.  Ana farklılıkları biçimlendirme ve konumudur. İzleme bayrağı 1204 tarafından toplanan veriler ise tamamen farklıdır.  Kullanabiliyorsanız Genişletilmiş olayları kullanmaya devam etmeniz işinizin kolaylaşması açısında daha iyidir. 1222 takip edilmesi genellikle önerilir. system_health içinde oluşan bilgilerde size yardımcı olacaktır. 

Yukarıdaki örnekde kilitlenme Purchasing.PurchaseOrderDetail  tablosundaki bir tetikleyiciden kaynaklanmaktadır. Miktar güncellendiğinde Purchasing.PurchaseOrderDetail tablosunda Purchasing.PurchaseOrderHeader tablosu güncellemeye çalışır. İlk iki sorgu çalıştığında her biri açık bir işlem üzerinde bir engelleme durumu söz konusu olur. İkinci sorgu ilk sorgunun temizlenmesini bekler böylece Purchasing.PurchaseOrderHeader tablosunu güncelleyebilir. Sorunu çözebilmek için süreçlerden birini öldürmektir.