Tham số Sniffing vs VARIABLES vs Recompile vs TỐI ƯU HÓA CHO UNKNOWN


40

Vì vậy, chúng tôi đã có một Proc chạy dài gây ra vấn đề sáng nay (30 giây + thời gian chạy). Chúng tôi quyết định kiểm tra xem liệu thông số đánh hơi có đáng trách không. Vì vậy, chúng tôi viết lại Proc và đặt các tham số đến thành các biến để đánh bại tham số đánh hơi. Một cách tiếp cận cố gắng / đúng. Bam, thời gian truy vấn được cải thiện (dưới 1 giây). Khi nhìn vào kế hoạch truy vấn, các cải tiến đã được tìm thấy trong một chỉ mục mà bản gốc không sử dụng.

Chỉ để xác minh rằng chúng tôi đã không nhận được kết quả dương tính giả, chúng tôi đã thực hiện một chương trình miễn phí dbcc trên Proc và reran ban đầu để xem kết quả được cải thiện có giống như vậy không. Nhưng, thật ngạc nhiên, các Proc ban đầu vẫn chạy chậm. Chúng tôi đã thử lại với MỘT KHUYẾN NGHỊ, vẫn chậm (chúng tôi đã thử biên dịch lại cuộc gọi đến Proc và bên trong chính nó). Chúng tôi thậm chí đã khởi động lại máy chủ (hộp dev rõ ràng).

Vì vậy, câu hỏi của tôi là ... làm thế nào để đánh hơi tham số có thể bị đổ lỗi khi chúng ta nhận được cùng một truy vấn chậm trên bộ đệm trống ... không nên có bất kỳ tham số nào để snif ???

Có phải chúng ta thay vì bị ảnh hưởng bởi các thống kê bảng không liên quan đến bộ đệm của kế hoạch. Và nếu vậy, tại sao việc thiết lập các tham số đến thành biến sẽ giúp ??

Trong thử nghiệm tiếp theo, chúng tôi cũng phát hiện ra rằng việc chèn TÙY CHỌN (TỐI ƯU HÓA CHO UNKNOWN) trên các bộ phận bên trong của DI DI có được kế hoạch cải thiện như mong đợi.

Vì vậy, một số bạn thông minh hơn tôi, bạn có thể đưa ra một số manh mối về những gì đang diễn ra đằng sau hậu trường để tạo ra loại kết quả này không?

Một lưu ý khác, kế hoạch chậm cũng bị hủy bỏ sớm với lý do GoodEnoughPlanFoundtrong khi kế hoạch nhanh không có lý do hủy bỏ sớm trong kế hoạch thực tế.

Tóm tắt

  • Tạo các biến trong số các tham số đến (1 giây)
  • với biên dịch lại (30+ giây)
  • dbcc freeproccache (hơn 30 giây)
  • TÙY CHỌN (TỐI ƯU HÓA CHO UKNOWN) (1 giây)

CẬP NHẬT:

Xem kế hoạch thực hiện chậm tại đây: https://www.dropbox.com/s/cmx2lrsea8q8mr6/plan_slow.xml

Xem kế hoạch thực hiện nhanh tại đây: https://www.dropbox.com/s/b28x6a01w7dxsed/plan_fast.xml

Lưu ý: bảng, lược đồ, tên đối tượng thay đổi vì lý do bảo mật.

Câu trả lời:


43

Truy vấn là

SELECT SUM(Amount) AS SummaryTotal
FROM   PDetail WITH(NOLOCK)
WHERE  ClientID = @merchid
       AND PostedDate BETWEEN @datebegin AND @dateend 

Bảng chứa 103.129.000 hàng.

Gói nhanh được tra cứu bởi ClientId với một vị từ còn lại vào ngày nhưng cần thực hiện 96 lần tra cứu để truy xuất Amount. Các <ParameterList>phần trong kế hoạch thực hiện như sau.

        <ParameterList>
          <ColumnReference Column="@dateend" 
                           ParameterRuntimeValue="'2013-02-01 23:59:00.000'" />
          <ColumnReference Column="@datebegin" 
                           ParameterRuntimeValue="'2013-01-01 00:00:00.000'" />
          <ColumnReference Column="@merchid" 
                           ParameterRuntimeValue="(78155)" />
        </ParameterList>

Kế hoạch chậm tìm kiếm theo ngày và có tra cứu để đánh giá vị từ còn lại trên ClientId và để lấy số tiền (Ước tính 1 so với thực tế 7,388,383). Các <ParameterList>phần là

        <ParameterList>
          <ColumnReference Column="@EndDate" 
                           ParameterCompiledValue="'2013-02-01 23:59:00.000'" 
                           ParameterRuntimeValue="'2013-02-01 23:59:00.000'" />
          <ColumnReference Column="@BeginDate" 
                           ParameterCompiledValue="'2013-01-01 00:00:00.000'"               
                           ParameterRuntimeValue="'2013-01-01 00:00:00.000'" />
          <ColumnReference Column="@ClientID" 
                           ParameterCompiledValue="(78155)" 
                           ParameterRuntimeValue="(78155)" />
        </ParameterList>

Trong trường hợp thứ hai ParameterCompiledValuenày không trống. SQL Server đã đánh hơi thành công các giá trị được sử dụng trong truy vấn.

Cuốn sách "Xử lý sự cố thực tế SQL Server 2005" có nội dung này để nói về việc sử dụng các biến cục bộ

Sử dụng các biến cục bộ để đánh bại tham số đánh hơi là một mẹo khá phổ biến, nhưng các gợi ý OPTION (RECOMPILE)OPTION (OPTIMIZE FOR)... nói chung là các giải pháp thanh lịch hơn và ít rủi ro hơn


Ghi chú

Trong SQL Server 2005, biên dịch mức câu lệnh cho phép biên dịch một câu lệnh riêng lẻ trong một thủ tục được lưu trữ được hoãn lại cho đến trước khi thực hiện truy vấn đầu tiên. Đến lúc đó giá trị của biến cục bộ sẽ được biết đến. Về mặt lý thuyết, SQL Server có thể lợi dụng điều này để đánh hơi các giá trị biến cục bộ giống như cách nó đánh hơi các tham số. Tuy nhiên, vì thông thường sử dụng các biến cục bộ để đánh bại tham số đánh hơi trong SQL Server 7.0 và SQL Server 2000+, việc đánh hơi các biến cục bộ không được kích hoạt trong SQL Server 2005. Nó có thể được kích hoạt trong bản phát hành SQL Server trong tương lai. lý do để sử dụng một trong các tùy chọn khác được nêu trong chương này nếu bạn có lựa chọn.


Từ một thử nghiệm nhanh, kết thúc này, hành vi được mô tả ở trên vẫn giống nhau trong năm 2008 và 2012 và các biến không được đánh hơi để biên dịch bị trì hoãn mà chỉ khi sử dụng một OPTION RECOMPILEgợi ý rõ ràng .

DECLARE @N INT = 0

CREATE TABLE #T ( I INT );

/*Reference to #T means this statement is subject to deferred compile*/
SELECT *
FROM   master..spt_values
WHERE  number = @N
       AND EXISTS(SELECT COUNT(*) FROM #T)

SELECT *
FROM   master..spt_values
WHERE  number = @N
OPTION (RECOMPILE)

DROP TABLE #T 

Mặc dù biên dịch bị hoãn, biến không được đánh hơi và số lượng hàng ước tính là không chính xác

Ước tính so với thực tế

Vì vậy, tôi giả định rằng kế hoạch chậm liên quan đến một phiên bản tham số của truy vấn.

Giá trị ParameterCompiledValuenày bằng ParameterRuntimeValuevới tất cả các tham số, vì vậy đây không phải là đánh hơi tham số điển hình (trong đó kế hoạch được biên dịch cho một bộ giá trị sau đó chạy cho một bộ giá trị khác).

Vấn đề là kế hoạch được biên dịch cho các giá trị tham số chính xác là không phù hợp.

Bạn có thể gặp phải vấn đề với ngày tăng dần được mô tả ở đâyđây . Đối với một bảng có 100 triệu hàng, bạn cần chèn (hoặc sửa đổi) 20 triệu trước khi SQL Server sẽ tự động cập nhật số liệu thống kê cho bạn. Có vẻ như lần trước chúng được cập nhật các hàng không khớp với phạm vi ngày trong truy vấn nhưng hiện có 7 triệu.

Bạn có thể lên lịch cập nhật thống kê thường xuyên hơn, xem xét các cờ theo dõi 2389 - 90hoặc sử dụng OPTIMIZE FOR UKNOWNđể nó chỉ dựa vào dự đoán thay vì có thể sử dụng số liệu thống kê hiện đang gây hiểu lầm trên datetimecột.

Điều này có thể không cần thiết trong phiên bản tiếp theo của SQL Server (sau năm 2012). Một mục Kết nối có liên quan chứa phản hồi hấp dẫn

Được đăng bởi Microsoft vào ngày 28/8/2012 lúc 1:35 PM
Chúng tôi đã thực hiện nâng cao ước tính cardinality cho phiên bản chính tiếp theo mà về cơ bản là khắc phục điều này. Hãy theo dõi để biết chi tiết khi xem trước của chúng tôi đi ra. Eric

Cải tiến năm 2014 này được xem xét bởi Benjamin Nevarez cho đến cuối bài viết:

Cái nhìn đầu tiên về Công cụ ước tính số lượng máy chủ SQL mới .

Có vẻ như công cụ ước tính cardinality mới sẽ quay trở lại và sử dụng mật độ trung bình trong trường hợp này thay vì đưa ra ước tính 1 hàng.

Một số chi tiết bổ sung về công cụ ước tính cardinality 2014 và vấn đề chính tăng dần ở đây:

Chức năng mới trong SQL Server 2014 - Phần 2 - Ước tính Cardinality mới


29

Vì vậy, câu hỏi của tôi là ... làm thế nào để đánh hơi tham số có thể bị đổ lỗi khi chúng ta nhận được cùng một truy vấn chậm trên bộ đệm trống ... không nên có bất kỳ tham số nào để đánh hơi?

Khi SQL Server biên dịch một truy vấn có chứa các giá trị tham số, nó sẽ đánh hơi các giá trị cụ thể của các tham số đó để ước tính số lượng thẻ (số hàng). Trong trường hợp của bạn, các giá trị đặc biệt của @BeginDate, @EndDate@ClientIDđược sử dụng khi lựa chọn một kế hoạch thực hiện. Bạn có thể tìm thêm chi tiết về thông số đánh hơi ở đâyđây . Tôi đang cung cấp các liên kết nền này bởi vì câu hỏi trên khiến tôi nghĩ rằng khái niệm này hoàn toàn được hiểu hiện tại - luôn có các giá trị tham số để đánh hơi khi một kế hoạch được biên dịch.

Dù sao, đó là tất cả bên cạnh điểm, bởi vì đánh hơi thông số không phải là vấn đề ở đây như Martin Smith đã chỉ ra. Tại thời điểm truy vấn chậm được biên dịch, số liệu thống kê cho thấy không có hàng nào cho các giá trị đánh hơi của @BeginDate@EndDate:

Kế hoạch chậm đánh hơi giá trị

Các giá trị đánh hơi là rất gần đây, cho thấy vấn đề chính tăng dần mà Martin đề cập. Bởi vì chỉ mục tìm kiếm vào các ngày được ước tính chỉ trả về một hàng duy nhất, trình tối ưu hóa chọn một kế hoạch đẩy vị từ vào ClientIDtoán tử Tra cứu khóa làm phần dư.

Ước tính hàng đơn cũng là lý do trình tối ưu hóa ngừng tìm kiếm các kế hoạch tốt hơn, trả về một thông báo Tìm thấy Kế hoạch Đủ tốt. Tổng chi phí ước tính của kế hoạch chậm với ước tính hàng đơn chỉ là 0,013136 đơn vị chi phí, vì vậy không có điểm nào cố gắng tìm kiếm bất cứ điều gì tốt hơn. Tất nhiên, ngoại trừ, việc tìm kiếm thực sự trả về 7.388.383 hàng chứ không phải một hàng, gây ra cùng số lượng Tra cứu chính.

Thống kê có thể khó để cập nhật và hữu ích trên các bảng lớn và phân vùng giới thiệu các thách thức của riêng nó trong vấn đề đó. Bản thân tôi đã không thành công đặc biệt với cờ theo dõi 2389 và 2390, nhưng bạn có thể thử nghiệm chúng. Các bản dựng gần đây hơn của SQL Server (R2 SP1 trở lên) có sẵn các bản cập nhật thống kê động , nhưng bản cập nhật thống kê theo phân vùng này vẫn chưa được triển khai. Trong thời gian này, bạn có thể muốn lên lịch cập nhật thống kê thủ công bất cứ khi nào bạn thực hiện các thay đổi quan trọng đối với bảng này.

Đối với truy vấn cụ thể này, tôi sẽ suy nghĩ về việc triển khai chỉ mục được trình tối ưu hóa đề xuất trong quá trình biên dịch kế hoạch truy vấn nhanh:

/*
The Query Processor estimates that implementing the following index could improve
the query cost by 98.8091%.

WARNING: This is only an estimate, and the Query Processor is making this 
recommendation based solely upon analysis of this specific query.
It has not considered the resulting index size, or its workload-wide impact,
including its impact on INSERT, UPDATE, DELETE performance.
These factors should be taken into account before creating this index.
*/
CREATE NONCLUSTERED INDEX [<Name of Missing Index>]
ON [dbo].[PDetail] ([ClientID],[PostedDate])
INCLUDE ([Amount]);

Chỉ mục phải được căn chỉnh theo phân vùng, với một ON PartitionSchemeName (PostedDate)mệnh đề, nhưng điểm quan trọng là việc cung cấp đường dẫn truy cập dữ liệu rõ ràng tốt nhất sẽ giúp trình tối ưu hóa tránh các lựa chọn kế hoạch kém, mà không cần dùng đến OPTIMIZE FOR UNKNOWNgợi ý hoặc cách giải quyết lỗi thời như sử dụng biến cục bộ.

Với chỉ mục được cải thiện, Tra cứu khóa để truy xuất Amountcột sẽ bị loại bỏ, bộ xử lý truy vấn vẫn có thể thực hiện loại bỏ phân vùng động và sử dụng tìm kiếm để tìm ClientIDphạm vi cụ thể và ngày.


Ước gì tôi có thể đánh dấu hai câu trả lời là đúng, nhưng một lần nữa, cảm ơn vì thông tin bổ sung - rất mang tính hướng dẫn.
RThomas

1
Đã một vài năm kể từ khi tôi đăng bài này ... nhưng tôi chỉ muốn cho bạn biết. Tôi vẫn sử dụng thuật ngữ "hiểu một cách không hoàn hảo" tất cả thời gian kỳ dị, và tôi luôn nghĩ về Paul White khi tôi làm. Khiến tôi cười thầm mỗi lần.
RThomas

0

Tôi có cùng một vấn đề chính xác khi một thủ tục được lưu trữ trở nên chậm OPTIMIZE FOR UNKNOWNvà các RECOMPILEgợi ý truy vấn đã giải quyết sự chậm chạp và tăng tốc thời gian thực hiện. Tuy nhiên, hai phương pháp sau không ảnh hưởng đến sự chậm chạp của thủ tục được lưu trữ: (i) Xóa bộ đệm (ii) bằng cách sử dụng VỚI RECOMPILE. Vì vậy, như bạn đã nói, nó không thực sự đánh hơi thông số.

Cờ dấu vết 2389 và 2390 cũng không giúp được gì. Chỉ cần cập nhật số liệu thống kê ( EXEC sp_updatestats) đã làm điều đó cho tôi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.