Müşterilerimizden birinde, uygulamamızda bazı performans sorunları yaşıyoruz. Bir SQL Server veritabanındaki verileri tüketen ve güncelleyen bir .NET 3.5 web uygulamasıdır. Üretim ortamımız şu anda ön uç olarak bir Windows 2008 R2 makinesinden ve arka uçta bir SQL Server 2008 R2 kümesinden oluşuyor. Bizim app veritabanına bağlanmak için COM + ve MSDTC kullanır.
İşte olanlar: Son kullanıcılarımız bazen uygulamadaki yavaşlıktan şikayet ediyorlar. Bazı sayfaların yüklenmesi beklenenden daha fazla zaman alıyor. Ne olduğunu anlamaya çalışırken, veritabanı tarafında performans düşüşünün nedeni olabilecek bazı garip davranışlar bulmayı başardım. Bazen ne olacağını çalıştırmak için çok daha fazla zaman alan bazı SQL ifadeleri olduğunu fark ettim. Uzun süren sorguları tanımlamak için bir profiler izlemesi (TSQL_Duration şablonu ile) kullanarak bu ifadelerden bazılarını (esas olarak uygulamanın saklı yordamlarının çağrıları) tanımlamayı başardım.
Sorun bu saklı yordamlar doğrudan SQL Management Studio üzerinde veritabanında çalıştırdığınızda bazen uzun (yaklaşık 7/8 saniye), diğer zamanlarda hızlı (1 sn altında). Bunun neden olduğunu bilmiyorum ve SQL makine (4 çekirdekli, 32 GB) başka uygulamalar tarafından kullanılmadığı için beni deli ediyor ve bu sorgular bu kadar uzun sürmemeli.
Bir DBA veya SQL Server gurusu olmamak, sorunu anlamama yardımcı olabilecek bazı şeylere bakmaya çalışıyorum. Sorunu ve şu ana kadar öğrendiklerimi çözmek için attığım adımlar şunlardır:
- Uygulama tarafından çağrılan tüm TSQL kodu saklı yordamlarda yazılır.
- SQL Server profiler üzerinde uzun süren sorguların bazılarını belirledim, ancak bunları Management Studio'da çalıştırdığımda ya çalıştırmak (4 ila 10 saniye arasında) uzun sürebilir ya da hızlı bir şekilde (1 saniyenin altında) çalışabilirler. Parametrelerde geçirilen aynı verilerle tam olarak aynı sorguları çalıştırıyorum. Bu sorgular çoğunlukla içinde belirli ifadeler bulunan saklı yordamlardır.
- Bazı kaynaklar üzerinde bekleyen süreçler olup olmadığını anlamak için beklemek ve kuyruk istatistiklerine bakmaya çalıştım. Aşağıdaki sorguyu çalıştırdım:
WITH Waits AS
(SELECT
wait_type,
wait_time_ms / 1000.0 AS WaitS,
(wait_time_ms - signal_wait_time_ms) / 1000.0 AS ResourceS,
signal_wait_time_ms / 1000.0 AS SignalS,
waiting_tasks_count AS WaitCount,
100.0 * wait_time_ms / SUM (wait_time_ms) OVER() AS Percentage,
ROW_NUMBER() OVER(ORDER BY wait_time_ms DESC) AS RowNum
FROM sys.dm_os_wait_stats
WHERE wait_type NOT IN (
'CLR_SEMAPHORE', 'LAZYWRITER_SLEEP', 'RESOURCE_QUEUE', 'SLEEP_TASK',
'SLEEP_SYSTEMTASK', 'SQLTRACE_BUFFER_FLUSH', 'WAITFOR', 'LOGMGR_QUEUE',
'CHECKPOINT_QUEUE', 'REQUEST_FOR_DEADLOCK_SEARCH', 'XE_TIMER_EVENT', 'BROKER_TO_FLUSH',
'BROKER_TASK_STOP', 'CLR_MANUAL_EVENT', 'CLR_AUTO_EVENT', 'DISPATCHER_QUEUE_SEMAPHORE',
'FT_IFTS_SCHEDULER_IDLE_WAIT', 'XE_DISPATCHER_WAIT', 'XE_DISPATCHER_JOIN', 'BROKER_EVENTHANDLER',
'TRACEWRITE', 'FT_IFTSHC_MUTEX', 'SQLTRACE_INCREMENTAL_FLUSH_SLEEP',
'BROKER_RECEIVE_WAITFOR', 'ONDEMAND_TASK_QUEUE', 'DBMIRROR_EVENTS_QUEUE',
'DBMIRRORING_CMD', 'BROKER_TRANSMITTER', 'SQLTRACE_WAIT_ENTRIES',
'SLEEP_BPOOL_FLUSH', 'SQLTRACE_LOCK')
)
SELECT
W1.wait_type AS WaitType,
CAST (W1.WaitS AS DECIMAL(14, 2)) AS Wait_S,
CAST (W1.ResourceS AS DECIMAL(14, 2)) AS Resource_S,
CAST (W1.SignalS AS DECIMAL(14, 2)) AS Signal_S,
W1.WaitCount AS WaitCount,
CAST (W1.Percentage AS DECIMAL(4, 2)) AS Percentage,
CAST ((W1.WaitS / W1.WaitCount) AS DECIMAL (14, 4)) AS AvgWait_S,
CAST ((W1.ResourceS / W1.WaitCount) AS DECIMAL (14, 4)) AS AvgRes_S,
CAST ((W1.SignalS / W1.WaitCount) AS DECIMAL (14, 4)) AS AvgSig_S
FROM Waits AS W1
INNER JOIN Waits AS W2 ON W2.RowNum <= W1.RowNum
GROUP BY W1.RowNum, W1.wait_type, W1.WaitS, W1.ResourceS, W1.SignalS, W1.WaitCount, W1.Percentage
HAVING SUM (W2.Percentage) - W1.Percentage < 95; -- percentage threshold
GO
İşte buldum:
- İstatistikleri DBCC SQLPERF kullanarak sıfırladıktan sonra (yaklaşık 1 veya 2 saat sonra), en çok sahip olduğum bekleme türleri SOS_SCHEDULER_YIELD ve WRITELOG
- Zaman içinde (yaklaşık 1 günlük uygulamadan sonra), her biri için ortalama bekleme süresi uzun olmasa da, veritabanında en çok meydana gelen bekleme türleri CXPACKET (% 67) ve OLEDB'dir (% 17). Ayrıca SQL Profiler üzerinde tanımlanan uzun çalışan ifadeler birden fazla sonuç kümesi (genellikle 3) döndürülen saklı yordamlar çağrıları olduğunu fark ettim. Burada bir paralellik sorunu olabilir mi? Sorunun sebebinin bu olup olmadığını belirlemeye çalışabileceğim herhangi bir yol var mı?
- OLEDB beklemelerinin bağlantılı sunucular gibi OLEDB kaynaklarına yapılan çağrılardan kaynaklanabileceğini bir yerde okudum. Bir Dizin Oluşturma Hizmetleri makinesine (MSIDXS) bağlanmak için bağlantılı bir sunucumuz var, ancak uzun süre çalıştığı belirtilen ifadelerin hiçbiri bu bağlantılı sunucuyu kullanmıyor.
- Sahip olduğum daha yüksek ortalama bekleme süresi LCK_M_X tipi beklemeler içindir (ortalama yaklaşık 1,5 sn), ancak bu bekleme türleri diğer türlerle karşılaştırıldığında çok sık gerçekleşmez (örneğin, 64 LCK_M_X bekler ve 10,823 CXPACKET aynı süre bekler ).
- Fark ettiğim bir şey, MSDTC hizmetinin kümelenmemiş olmasıdır. SQL Server hizmeti kümelenmiş ancak MSDTC değil. Bu nedenle bir performans isabeti olabilir mi? Uygulamamız veritabanına erişmek için Enterprise Services (DCOM) kullandığından MSDTC kullanıyoruz, ancak sunucular tarafımızdan değil, istemcimiz tarafından yüklenip yapılandırıldı.
Herkes bu veriler hakkında daha fazla anlam ifade etmeme yardımcı olabilir mi? Biri bana neler olabileceğini anlama konusunda yardım edebilir mi? Sunucuda bir şeyler bulmaya çalışmak için yapabileceğim bir şey var mı? Uygulama geliştirme ekibiyle konuşmalı mıyım?
exec()Fonksiyonu kullanmak gözlemlenen davranışı açıklar. Bu durumdasp_executesqlnormal olarak kullanmak dinamik SQL ifadeleriyle ilgili sorunları çözer.