TÙY CHỌN (RECOMPILE) luôn nhanh hơn; Tại sao?


169

Tôi đã gặp một tình huống kỳ lạ khi nối thêm OPTION (RECOMPILE)vào truy vấn của mình khiến nó chạy trong nửa giây, trong khi bỏ qua nó khiến truy vấn mất hơn năm phút.

Đây là trường hợp khi truy vấn được thực thi từ Trình phân tích truy vấn hoặc từ chương trình C # của tôi thông qua SqlCommand.ExecuteReader(). Gọi (hoặc không gọi) DBCC FREEPROCCACHEhoặc DBCC dropcleanbufferskhông có sự khác biệt; Kết quả truy vấn luôn được trả về ngay lập tức với OPTION (RECOMPILE)và lớn hơn năm phút mà không có nó. Truy vấn luôn được gọi với cùng tham số [vì mục đích kiểm tra này].

Tôi đang sử dụng SQL Server 2008.

Tôi khá thoải mái với việc viết SQL nhưng chưa bao giờ sử dụng một OPTIONlệnh trong truy vấn trước đây và không quen thuộc với toàn bộ khái niệm về bộ đệm kế hoạch cho đến khi quét các bài đăng trên diễn đàn này. Sự hiểu biết của tôi từ các bài viết OPTION (RECOMPILE)là một hoạt động đắt tiền. Nó rõ ràng tạo ra một chiến lược tra cứu mới cho truy vấn. Vậy tại sao sau đó, các truy vấn tiếp theo mà bỏ qua OPTION (RECOMPILE)rất chậm? Không nên sử dụng các truy vấn tiếp theo bằng cách sử dụng chiến lược tra cứu được tính trên cuộc gọi trước đó có bao gồm gợi ý biên dịch lại không?

Có phải là rất bất thường khi có một truy vấn yêu cầu gợi ý biên dịch lại trên mỗi cuộc gọi không?

Xin lỗi cho câu hỏi cấp độ đầu vào nhưng tôi thực sự không thể thực hiện đầu hoặc đuôi của vấn đề này.

CẬP NHẬT: Tôi đã được yêu cầu gửi truy vấn ...

select acctNo,min(date) earliestDate 
from( 
    select acctNo,tradeDate as date 
    from datafeed_trans 
    where feedid=@feedID and feedDate=@feedDate 

    union 

    select acctNo,feedDate as date 
    from datafeed_money 
    where feedid=@feedID and feedDate=@feedDate 

    union 

    select acctNo,feedDate as date 
    from datafeed_jnl 
    where feedid=@feedID and feedDate=@feedDate 
)t1 
group by t1.acctNo
OPTION(RECOMPILE)

Khi chạy thử nghiệm từ Trình phân tích truy vấn, tôi thêm vào các dòng sau:

declare @feedID int
select @feedID=20

declare @feedDate datetime
select @feedDate='1/2/2009'

Khi gọi nó từ chương trình C # của tôi, các tham số được truyền qua thuộc SqlCommand.Parameterstính.

Đối với mục đích của cuộc thảo luận này, bạn có thể giả sử rằng các tham số không bao giờ thay đổi để chúng tôi có thể loại trừ tham số phụ tối ưu có mùi là nguyên nhân.


3
Các tham số cho truy vấn là gì? Kiểm tra bài viết này ra. blog.msdn.com/b/turgays/archive/2013/09/10/ về cơ bản, SQL cố gắng tạo kế hoạch truy vấn dựa trên các tham số khi lần đầu tiên được biên dịch. Nó có thể tạo ra một kế hoạch không tối ưu khi bạn bắt đầu chuyển các thông số khác nhau, có thể thực tế hơn
Sparky

3
Là truy vấn ngắn gọn đủ để liệt kê ở đây? Tôi nghĩ Sparky là chính xác và có lẽ nó liên quan đến việc đánh hơi thông số, tôi đã gặp một vấn đề tương tự làm tôi bối rối cho đến khi đọc bài viết tuyệt vời này: sommarskog.se/query-plan-mysteries.html
Chris

1
Nhưng trong trường hợp này (vì lợi ích của bài kiểm tra này) tôi luôn truyền các thông số giống nhau. Không có ứng dụng nào khác có thể lẻn vào và gọi truy vấn bằng các thông số khác. Cảm ơn các bài viết. Sẽ xem lại.
Chad Decker

2
Điều này có thể xảy ra vì nó đánh hơi các giá trị của các tham số và biến hoặc bởi vì nó đơn giản hóa hơn. Ví dụ về các đơn giản hóa lớn hơn sẽ được thu gọn X = @X OR @X IS NULLlại X=@Xvà thực hiện tìm kiếm Xem tại đây hoặc đẩy các vị từ xuống xa hơn so với chế độ xem với các chức năng của cửa sổ
Martin Smith

3
Sau khi chỉnh sửa, ví dụ Trình phân tích truy vấn của bạn sử dụng các biến, không phải tham số. giá trị của những thứ đó không bao giờ được đánh hơi ngoại trừ với RECOMPILE. Trong mọi trường hợp, nắm bắt các kế hoạch thực hiện và xem xét sự khác biệt.
Martin Smith

Câu trả lời:


157

Có những lúc sử dụng OPTION(RECOMPILE)có ý nghĩa. Theo kinh nghiệm của tôi, lần duy nhất đây là một tùy chọn khả thi là khi bạn đang sử dụng SQL động. Trước khi bạn khám phá liệu điều này có ý nghĩa trong tình huống của bạn, tôi sẽ khuyên bạn nên xây dựng lại số liệu thống kê của mình. Điều này có thể được thực hiện bằng cách chạy như sau:

EXEC sp_updatestats

Và sau đó tạo lại kế hoạch thực hiện của bạn. Điều này sẽ đảm bảo rằng khi kế hoạch thực hiện của bạn được tạo, nó sẽ sử dụng thông tin mới nhất.

Thêm OPTION(RECOMPILE)xây dựng lại kế hoạch thực hiện mỗi khi truy vấn của bạn thực thi. Tôi chưa bao giờ nghe mô tả như creates a new lookup strategyvậy nhưng có lẽ chúng ta chỉ đang sử dụng các thuật ngữ khác nhau cho cùng một điều.

Khi một thủ tục được lưu trữ được tạo ra (tôi nghi ngờ bạn đang gọi ad-hoc sql từ .NET nhưng nếu bạn đang sử dụng một truy vấn được tham số hóa thì đây sẽ là một cuộc gọi Proc được lưu trữ ) SQL Server cố gắng xác định kế hoạch thực hiện hiệu quả nhất cho truy vấn này dựa trên dữ liệu trong cơ sở dữ liệu của bạn và các tham số được truyền vào ( đánh hơi tham số ), và sau đó lưu trữ kế hoạch này. Điều này có nghĩa là nếu bạn tạo truy vấn trong đó có 10 bản ghi trong cơ sở dữ liệu của bạn và sau đó thực hiện nó khi có 100.000.000 bản ghi thì kế hoạch thực hiện được lưu trong bộ nhớ cache có thể không còn hiệu quả nhất.

Tóm lại - Tôi không thấy bất kỳ lý do nào OPTION(RECOMPILE)sẽ là một lợi ích ở đây. Tôi nghi ngờ bạn chỉ cần cập nhật số liệu thống kê và kế hoạch thực hiện của bạn. Thống kê xây dựng lại có thể là một phần thiết yếu của công việc DBA tùy thuộc vào tình huống của bạn. Nếu bạn vẫn gặp sự cố sau khi cập nhật số liệu thống kê của mình, tôi khuyên bạn nên đăng cả hai kế hoạch thực hiện.

Và để trả lời câu hỏi của bạn - vâng, tôi sẽ nói rằng điều rất bất thường đối với tùy chọn tốt nhất của bạn là biên dịch lại kế hoạch thực hiện mỗi khi bạn thực hiện truy vấn.


22
Vâng, sp_updatestats đã lừa. Bạn đánh vào đầu đinh khi bạn đề cập đến một truy vấn ban đầu chạy trên một bảng có 10 bản ghi và bây giờ bảng có hàng triệu bản ghi. Đó là trường hợp của tôi chính xác. Tôi đã không đề cập đến nó trong bài viết bởi vì tôi không nghĩ nó quan trọng. Thứ hấp dẫn. Cảm ơn một lần nữa.
Chad Decker

3
Đó là cách duy nhất tôi thấy để làm việc với các biến bảng, vì SQL luôn nghĩ rằng có một hàng duy nhất. Khi nó chứa hàng ngàn hàng, nó trở thành một vấn đề.
Alex Zhukovskiy

4
Một chi tiết thú vị: cập nhật thống kê hoàn toàn vô hiệu hóa tất cả các gói được lưu trong bộ nhớ cache sử dụng các thống kê này, nhưng chỉ khi số liệu thống kê thực sự thay đổi sau hành động cập nhật . Vì vậy, đối với các bảng chỉ đọc sai lệch cao, có vẻ như một OPTION (RECOMPILE)giải pháp rõ ràng có thể là giải pháp duy nhất.
Groo

141

Thông thường khi có sự khác biệt lớn từ chạy đến chạy truy vấn tôi thấy rằng đó thường là một trong 5 vấn đề.

  1. SỐ LIỆU THỐNG KÊ- Thống kê đã hết hạn. Một cơ sở dữ liệu lưu trữ số liệu thống kê về phạm vi và phân phối các loại giá trị trong các cột khác nhau trên các bảng và chỉ mục. Điều này giúp công cụ truy vấn phát triển một "Kế hoạch" tấn công về cách nó sẽ thực hiện truy vấn, ví dụ như loại phương thức mà nó sẽ sử dụng để khớp các khóa giữa các bảng bằng cách sử dụng hàm băm hoặc xem qua toàn bộ tập hợp. Bạn có thể gọi Cập nhật thống kê trên toàn bộ cơ sở dữ liệu hoặc chỉ một số bảng hoặc chỉ mục nhất định. Điều này làm chậm truy vấn từ lần chạy này sang lần chạy khác vì khi hết số liệu thống kê, có thể kế hoạch truy vấn không tối ưu cho dữ liệu mới được chèn hoặc thay đổi cho cùng một truy vấn (sẽ giải thích thêm sau). Có thể không đúng khi cập nhật Thống kê ngay lập tức trên cơ sở dữ liệu Sản xuất vì sẽ có một số chi phí, chậm và chậm tùy thuộc vào lượng dữ liệu cần lấy mẫu. Bạn cũng có thể chọn sử dụng Quét toàn bộ hoặc Lấy mẫu để cập nhật Thống kê. Nếu bạn nhìn vào Kế hoạch truy vấn, thì bạn cũng có thể xem số liệu thống kê về các Chỉ mục được sử dụng như vậy bằng cách sử dụng lệnhDBCC SHOW_STATISTICS (tablename, tên chỉ mục) . Điều này sẽ cho bạn thấy sự phân phối và phạm vi của các khóa mà kế hoạch truy vấn đang sử dụng để dựa trên cách tiếp cận của nó.

  2. PARAMETER SNIFFING - Gói truy vấn được lưu trong bộ nhớ cache không tối ưu cho các tham số cụ thể mà bạn đang truyền vào, mặc dù bản thân truy vấn không thay đổi. Ví dụ: nếu bạn truyền vào một tham số chỉ truy xuất 10 trên 1.000.000 hàng thì kế hoạch truy vấn được tạo có thể sử dụng Hash Join, tuy nhiên nếu tham số bạn truyền vào sẽ sử dụng 750.000 trong số 1.000.000 hàng, kế hoạch được tạo có thể là một quét chỉ mục hoặc quét bảng. Trong tình huống như vậy, bạn có thể yêu cầu câu lệnh SQL sử dụng tùy chọn TÙY CHỌN (RECOMPILE) hoặc SP để sử dụng VỚI RECOMPILE. Để nói với Công cụ, đây là "Kế hoạch sử dụng một lần" và không sử dụng Gói lưu trữ có khả năng không áp dụng. Không có quy tắc về cách đưa ra quyết định này, nó phụ thuộc vào việc biết cách sử dụng truy vấn của người dùng.

  3. INDEXES - Có thể truy vấn không thay đổi, nhưng thay đổi ở nơi khác như xóa chỉ mục rất hữu ích đã làm chậm truy vấn.

  4. ROWS THAY ĐỔI - Các hàng bạn đang truy vấn thay đổi mạnh mẽ từ cuộc gọi sang cuộc gọi. Thông thường số liệu thống kê được tự động cập nhật trong những trường hợp này. Tuy nhiên, nếu bạn đang xây dựng SQL động hoặc gọi SQL trong một vòng lặp chặt chẽ, có khả năng bạn đang sử dụng Kế hoạch truy vấn lỗi thời dựa trên số lượng hàng hoặc số liệu thống kê sai. Một lần nữa trong trường hợp này TÙY CHỌN (RECOMPILE) là hữu ích.

  5. LOGIC Đó là Logic, truy vấn của bạn không còn hiệu quả, nó ổn đối với một số lượng nhỏ hàng, nhưng không còn tỷ lệ. Điều này thường liên quan đến phân tích chuyên sâu hơn về Kế hoạch truy vấn. Ví dụ: bạn không còn có thể thực hiện hàng loạt, nhưng phải Chunk mọi thứ và thực hiện các Cam kết nhỏ hơn hoặc Sản phẩm chéo của bạn vẫn ổn cho một bộ nhỏ hơn nhưng hiện chiếm CPU và Bộ nhớ vì nó có tỷ lệ lớn hơn, điều này cũng có thể đúng bằng cách sử dụng DISTINCT, bạn đang gọi một hàm cho mỗi hàng, các kết quả khớp chính của bạn không sử dụng chỉ mục vì chuyển đổi loại CASTING hoặc NULLS hoặc các hàm ... Có quá nhiều khả năng ở đây.

Nói chung khi bạn viết một truy vấn, bạn sẽ có một bức tranh tinh thần về cách dữ liệu nhất định được phân phối trong bảng của bạn. Ví dụ, một cột có thể có số lượng giá trị khác nhau được phân bổ đều hoặc có thể bị lệch, 80% thời gian có một bộ giá trị cụ thể, cho dù phân phối sẽ thay đổi thường xuyên theo thời gian hay khá tĩnh. Điều này sẽ cho bạn ý tưởng tốt hơn về cách xây dựng một truy vấn hiệu quả. Nhưng cũng khi gỡ lỗi hiệu năng truy vấn có cơ sở để xây dựng một giả thuyết về lý do tại sao nó chậm hoặc không hiệu quả.


2
cảm ơn bạn. Đây là thông tin tuyệt vời. Tôi sẽ không thể hiểu câu trả lời của bạn khi ban đầu tôi đăng câu hỏi của mình nhưng bây giờ nó hoàn toàn có ý nghĩa với tôi.
Chad Decker

3
PARAMETER SNIFFING cho đến nay là nguyên nhân lớn nhất cho sự tồn tại của tôi. Tôi thậm chí không biết về lệnh này cho đến khi một câu hỏi phỏng vấn thất bại. Giải pháp của tôi để đánh hơi tham số luôn là băm các giá trị tham số và nối thêm "AND {hash} = {hash}" để sql luôn khác nhau cho các giá trị khác nhau. Một hack, nhưng nó đã làm việc.
Jeremy Boyd

27

Để thêm vào danh sách xuất sắc (được đưa ra bởi @CodeCowboyOrg) về các tình huống trong đó TÙY CHỌN (RECOMPILE) có thể rất hữu ích,

  1. Bảng biến . Khi bạn đang sử dụng biến bảng, sẽ không có bất kỳ số liệu thống kê dựng sẵn nào cho biến bảng, thường dẫn đến sự khác biệt lớn giữa các hàng ước tính và thực tế trong kế hoạch truy vấn. Sử dụng TÙY CHỌN (RECOMPILE) cho các truy vấn với các biến của bảng cho phép tạo kế hoạch truy vấn có ước tính tốt hơn nhiều về các số hàng liên quan. Tôi đã sử dụng một biến đặc biệt quan trọng của một biến bảng không thể sử dụng được và tôi sẽ từ bỏ cho đến khi tôi thêm TÙY CHỌN (RECOMPILE). Thời gian chạy từ vài giờ đến chỉ vài phút. Điều đó có thể là bất thường, nhưng trong mọi trường hợp, nếu bạn đang sử dụng các biến của bảng và làm việc để tối ưu hóa, thì cũng đáng để xem liệu TÙY CHỌN (RECOMPILE) có tạo ra sự khác biệt hay không.

1
Tôi có một truy vấn với 5 biến bảng. Trên máy của tôi, nó thực thi trong hơn nửa giờ. Trên máy của đồng nghiệp của tôi, nó thực thi trong <1 giây. Các máy có phần cứng tương tự và cùng phiên bản SQL Server. Nếu cả hai chúng ta đều thêm TÙY CHỌN (RECOMPILE) thì nó sẽ thực thi trong 2 giây trên cả hai máy. Trong mọi trường hợp, kiểm tra thực hiện được thực hiện trong SSMS. Điều gì có thể gây ra sự khác biệt này?
Adam

1
Bạn có thể so sánh kế hoạch thực hiện cho nó trên máy của bạn và trên máy của đồng nghiệp mà không có Tùy chọn (biên dịch lại) không? Điều đó có thể cho thấy nguồn gốc của sự khác biệt.
DWright

1
cho các bảng tạm thời, đó là một tình huống tương tự?
Muflix

1
@muflix: câu hỏi hay. Tôi không tin rằng hiệu ứng là như nhau đối với các bảng tạm thời, vì chúng có số liệu thống kê và công cụ sẽ thực hiện các lựa chọn biên dịch lại tự động như đối với các bảng khác, tôi tin (nhưng không chắc chắn). Có lẽ ai đó biết với sự chắc chắn lớn hơn.
DWright

2
Số liệu thống kê trong các bảng tạm thời không được tự động cập nhật hoặc biên dịch lại, vì vậy lập trình viên cần phải làm điều đó.
J. Michael Wuerth

1

Những hành động đầu tiên trước khi điều chỉnh truy vấn là chống phân mảnh / xây dựng lại các chỉ mục và thống kê, ngược lại bạn đang lãng phí thời gian của mình.

Bạn phải kiểm tra kế hoạch thực hiện để xem nó có ổn định không (giống như khi bạn thay đổi các tham số), nếu không, bạn có thể phải tạo một chỉ mục bìa (trong trường hợp này cho mỗi bảng) (biết rằng hệ thống bạn có thể tạo một bảng cũng hữu ích cho các truy vấn khác).

làm ví dụ: tạo chỉ mục idx01_datafeed_trans Trên datafeed_trans (feedid, feedDate) INCLUDE (acctNo, TradeDate)

nếu kế hoạch ổn định hoặc bạn có thể ổn định nó, bạn có thể thực hiện câu với sp_executesql ('câu sql') để lưu và sử dụng một kế hoạch thực hiện cố định.

nếu kế hoạch không ổn định, bạn phải sử dụng câu lệnh đặc biệt hoặc EXEC ('câu sql') để đánh giá và tạo kế hoạch thực hiện mỗi lần. (hoặc một thủ tục được lưu trữ "với biên dịch lại").

Hy vọng nó giúp.


1

Necroing câu hỏi này nhưng có một lời giải thích mà dường như không ai đã xem xét.

THỐNG KÊ - Số liệu thống kê không có sẵn hoặc gây hiểu nhầm

Nếu tất cả những điều sau đây là đúng:

  1. Các cột feedid và feedDate có khả năng tương quan cao (ví dụ: id nguồn cấp dữ liệu cụ thể hơn ngày cấp dữ liệu và tham số ngày là thông tin dự phòng).
  2. Không có chỉ mục với cả hai cột là cột liên tiếp.
  3. Không có số liệu thống kê được tạo thủ công bao gồm cả hai cột này.

Sau đó, máy chủ sql có thể không chính xác giả định rằng các cột không tương thích, dẫn đến ước tính cardinality thấp hơn dự kiến ​​để áp dụng cả hai hạn chế và kế hoạch thực hiện kém được chọn. Cách khắc phục trong trường hợp này là tạo một đối tượng thống kê liên kết hai cột, đây không phải là một hoạt động đắt tiền.

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.