Lỗi hiệu năng chỉ mục thời gian SQL Server 2008


11

Chúng tôi đang sử dụng SQL Server 2008 R2 và có một bảng rất lớn (100M + hàng) với chỉ mục id chính và một datetimecột có chỉ mục không bao gồm. Chúng tôi đang thấy một số hành vi máy khách / máy chủ rất bất thường dựa trên việc sử dụng một order bymệnh đề cụ thể trên cột datetime được lập chỉ mục .

Tôi đã đọc qua bài viết sau: /programming/1716798/sql-server-2008-ordering-by-datetime-is-too-slow nhưng có nhiều điều xảy ra với máy khách / máy chủ hơn là bắt đầu mô tả ở đây

Nếu chúng tôi chạy truy vấn sau (được chỉnh sửa để bảo vệ một số nội dung):

select * 
from [big table] 
where serial_number = [some number] 
order by test_date desc

Truy vấn hết thời gian mỗi lần. Trong SQL Server Profiler, truy vấn được thực hiện trông như thế này với máy chủ:

exec sp_cursorprepexec @p1 output,@p2 output,NULL,N'select * .....

Bây giờ nếu bạn sửa đổi truy vấn thành, hãy nói điều này:

declare @temp int;
select * from [big table] 
where serial_number = [some number] 
order by test_date desc

Trình cấu hình máy chủ SQL hiển thị truy vấn được thực hiện giống như máy chủ này và nó hoạt động ngay lập tức:

exec sp_prepexec @p1 output, NULL, N'declare @temp int;select * from .....

Như một vấn đề thực tế, bạn thậm chí có thể đặt một bình luận trống ('-;') thay vì một tuyên bố tuyên bố không sử dụng và nhận được kết quả tương tự. Vì vậy, ban đầu chúng tôi đã chỉ đến bộ xử lý trước sp là nguyên nhân gốc rễ của vấn đề này, nhưng nếu bạn làm điều này:

select * 
from [big table] 
where serial_number = [some number] 
order by Cast(test_date as smalldatetime) desc

Nó cũng hoạt động ngay lập tức (bạn có thể sử dụng nó như bất kỳ datetimeloại nào khác ), trả về kết quả sau mili giây. Và trình hồ sơ hiển thị yêu cầu đến máy chủ là:

exec sp_cursorprepexec @p1 output, @p2 output, NULL, N'select * from .....

Vì vậy, phần nào loại trừ các sp_cursorprepexecthủ tục từ nguyên nhân đầy đủ của vấn đề. Thêm vào đó là thực tế rằng sp_cursorprepexecnó cũng được gọi khi không sử dụng 'order by' và kết quả nó cũng được trả về ngay lập tức.

Chúng tôi đã tìm hiểu về vấn đề này khá nhiều và tôi thấy các vấn đề tương tự được đăng từ những người khác, nhưng không có vấn đề nào phá vỡ đến mức này.

Vậy những người khác đã chứng kiến ​​hành vi này? Có ai có giải pháp tốt hơn việc đặt SQL vô nghĩa trước câu lệnh select để thay đổi hành vi không? Là SQL Server nên gọi thứ tự sau khi dữ liệu được thu thập, có vẻ như đây là một lỗi trong máy chủ đã tồn tại trong một thời gian dài. Chúng tôi đã tìm thấy hành vi này phù hợp trên nhiều bảng lớn của chúng tôi và có thể tái tạo.

Chỉnh sửa:

Tôi cũng nên thêm một đặt forceseekvào cũng làm cho vấn đề biến mất.

Tôi nên thêm để giúp người tìm kiếm, lỗi hết thời gian ODBC được ném là: [Microsoft] [Trình điều khiển máy chủ SQL ODBC] Thao tác bị hủy

Đã thêm 10/12/2012: Vẫn đang tìm kiếm nguyên nhân gốc, (cùng với việc đã xây dựng một mẫu để cung cấp cho Microsoft, tôi sẽ đăng chéo bất kỳ kết quả nào tại đây sau khi tôi gửi). Tôi đã đào sâu vào tệp theo dõi ODBC giữa một truy vấn đang hoạt động (với một câu lệnh nhận xét / khai báo được thêm vào) và truy vấn không hoạt động. Sự khác biệt dấu vết cơ bản được đăng dưới đây. Nó xảy ra trong cuộc gọi đến cuộc gọi SQLExtendsFetch sau khi tất cả các cuộc thảo luận SQLBindCol được hoàn thành. Cuộc gọi thất bại với mã trả về -1 và luồng cha sau đó nhập SQL Hủy. Vì chúng tôi có thể tạo ra điều này với cả trình điều khiển ODBC của Máy khách gốc và Di sản, tôi vẫn chỉ ra một số vấn đề tương thích ở phía máy chủ.

(clip)
MSSQLODBCTester 1664-1718   EXIT  SQLBindCol  with return code 0 (SQL_SUCCESS)
        HSTMT               0x001EEA10
        UWORD                       16 
        SWORD                        1 <SQL_C_CHAR>
        PTR                0x03259030
        SQLLEN                    51
        SQLLEN *            0x0326B820 (0)

MSSQLODBCTester 1664-1718   ENTER SQLExtendedFetch 
        HSTMT               0x001EEA10
        UWORD                        1 <SQL_FETCH_NEXT>
        SQLLEN                     1
        SQLULEN *           0x032677C4
        UWORD *             0x032679B0

MSSQLODBCTester 1664-1fd0   ENTER SQLCancel 
        HSTMT               0x001EEA10

MSSQLODBCTester 1664-1718   EXIT  SQLExtendedFetch  with return code -1 (SQL_ERROR)
        HSTMT               0x001EEA10
        UWORD                        1 <SQL_FETCH_NEXT>
        SQLLEN                     1
        SQLULEN *           0x032677C4
        UWORD *             0x032679B0

        DIAG [S1008] [Microsoft][ODBC SQL Server Driver]Operation canceled (0) 

MSSQLODBCTester 1664-1fd0   EXIT  SQLCancel  with return code 0 (SQL_SUCCESS)
        HSTMT               0x001EEA10

MSSQLODBCTester 1664-1718   ENTER SQLErrorW 
        HENV                0x001E7238
        HDBC                0x001E7B30
        HSTMT               0x001EEA10
        WCHAR *             0x08BFFC5C
        SDWORD *            0x08BFFF08
        WCHAR *             0x08BFF85C 
        SWORD                      511 
        SWORD *             0x08BFFEE6

MSSQLODBCTester 1664-1718   EXIT  SQLErrorW  with return code 0 (SQL_SUCCESS)
        HENV                0x001E7238
        HDBC                0x001E7B30
        HSTMT               0x001EEA10
        WCHAR *             0x08BFFC5C [       5] "S1008"
        SDWORD *            0x08BFFF08 (0)
        WCHAR *             0x08BFF85C [      53] "[Microsoft][ODBC SQL Server Driver]Operation canceled"
        SWORD                      511 
        SWORD *             0x08BFFEE6 (53)

MSSQLODBCTester 1664-1718   ENTER SQLErrorW 
        HENV                0x001E7238
        HDBC                0x001E7B30
        HSTMT               0x001EEA10
        WCHAR *             0x08BFFC5C
        SDWORD *            0x08BFFF08
        WCHAR *             0x08BFF85C 
        SWORD                      511 
        SWORD *             0x08BFFEE6

MSSQLODBCTester 1664-1718   EXIT  SQLErrorW  with return code 100 (SQL_NO_DATA_FOUND)
        HENV                0x001E7238
        HDBC                0x001E7B30
        HSTMT               0x001EEA10
        WCHAR *             0x08BFFC5C
        SDWORD *            0x08BFFF08
        WCHAR *             0x08BFF85C 
        SWORD                      511 
        SWORD *             0x08BFFEE6
(clip)

Đã thêm trường hợp Microsoft Connect 10/12/2012:

https://connect.microsoft.com/QueryServer/feedback/details/767196/order-by-datetime-in-odbc-fails-for-clean-sql-statements#details

Tôi cũng cần lưu ý rằng chúng tôi đã tìm kiếm các kế hoạch truy vấn cho cả truy vấn chức năng và không hoạt động. Cả hai đều được tái sử dụng một cách thích hợp dựa trên số lượng thực hiện. Xóa các kế hoạch được lưu trữ và chạy lại không thay đổi thành công của truy vấn.


Điều gì xảy ra nếu bạn cố gắng select id, test_date from [big table] where serial_number = ..... order by test_date- Tôi chỉ tự hỏi liệu điều đó SELECT *có ảnh hưởng tiêu cực đến hiệu suất của bạn không. Nếu bạn có chỉ mục không bao gồm trên test_datevà chỉ mục được nhóm trên id(giả sử đó là tên gọi), truy vấn này sẽ được bao phủ bởi chỉ mục không bao gồm đó và do đó sẽ trả về khá nhanh
marc_s

Xin lỗi, điểm tốt. Tôi nên bao gồm rằng chúng tôi đã cố gắng sửa đổi không gian cột đã chọn (loại bỏ '*', v.v.) bằng nhiều kết hợp khác nhau. Các hành vi được mô tả ở trên vẫn tồn tại thông qua những thay đổi.
DBtheDBA

Tôi đã liên kết tài khoản của mình bây giờ với trang web đó. Nếu người điều hành muốn chuyển bài đăng lên trang web đó, tôi cũng sẽ ổn thôi. Một trong những nhà phát triển của tôi đã chỉ ra trang web đó cho tôi sau khi tôi đăng ở đây.
DBtheDBA

Ngăn xếp khách hàng nào đang được sử dụng ở đây? Không có toàn bộ văn bản theo dõi, đó có vẻ như vấn đề. Hãy thử gói cuộc gọi ban đầu bên trong sp_executesqlvà xem những gì sẽ xảy ra.
Jon Seigel

1
Kế hoạch thực hiện chậm trông như thế nào? Thông số đánh hơi?
Martin Smith

Câu trả lời:


6

Không có gì bí ẩn, bạn có được một kế hoạch tốt (er) hoặc (thực sự) xấu về cơ bản ngẫu nhiên vì không có lựa chọn cắt rõ ràng nào cho chỉ mục sử dụng. Trong khi thuyết phục cho mệnh đề ORDER BY và do đó tránh sắp xếp, chỉ mục không được nhóm trên cột datetime là một lựa chọn rất kém cho truy vấn này. Điều gì làm cho một chỉ mục tốt hơn nhiều cho truy vấn này sẽ là một (serial_number, test_date). Thậm chí tốt hơn, điều này sẽ làm cho một ứng cử viên rất tốt cho một khóa chỉ mục cụm .

Theo quy tắc của chuỗi thời gian ngón tay cái nên được nhóm theo cột thời gian, bởi vì phần lớn các yêu cầu được quan tâm trong các khoảng thời gian cụ thể. Nếu dữ liệu cũng được phân vùng trên một cột có độ chọn lọc thấp, có vẻ như đó là trường hợp với serial_number của bạn, thì cột này phải được thêm vào như là cột ngoài cùng bên trái trong định nghĩa khóa cụm.


Tôi có một chút bối rối ở đây. Tại sao kế hoạch sẽ dựa trên the orderđiều khoản? Không phải kế hoạch tự giới hạn các wheređiều kiện vì thứ tự chỉ nên xảy ra sau khi các hàng đã được tìm nạp? Tại sao máy chủ sẽ thử và sắp xếp các bản ghi trước khi có toàn bộ kết quả?
DBtheDBA

5
Điều này cũng không giải thích tại sao việc thêm một bình luận ở đầu truy vấn ảnh hưởng đến thời lượng chạy.
cfradenburg

Ngoài ra, các bảng của chúng tôi hầu như luôn được truy vấn bởi số sê-ri, không phải test_date. Chúng tôi có các chỉ mục không được nhóm trên cả hai và chỉ một cụm trên cột id trong bảng. Đó là một kho lưu trữ dữ liệu hoạt động và việc thêm các chỉ mục được nhóm trên các cột khác sẽ chỉ dẫn đến việc phân chia trang và hiệu suất kém hơn.
DBtheDBA

1
@DBtheDBA: nếu bạn muốn đưa ra yêu cầu về 'lỗi', bạn cần thực hiện một cuộc điều tra và công bố hợp lý. Các chính xác schema của bảng của bạn và số liệu thống kê xuất khẩu, hãy làm theo như thế nào để tạo ra một kịch bản của siêu dữ liệu cơ sở dữ liệu cần thiết để tạo ra một cơ sở dữ liệu thống kê chỉ trong SQL Server 2005 và SQL Server 2008 , đặc biệt là tất cả các quan trọng Script Thống kê : Script Thống kê và biểu đồ . Thêm chúng vào thông tin bài viết cùng với các bước tái tạo vấn đề.
Remus Rusanu

1
Chúng tôi đã đọc điều đó trước khi tìm kiếm và tôi hiểu bạn đang nói gì, nhưng có một lỗ hổng cơ bản trong một cái gì đó mà máy chủ đang làm ở đây. Chúng tôi đã xây dựng lại bảng và các chỉ mục và đã sao chép nó trên một bảng mới. Tùy chọn biên dịch lại không khắc phục được sự cố, đây là một gợi ý lớn có gì đó không ổn. Tôi không nghi ngờ rằng việc đặt các chỉ mục được nhóm vào mọi thứ có thể có khả năng khắc phục vấn đề này, nhưng nó không phải là giải pháp cho nguyên nhân gốc rễ, đó là một cách giải quyết và một cái đắt tiền trên một cái bàn lớn.
DBtheDBA

0

Tài liệu chi tiết về cách tái tạo lỗi và gửi nó trên connect.microsoft.com. Tôi đã kiểm tra và không thể thấy bất cứ điều gì ngoài đó có liên quan đến điều này.


Tôi sẽ yêu cầu DBA của tôi gõ một kịch bản vào ngày mai để tạo môi trường để tái tạo. Tôi không nghĩ nó khó đến thế. Tôi cũng sẽ đăng nó ở đây nếu ai đó quan tâm đến việc tự thử.
DBtheDBA

Đăng mục kết nối quá khi nó được mở. Theo cách đó, nếu người khác có vấn đề này, họ sẽ nhận được ngay. Và bất cứ ai xem câu hỏi này có thể muốn bỏ phiếu cho mục này để Microsoft có thể chú ý đến nó hơn.
cfradenburg

0

Giả thuyết của tôi là bạn đang chạy afoul của bộ đệm kế hoạch truy vấn. (Remus có thể đang nói điều tương tự như tôi, nhưng theo một cách khác.)

Đây là một tấn chi tiết về cách SQL lên kế hoạch lưu trữ .

Đánh bóng qua các chi tiết: Ai đó đã chạy truy vấn đó trước đó, cho một số [cụ thể]. SQL đã xem xét giá trị được cung cấp, các chỉ mục và số liệu thống kê cho bảng / cột có liên quan, v.v. và xây dựng một kế hoạch hoạt động tốt cho [một số] cụ thể đó. Sau đó, nó lưu trữ kế hoạch, chạy nó và đưa kết quả lại cho người gọi.

Sau đó, một người khác đang chạy cùng một truy vấn, với giá trị khác là [một số]. Giá trị cụ thể này dẫn đến một số lượng hàng kết quả khác nhau và công cụ sẽ tạo ra một kế hoạch khác cho trường hợp truy vấn này. Nhưng nó không hoạt động theo cách đó. Thay vào đó, SQL lấy truy vấn và (ít nhiều) thực hiện tìm kiếm phân biệt chữ hoa chữ thường của bộ đệm truy vấn, tìm kiếm một phiên bản truy vấn có sẵn. Khi nó tìm thấy cái trước đó, nó chỉ sử dụng kế hoạch đó.

Ý tưởng là nó tiết kiệm thời gian cần thiết để quyết định kế hoạch và xây dựng nó. Lỗ hổng trong ý tưởng là khi cùng một truy vấn được chạy với các giá trị tạo ra kết quả cực kỳ khác nhau. Họ nên có kế hoạch khác nhau, nhưng họ không. Bất cứ ai chạy truy vấn trước sẽ giúp thiết lập hành vi cho mọi người chạy nó sau đó.

Một ví dụ nhanh: chọn * từ [people] where lastname = 'SMITH' - tên rất phổ biến ở US GO chọn * từ [people] where lastname = 'BONAPARTE' - KHÔNG phải tên cuối cùng phổ biến ở Hoa Kỳ

Khi truy vấn cho BONAPARTE được chạy, kế hoạch được xây dựng cho SMITH sẽ được sử dụng lại. Nếu SMITH gây ra quét bảng (có thể tốt , nếu các hàng trong bảng là 99% SMITH), thì BONAPARTE cũng sẽ quét bảng. Nếu BONAPARTE được chạy trước SMITH, một kế hoạch sử dụng chỉ mục có thể được xây dựng và sử dụng, sau đó được sử dụng lại cho SMITH (có thể tốt hơn khi quét bảng). Mọi người có thể không nhận thấy rằng hiệu suất cho SMITH kém vì họ mong đợi hiệu suất kém vì toàn bộ bảng phải được đọc và đọc chỉ mục và nhảy vào bảng không được chú ý trực tiếp.

Đối với những thay đổi cần thay đổi của bạn, tôi nghi ngờ rằng SQL chỉ xem đó là một truy vấn hoàn toàn khác và xây dựng một kế hoạch mới, cụ thể theo giá trị của bạn là [một số].

Để kiểm tra điều này, hãy thực hiện một thay đổi vô nghĩa đối với truy vấn, như thêm một số khoảng trắng giữa FOR và tên bảng hoặc đặt nhận xét ở cuối. Có nhanh không Nếu vậy, đó là vì truy vấn đó hơi khác so với những gì trong bộ đệm, vì vậy SQL đã làm những gì nó làm cho các truy vấn "mới".

Đối với một giải pháp, tôi sẽ xem xét ba điều. Đầu tiên, hãy chắc chắn rằng số liệu thống kê của bạn được cập nhật. Đây thực sự nên là điều đầu tiên bạn làm khi một truy vấn dường như hành động kỳ lạ hoặc ngẫu nhiên. DBA của bạn nên làm điều này, nhưng mọi thứ xảy ra. Cách thông thường để đảm bảo số liệu thống kê cập nhật là lập chỉ mục lại các bảng của bạn, đây không nhất thiết phải là một việc nhẹ, nhưng cũng có các tùy chọn để chỉ cập nhật số liệu thống kê.

Điều thứ hai cần suy nghĩ là thêm các chỉ mục dọc theo dòng gợi ý của Remus. Với chỉ số tốt hơn / khác nhau, một giá trị so với giá trị khác có thể ổn định hơn và không thay đổi quá lớn.

Nếu điều đó không có ích, điều thứ ba cần thử là buộc một kế hoạch mới mỗi khi bạn chạy câu lệnh, sử dụng từ khóa RECOMPILE:

chọn * từ [bảng lớn] trong đó serial_number = [một số số] thứ tự theo test_date desc TÙY CHỌN (RECOMPILE)

Có một bài viết mô tả một tình huống tương tự ở đây . Thành thật mà nói, tôi chỉ thấy RECOMPILE được áp dụng cho các thủ tục được lưu trữ trước đó, nhưng dường như nó hoạt động với các câu lệnh CHỌN "thông thường". Kimberly Tripp chưa bao giờ lái nhầm tôi.

Bạn cũng có thể xem xét tính năng gọi là " hướng dẫn kế hoạch ", nhưng nó phức tạp hơn và có thể là quá mức cần thiết.


Để giải quyết một số vấn đề sau: 1. Số liệu thống kê đã được cập nhật, đang được cập nhật. 2. Chúng tôi đã thử lập chỉ mục theo nhiều cách (bao gồm các chỉ mục, v.v.) nhưng vấn đề dường như gắn chặt hơn với việc order bysử dụng so với chỉ số datetime cụ thể. 3. Chỉ cần thử ý tưởng của bạn với tùy chọn RECOMPILE, nó vẫn thất bại, điều đó làm tôi ngạc nhiên một chút, tôi đã hy vọng nó sẽ hoạt động, mặc dù tôi không biết liệu đó có phải là một giải pháp cho sản xuất hay không.
DBtheDBA
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.