Trên một trong những khách hàng của chúng tôi, chúng tôi đã gặp một số vấn đề về hiệu suất trên ứng dụng của mình. Đây là một ứng dụng web .NET 3.5 đang tiêu thụ và cập nhật dữ liệu trên cơ sở dữ liệu SQL Server. Hiện tại môi trường sản xuất của chúng tôi bao gồm một máy Windows 2008 R2 làm mặt trước và cụm SQL Server 2008 R2 ở mặt sau. Ứng dụng của chúng tôi sử dụng COM + và MSDTC để kết nối với cơ sở dữ liệu.
Đây là những gì đang xảy ra: người dùng cuối của chúng tôi đôi khi phàn nàn về sự chậm chạp trong ứng dụng. Một số trang mất nhiều thời gian hơn để tải hơn dự kiến. Trong khi cố gắng tìm hiểu điều gì đang xảy ra, tôi đã tìm ra một số hành vi lạ ở phía cơ sở dữ liệu có thể là nguyên nhân dẫn đến sự suy giảm hiệu suất. Tôi nhận thấy rằng đôi khi có một số câu lệnh SQL mất nhiều thời gian hơn để chạy những gì sẽ được mong đợi. Tôi đã quản lý để xác định một số trong các tuyên bố này (chủ yếu là các yêu cầu của một số quy trình được lưu trữ trong ứng dụng của chúng tôi) bằng cách sử dụng dấu vết hồ sơ (với mẫu TSQL_Duration) để xác định các truy vấn chạy dài.
Vấn đề là khi tôi chạy các thủ tục được lưu trữ này trực tiếp trên cơ sở dữ liệu trên SQL Management Studio, đôi khi chúng mất nhiều thời gian (khoảng 7/8 giây), những lần khác chúng rất nhanh (dưới 1 giây). Tôi không biết tại sao điều này xảy ra và nó khiến tôi phát điên, vì máy SQL (4 lõi, 32 GB) không được sử dụng bởi bất kỳ ứng dụng nào khác và các truy vấn này sẽ không mất nhiều thời gian để chạy.
Không phải là một DBA hay một chuyên gia máy chủ SQL, tôi đã cố gắng xem xét một số thứ có thể giúp tôi hiểu vấn đề. Đây là các bước tôi đã thực hiện để thử và giải quyết vấn đề và những gì tôi đã tìm ra cho đến nay:
- Tất cả các mã TSQL được gọi bởi ứng dụng được viết trong các thủ tục được lưu trữ.
- Tôi đã xác định một số truy vấn chạy dài trên trình hồ sơ SQL Server, tuy nhiên khi tôi chạy những truy vấn này trên Management Studio, chúng sẽ mất nhiều thời gian để chạy (từ 4 đến 10 giây.) Hoặc chạy nhanh (dưới 1 giây). Tôi đang chạy các truy vấn chính xác giống nhau với cùng một dữ liệu được truyền trong các tham số. Các truy vấn này chủ yếu được lưu trữ các thủ tục với các câu lệnh chọn trong đó.
- Tôi đã thử nhìn vào số liệu thống kê về hàng đợi và hàng đợi để thử và tìm hiểu xem có các quy trình đang chờ trên một số tài nguyên không. Tôi đã chạy truy vấn sau đây:
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
Đây là những gì tôi phát hiện ra:
- Sau khi tôi đặt lại các số liệu thống kê bằng DBCC SQLPERF (khoảng 1 hoặc 2 giờ sau), các loại chờ tôi có nhiều nhất là SOS_SCHEDULER_YIELD và WRITELOG
- Theo thời gian (sau khoảng 1 ngày thực hiện), các loại chờ xảy ra nhiều nhất trên cơ sở dữ liệu là CXPACKET (67%) và OLEDB (17%), mặc dù thời gian chờ trung bình cho mỗi loại không dài. Tôi cũng nhận thấy rằng các câu lệnh chạy dài hơn được xác định trên SQL Profiler là các lệnh gọi đến các thủ tục được lưu trữ trả về nhiều hơn một tập kết quả (thường là 3). Có thể có một vấn đề paralellism ở đây? Có cách nào tôi có thể cố gắng xác định nếu đây là nguyên nhân của vấn đề?
- Tôi đã đọc ở đâu đó rằng OLEDB chờ đợi có thể được gây ra bởi các cuộc gọi đến tài nguyên OLEDB như các máy chủ được liên kết. Chúng tôi có một máy chủ được liên kết để kết nối với máy Dịch vụ lập chỉ mục (MSIDXS), tuy nhiên không có câu lệnh nào được xác định là chạy lâu sử dụng máy chủ được liên kết đó.
- Thời gian chờ trung bình cao hơn mà tôi có là dành cho loại chờ LCK_M_X (trung bình khoảng 1,5 giây), nhưng các loại chờ này không xảy ra rất thường xuyên so với các loại khác (ví dụ: 64 LCK_M_X chờ so với 10.823 CXPACKET chờ trong cùng khoảng thời gian ).
- Một điều tôi nhận thấy là dịch vụ MSDTC không được nhóm. Dịch vụ SQL Server được phân cụm nhưng không phải MSDTC. Có thể có một hit hiệu suất vì điều này? Chúng tôi đang sử dụng MSDTC vì ứng dụng của chúng tôi sử dụng Dịch vụ doanh nghiệp (DCOM) để truy cập cơ sở dữ liệu, nhưng các máy chủ không được chúng tôi cài đặt và định cấu hình, mà bởi khách hàng của chúng tôi.
Bất cứ ai có thể giúp tôi làm cho một số ý nghĩa hơn của dữ liệu này? Bất cứ ai có thể giúp tôi hiểu những gì có thể xảy ra? Có điều gì tôi có thể làm trên máy chủ để thử và tìm hiểu mọi thứ không? Tôi có nên nói chuyện với nhóm phát triển ứng dụng?
exec()
chức năng sẽ giải thích các hành vi quan sát. Trong trường hợp này, việc sử dụngsp_executesql
thường giải quyết các vấn đề với các câu lệnh SQL động.