Mağazalardaki verileri alan ve şirket genelindeki bir envanter tablosunu güncelleyen bir işlemimiz var. Bu tabloda, her mağaza için tarihe ve öğeye göre satırlar bulunur. Birçok mağazaya sahip müşteriler için, bu tablo çok büyük olabilir - 500 milyon sıraya kadar.
Bu envanter güncelleme işlemi, mağazalar veri girdikçe genellikle günde birçok kez çalıştırılır. Bunlar yalnızca birkaç mağazadan güncelleme verilerini çalıştırır. Bununla birlikte, müşteriler bunu son 30 gün içindeki tüm mağazaları güncellemek için de çalıştırabilir. Bu durumda, işlem 10 iş parçacığı döndürür ve her mağazanın envanterini ayrı bir iş parçacığında günceller.
Müşteri, sürecin uzun sürdüğünden şikayet ediyor. Süreci profilli ve bu tabloya INSERTs bir sorgu beklediğimden çok daha fazla zaman harcadığını bulundu. Bu INSERT bazen 30 saniye içinde tamamlanır.
Bu tabloya karşı geçici bir SQL INSERT komutu çalıştırdığımda (BEGIN TRAN ve ROLLBACK ile sınırlı), geçici SQL milisaniye sırasına göre tamamlanır.
Yavaş performans gösteren sorgu aşağıdadır. Fikir, orada olmayan kayıtları eklemek ve daha sonra çeşitli veri bitlerini hesaplarken bunları GÜNCELLEMEK. Süreçteki bir önceki adım, güncellenmesi gereken öğeleri belirledi, bazı hesaplamalar yaptı ve sonuçları temp_d_ Update_Item_Work tempdb tablosuna doldurdu. Bu işlem 10 ayrı iş parçacığında çalışır ve her iş parçacığının Update_Item_Work içinde kendi GUID değeri vardır.
INSERT INTO Inventory
(
Inv_Site_Key,
Inv_Item_Key,
Inv_Date,
Inv_BusEnt_ID,
Inv_End_WtAvg_Cost
)
SELECT DISTINCT
UpdItemWrk_Site_Key,
UpdItemWrk_Item_Key,
UpdItemWrk_Date,
UpdItemWrk_BusEnt_ID,
(CASE UpdItemWrk_Set_WtAvg_Cost WHEN 1 THEN UpdItemWrk_WtAvg_Cost ELSE 0 END)
FROM tempdb..Update_Item_Work (NOLOCK)
WHERE UpdItemWrk_GUID = @GUID
AND NOT EXISTS
-- Only insert for site/item/date combinations that don't exist
(SELECT *
FROM Inventory (NOLOCK)
WHERE Inv_Site_Key = UpdItemWrk_Site_Key
AND Inv_Item_Key = UpdItemWrk_Item_Key
AND Inv_Date = UpdItemWrk_Date)
Envanter tablosunda, çoğu çeşitli envanter ayarlamaları için miktarları ve sayıları izleyen 42 sütun bulunur. sys.dm_db_index_physical_stats her satırın yaklaşık 242 bayt olduğunu söylüyor, bu yüzden yaklaşık 33 satırın tek bir 8 KB'lik sayfaya sığacağını umuyorum.
Tablo benzersiz kısıtlamayla (Inv_Site_Key, Inv_Item_Key, Inv_Date) kümelenmiştir. Tüm tuşlar DECIMAL (15,0) ve tarih SMALLDATETIME. Bir IDENTITY birincil anahtarı (kümelenmemiş) ve diğer 4 dizin vardır. Tüm dizinler ve kümelenmiş kısıtlama açık bir şekilde tanımlanır (FILLFACTOR = 90, PAD_INDEX = ON).
Sayfa bölünmelerini saymak için günlük dosyasına baktım. Kümelenmiş dizinde yaklaşık 1.027 bölme ve başka bir dizinde 1.724 bölme ölçtüm, ancak bunların hangi aralıkta gerçekleştiğini kaydetmedim. Bir buçuk saat sonra, kümelenmiş dizinde 7.035 sayfa bölünmesi ölçtüm.
Profil oluşturucuda yakaladığım sorgu planı şöyle:
Rows Executes StmtText
---- -------- --------
490 1 Sequence
0 1 |--Index Update
0 1 | |--Collapse
0 1 | |--Sort
0 1 | |--Filter
996 1 | |--Table Spool
996 1 | |--Split
498 1 | |--Assert
0 0 | |--Compute Scalar
498 1 | |--Clustered Index Update(UK_Inventory)
498 1 | |--Compute Scalar
0 0 | |--Compute Scalar
0 0 | |--Compute Scalar
498 1 | |--Compute Scalar
498 1 | |--Top
498 1 | |--Nested Loops
498 1 | |--Stream Aggregate
0 0 | | |--Compute Scalar
498 1 | | |--Clustered Index Seek(tempdb..Update_Item_Work)
498 498 | |--Clustered Index Seek(Inventory)
0 1 |--Index Update(UX_Inv_Exceptions_Date_Site_Item)
0 1 | |--Collapse
0 1 | |--Sort
0 1 | |--Filter
996 1 | |--Table Spool
490 1 |--Index Update(UX_Inv_Date_Site_Item)
490 1 |--Collapse
980 1 |--Sort
980 1 |--Filter
996 1 |--Table Spool
Sorgulara vs çeşitli dmv bakarak, ben bu Envanter tablosundaki bir sayfada 0 süre PAGEIOLATCH_EX sorgu bekliyor görüyorum. Ben kilit beklemek veya engelleme görmüyorum.
Bu makinenin yaklaşık 32 GB belleği vardır. Yakında 2008 R2 Enterprise Edition'a yükseltmekle birlikte SQL Server 2005 Standard Edition çalıştırıyor. Envanter tablosunun disk kullanımı açısından ne kadar büyük olduğuna dair rakamlarım yok, ancak gerekirse alabilirim. Bu sistemdeki en büyük tablolardan biridir.
Sys.dm_io_virtual_file_stats karşı bir sorgu çalıştırdı ve tempdb karşı ortalama yazma beklemeleri 1.1 saniye yukarı olduğunu gördüm . Bu tablonun saklandığı veritabanı ~ 350 ms ortalama yazma beklemesine sahiptir. Ancak sunucularını yalnızca 6 ayda bir yeniden başlatırlar, bu nedenle bu bilgilerin alakalı olup olmadığı hakkında hiçbir fikrim yok. tempdb 4 farklı dosyaya dağıtılır Envanter tablosunu tutan veritabanı için 3 farklı dosyaya sahiptir.
Neden tek bir INSERT çok hızlı olduğunda bu sorgu birçok farklı iş parçacığı ile çalıştırıldığında birkaç satır INSERT kadar uzun sürer?
-- GÜNCELLEME --
İşte okunan bayt dahil sürücü başına gecikme sayıları. Gördüğünüz gibi tempdb performansı şüphelidir. Envanter tablosu PDICompany_252_01.mdf, PDICompany_252_01_Second.ndf veya PDICompany_252_01_Third.ndf dizinindedir.
ReadLatencyWriteLatencyLatencyAvgBPerRead AvgBPerWriteAvgBPerTransferDriveDB physical_name
42 1112 623 62171 67654 65147R: tempdb R:\Microsoft SQL Server\Tempdb\tempdev1.mdf
38 1101 615 62122 67626 65109S: tempdb S:\Microsoft SQL Server\Tempdb\tempdev2.ndf
38 1101 615 62136 67639 65123T: tempdb T:\Microsoft SQL Server\Tempdb\tempdev3.ndf
38 1101 615 62140 67629 65119U: tempdb U:\Microsoft SQL Server\Tempdb\tempdev4.ndf
25 341 71 92767 53288 87009X: PDICompany X:\Program Files\PDI\Enterprise\Databases\PDICompany_Third.ndf
26 339 71 90902 52507 85345X: PDICompany X:\Program Files\PDI\Enterprise\Databases\PDICompany_Second.ndf
10 231 90 98544 60191 84618W: PDICompany_FRx W:\Program Files\PDI\Enterprise\Databases\PDICompany_FRx.mdf
61 137 68 9120 9181 9125W: model W:\Microsoft SQL Server\MSSQL.3\MSSQL\Data\modeldev.mdf
36 113 97 9376 5663 6419V: model V:\Microsoft SQL Server\Logs\modellog.ldf
22 99 34 92233 52112 86304W: PDICompany W:\Program Files\PDI\Enterprise\Databases\PDICompany.mdf
9 20 10 25188 9120 23538W: master W:\Microsoft SQL Server\MSSQL.3\MSSQL\Data\master.mdf
20 18 19 53419 10759 40850W: msdb W:\Microsoft SQL Server\MSSQL.3\MSSQL\Data\MSDBData.mdf
23 18 19 947956 58304 110123V: PDICompany_FRx V:\Program Files\PDI\Enterprise\Databases\PDICompany_FRx_1.ldf
20 17 17 828123 55295 104730V: PDICompany V:\Program Files\PDI\Enterprise\Databases\PDICompany.ldf
5 13 13 12308 4868 5129V: master V:\Microsoft SQL Server\Logs\mastlog.ldf
11 13 13 22233 7598 8513V: PDIMaster V:\Program Files\PDI\Enterprise\Databases\PDIMaster.ldf
14 11 13 13846 9540 12598W: PDIMaster W:\Program Files\PDI\Enterprise\Databases\PDIMaster.mdf
13 11 11 22350 1107 1110V: msdb V:\Microsoft SQL Server\Logs\MSDBLog.ldf
17 9 9 745437 11821 23249V: PDIFoundation V:\Program Files\PDI\Enterprise\Databases\PDIFoundation.ldf
34 8 31 29490 33725 30031W: PDIFoundation W:\Program Files\PDI\Enterprise\Databases\PDIFoundation.mdf
5 8 8 61560 61236 61237V: tempdb V:\Microsoft SQL Server\Logs\templog.ldf
13 6 11 8370 35087 16785W: SAHost_Company01 W:\Program Files\PDI\Enterprise\Databases\SAHostCompany.mdf
2 6 5 56235 33667 38911W: SAHost_Company01 W:\Program Files\PDI\Enterprise\Databases\SAHost_Company_01_log.LDF