Chúng tôi có một quy trình lấy dữ liệu từ các cửa hàng và cập nhật bảng kiểm kê toàn công ty. Bảng này có các hàng cho mọi cửa hàng theo ngày và theo mặt hàng. Tại các khách hàng có nhiều cửa hàng, bảng này có thể nhận được rất lớn - với đơn hàng 500 triệu hàng.
Quá trình cập nhật hàng tồn kho này thường được chạy nhiều lần trong ngày khi các cửa hàng nhập dữ liệu. Những chạy này cập nhật dữ liệu từ chỉ một vài cửa hàng. Tuy nhiên, khách hàng cũng có thể chạy nó để cập nhật, tất cả các cửa hàng trong 30 ngày qua. Trong trường hợp này, quy trình sẽ tạo ra 10 luồng và cập nhật hàng tồn kho của mỗi cửa hàng trong một luồng riêng biệt.
Khách hàng đang phàn nàn rằng quá trình này mất nhiều thời gian. Tôi đã mô tả quá trình và thấy rằng một truy vấn mà CHỈ vào bảng này sẽ tiêu tốn nhiều thời gian hơn tôi mong đợi. CHERTN này đôi khi hoàn thành trong 30 giây.
Khi tôi chạy lệnh INSERT SQL ad-hoc đối với bảng này (giới hạn bởi BEGIN TRAN và ROLLBACK), SQL ad-hoc hoàn thành theo thứ tự mili giây.
Các truy vấn thực hiện chậm là dưới đây. Ý tưởng là để ghi lại các bản ghi không có ở đó và sau đó để CẬP NHẬT chúng khi chúng tôi tính toán các bit dữ liệu khác nhau. Bước trước trong quy trình đã xác định các mục cần được cập nhật, thực hiện một số tính toán và nhồi kết quả vào bảng tempdb Update_Item_Work. Quá trình này đang chạy trong 10 luồng riêng biệt và mỗi luồng có GUID riêng trong Update_Item_Work.
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)
Bảng Inventory có 42 cột, hầu hết theo dõi số lượng và số lượng để điều chỉnh hàng tồn kho khác nhau. sys.dm_db_index_physical_stats nói rằng mỗi hàng có khoảng 242 byte, vì vậy tôi hy vọng khoảng 33 hàng sẽ phù hợp trên một trang 8 KB.
Bảng được nhóm trên ràng buộc duy nhất (Inv_Site_Key, Inv_Item_Key, Inv_Date). Tất cả các khóa là DECIMAL (15,0) và ngày là SMALLDATETIME. Có một khóa chính IDENTITY (không bao gồm) và 4 chỉ mục khác. Tất cả các chỉ mục và ràng buộc phân cụm được xác định bằng một tường minh (FILLFACTOR = 90, PAD_INDEX = ON).
Tôi đã xem trong tệp nhật ký để đếm số lần chia trang. Tôi đã đo khoảng 1.027 lần phân tách trên chỉ mục được nhóm và 1.724 lần phân tách trên một chỉ mục khác, nhưng tôi đã không ghi lại khoảng thời gian xảy ra. Một tiếng rưỡi sau, tôi đo được 7.035 lần chia trang trên chỉ mục được nhóm.
Kế hoạch truy vấn tôi đã chụp trong trình lược tả trông như thế này:
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
Nhìn vào các truy vấn so với các dmv khác nhau, tôi thấy truy vấn đang chờ trên PAGEIOLATCH_EX với thời lượng 0 trên một trang trong bảng Khoảng không quảng cáo này. Tôi không thấy bất kỳ sự chờ đợi hay chặn trên ổ khóa.
Máy này có bộ nhớ khoảng 32 GB. Nó đang chạy SQL Server 2005 Standard Edition, mặc dù họ sẽ sớm nâng cấp lên 2008 R2 Enterprise Edition. Tôi không có số liệu về mức độ lớn của bảng kiểm kê về mức độ sử dụng đĩa, nhưng tôi có thể lấy số đó nếu cần. Đây là một trong những bảng lớn nhất trong hệ thống này.
Tôi đã chạy một truy vấn đối với sys.dm_io_virtual_file_stats và thấy ghi trung bình chờ đợi với tempdb lên tới 1,1 giây . Cơ sở dữ liệu trong đó bảng này được lưu trữ có thời gian chờ ghi trung bình là ~ 350 ms. Nhưng họ chỉ khởi động lại máy chủ của họ sau mỗi 6 tháng hoặc lâu hơn, vì vậy tôi không biết thông tin này có liên quan hay không. tempdb được trải rộng trên 4 tệp khác nhau Họ có 3 tệp khác nhau cho cơ sở dữ liệu chứa bảng Inventory.
Tại sao truy vấn này sẽ mất nhiều thời gian để CHỈ một vài hàng khi chạy với nhiều luồng khác nhau khi một INSERT duy nhất rất nhanh?
- CẬP NHẬT -
Dưới đây là số độ trễ trên mỗi ổ đĩa bao gồm cả byte đọc. Như bạn có thể thấy, hiệu suất tempdb là nghi vấn. Bảng Inventory có trong PDICompany_252_01.mdf, PDICompany_252_01_Second.ndf hoặc PDICompany_252_01_Third.ndf.
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