SQL Server 2008 날짜 시간 인덱스 성능 버그


11

우리는 SQL Server 2008 R2를 사용하고 있으며 기본 id 인덱스가있는 매우 큰 (100M + 행) 테이블과 datetime비 클러스터형 인덱스가 있는 열을 가지고 있습니다. 우리는의 사용에 따라 일부 매우 이례적인 클라이언트 / 서버 동작을보고있는 order by특히 A의 인덱스 날짜 열을 .

다음 게시물을 읽었습니다 .https : //stackoverflow.com/questions/1716798/sql-server-2008-ordering-by-datetime-is-too-slow 그러나 클라이언트 / 서버에 대한 것보다 더 많은 일이 있습니다. 여기에 설명을 시작하십시오.

다음 쿼리를 실행하면 (일부 내용을 보호하도록 편집 됨) :

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

쿼리는 매번 시간 초과됩니다. SQL Server 프로파일 러에서 실행 된 쿼리는 다음과 같습니다.

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

이제 검색어를 수정하면 다음과 같이 말합니다.

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

SQL Server 프로파일 러는 실행 된 쿼리가 서버에 다음과 같이 표시되며 즉시 작동합니다.

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

사실, 사용하지 않는 선언문 대신 빈 주석 ( '-;')을 넣고 동일한 결과를 얻을 수도 있습니다. 그래서 처음에 우리는이 문제의 근본 원인으로 sp 전처리기를 가리 켰지 만, 이렇게하면 :

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

즉시 작동합니다 (다른 datetime유형 으로 캐스팅 할 수 있음 ). 밀리 초 단위로 결과를 반환합니다. 그리고 프로파일 러는 서버에 대한 요청을 다음과 같이 표시합니다.

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

따라서 sp_cursorprepexec문제의 전체 원인에서 절차 가 다소 제외됩니다 . 여기에 sp_cursorprepexec'order by'가 사용되지 않을 때도 호출되고 결과도 즉시 반환 된다는 사실을 추가하십시오 .

우리는이 문제에 대해 google을 약간 살펴 보았지만 다른 사람들이 비슷한 문제를 게시했지만이 수준으로 분류하는 것은 없습니다.

다른 사람들이이 행동을 목격 했습니까? 누구든지 select 문 앞에 의미없는 SQL을 배치하여 동작을 변경하는 것보다 더 나은 솔루션을 가지고 있습니까? SQL Server가 데이터를 수집 한 후 주문을 호출해야하므로 서버에서 오랫동안 지속 된 버그 인 것 같습니다. 우리는이 동작이 우리의 많은 큰 테이블에서 일관성이 있고 재현 가능한 것을 발견했습니다.

편집 :

또한 퍼팅 추가해야합니다 forceseek또한 문제가 사라질 수있다.

검색자를 돕기 위해 추가해야하는 ODBC 시간 초과 오류는 다음과 같습니다. [Microsoft] [ODBC SQL Server 드라이버] 작업이 취소되었습니다.

2012 년 10 월 12 일 추가 : 여전히 근본 원인을 찾아냅니다 (Microsoft에 제공하기 위해 샘플을 작성 한 것과 함께 제출 한 후 여기에 결과를 게시 할 것입니다). 작업 쿼리 (추가 주석 / 선언문 포함)와 작동하지 않는 쿼리 사이의 ODBC 추적 파일을 파고 들었습니다. 기본적인 미량 차이는 다음과 같습니다. 모든 SQLBindCol 토론이 완료된 후 SQLExtendedFetch 호출에 대한 호출에서 발생합니다. 리턴 코드 -1로 호출이 실패한 후 상위 스레드가 SQLCancel을 입력합니다. 우리는 Native Client 및 Legacy ODBC 드라이버를 사용하여 이것을 생성 할 수 있기 때문에 여전히 서버 측에서 일부 호환성 문제를 지적하고 있습니다.

(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)

2012 년 10 월 12 일 Microsoft Connect 사례가 추가되었습니다.

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

또한 작동하는 쿼리와 작동하지 않는 쿼리 모두에 대한 쿼리 계획을 찾았습니다. 이들은 모두 실행 횟수에 따라 적절하게 재사용됩니다. 캐시 된 계획을 비우고 다시 실행해도 쿼리 성공은 바뀌지 않습니다.


시도하면 어떻게됩니까 select id, test_date from [big table] where serial_number = ..... order by test_date- SELECT *성능에 부정적인 영향이 있는지 궁금합니다 . 당신이 클러스터되지 않은 인덱스가있는 경우 test_date와에 클러스터 된 인덱스 id(그것이라고 그 무엇의 가정을),이 쿼리는해야 덮여 그 클러스터되지 않은 인덱스 때문에 매우 빨리 반환해야합니다
marc_s

미안, 좋은 지적이야 선택한 열 공간을 수정하고 ( '*'등을 제거하는 등) 다양한 조합으로 크게 수정해야했습니다. 위에서 설명한 동작은 이러한 변경 사항을 통해 지속되었습니다.
DBtheDBA

내 계정을 해당 사이트에 연결했습니다. 중재자가 게시물을 해당 사이트로 이동하려면 어느 쪽이든 괜찮습니다. 내 개발자 중 한 명이 여기에 게시 한 후 해당 사이트를 지적했습니다.
DBtheDBA

어떤 클라이언트 스택이 사용되고 있습니까? 전체 추적 텍스트가 없으면 문제처럼 보입니다. 원래 통화를 감싸서 sp_executesql어떤 일이 발생하는지 보십시오 .
Jon Seigel

1
느린 실행 계획은 어떻게 생겼습니까? 스니핑 매개 변수?
Martin Smith

답변:


6

수수께끼 는 없으며 인덱스를 사용할 명확한 선택이 없기 때문에 기본적으로 무작위로 좋은 계획 또는 나쁜 계획을 얻습니다 . ORDER BY 절을 강요하고 정렬을 피하는 동안 datetime 열의 비 클러스터형 인덱스는이 쿼리에 매우 적합하지 않습니다. 쿼리에 대해 훨씬 더 나은 인덱스를 만드는 것은 에 관한 것 (serial_number, test_date)입니다. 더 좋은 방법은 클러스터 된 인덱스 키에 매우 적합 합니다.

대체로 대부분의 요청은 특정 시간 범위에 관심이 있기 때문에 일반적으로 시계열은 시간 열에 의해 클러스터되어야합니다. serial_number의 경우처럼 선택성이 낮은 열에 데이터가 본질적으로 분할 된 경우이 열은 클러스터 된 키 정의에서 가장 왼쪽에 추가되어야합니다.


나는 약간 혼란 스러워요. 계획이 왜 the order절을 기반으로 합니까? where행이 페치 된 후에 만 ​​순서가 발생 하므로 계획 자체가 조건으로 제한 되어서는 안됩니까? 전체 결과 세트를 갖기 전에 서버가 레코드를 시도하고 정렬하는 이유는 무엇입니까?
DBtheDBA

5
또한 쿼리 시작시 주석을 추가하면 실행 기간에 영향을 미치는 이유를 설명하지 않습니다.
cfradenburg

또한 테이블은 거의 항상 test_date가 아닌 일련 번호로 쿼리됩니다. 둘 다 클러스터되지 않은 인덱스가 있고 테이블의 id 열에 만 클러스터됩니다. 운영 데이터 저장소이며 다른 열에 클러스터 된 인덱스를 추가하면 페이지 분할 만 발생하고 성능이 저하됩니다.
DBtheDBA

1
@DBtheDBA : '버그'에 대한 주장을하려면 적절한 조사와 공개가 필요합니다. 테이블 및 내 보낸 통계 의 정확한 스키마는 필요한 데이터베이스 메타 데이터의 스크립트를 생성하는 방법에 따라 SQL Server 2005 및 SQL Server 2008에서 통계 전용 데이터베이스 , 특히 모든 중요한 스크립트 통계 : 스크립트 통계 및 히스토그램 을 생성하는 방법을 따릅니다 . 이슈를 재현하는 단계와 함께 게시물 정보에 추가하십시오.
Remus Rusanu

1
우리는 검색하기 전에 그것을 읽었으며, 나는 당신이 말하는 것을 이해하지만 서버가 여기에서하고있는 것에 근본적인 결함이 있습니다. 테이블과 인덱스를 다시 작성하고 새 테이블에서 재현했습니다. 재 컴파일 옵션은 문제를 해결하지 못합니다. 이는 잘못된 힌트입니다. 클러스터 된 인덱스를 모든 것에 배치하면 잠재적 으로이 문제를 해결할 수는 있지만 근본 원인에 대한 해결책은 아니며 해결 방법이며 큰 테이블에 비싼 것입니다.
DBtheDBA

0

버그를 재현하는 방법에 대한 세부 사항을 문서화하고 connect.microsoft.com에 제출하십시오. 나는 이것과 관련이있는 것을 이미 확인했고 아무것도 볼 수 없었다.


나는 내일 DBA가 재현 할 환경을 만들기 위해 스크립트를 입력하도록 할 것이다. 그렇게 어렵다고 생각하지 않습니다. 누군가 스스로 시도해보고 싶다면 여기에 게시하겠습니다.
DBtheDBA

연결 항목이 열리면 게시하십시오. 다른 사람 이이 문제를 겪고 있다면 그렇게 할 수 있습니다. 그리고이 질문을보고있는 사람이라면 누구나 투표에 참여하여 Microsoft가 관심을 가질 가능성이 높습니다.
cfradenburg

0

내 가설은 쿼리 계획 캐시를 무시하고 있다는 것입니다. (Remus는 나와 같은 내용이지만 다른 방식으로 말하고있을 수 있습니다.)

다음은 SQL이 캐싱을 계획하는 방법대한 자세한 내용입니다 .

세부 사항에 대한 광택 : 누군가가 특정 [일부 숫자]에 대해 해당 쿼리를 더 일찍 실행했습니다. SQL은 제공된 값, 관련 테이블 / 열 등에 대한 인덱스 및 통계를 검토하고 해당 특정 [일부 숫자]에 대해 잘 작동하는 계획을 작성했습니다. 그런 다음 계획을 캐시하고 실행 한 후 결과를 호출자에게 다시 제공했습니다.

나중에 다른 사람이 [일부 숫자]의 다른 값에 대해 동일한 쿼리를 실행하고 있습니다. 이 특정 값으로 인해 결과 행 수가 크게 달라지고 엔진은이 쿼리 인스턴스에 대해 다른 계획을 작성해야합니다. 그러나 그것은 그렇게 작동하지 않습니다. 대신 SQL은 쿼리를 가져오고 대 / 소문자를 구분하여 쿼리 캐시를 검색하여 기존 버전의 쿼리를 찾습니다. 이전 계획을 찾으면 해당 계획 만 사용합니다.

아이디어는 계획을 세우고 계획을 세우는 데 필요한 시간을 절약한다는 것입니다. 아이디어의 결점은 완전히 다른 결과 를 생성하는 값으로 동일한 쿼리를 실행할 때 입니다. 그들은 다른 계획을 가져야하지만 그렇지 않습니다. 쿼리를 먼저 실행 한 사람은 나중에 쿼리를 실행하는 모든 사람의 동작을 설정하는 데 도움이됩니다.

간단한 예 : select * from [people], 여기서 lastname = 'SMITH'-미국에서 매우 인기있는 성 select * from [people], 여기서 lastname = 'BONAPARTE'-미국에서 인기없는 성

BONAPARTE에 대한 조회가 실행되면 SMITH 용으로 작성된 계획이 재사용됩니다. SMITH가 테이블 스캔을 일으킨 경우 (테이블 의 행이 99 % SMITH 인 경우 양호 할 수 있음 ) BONAPARTE도 테이블 스캔을받습니다. BONAPARTE가 SMITH 이전에 실행 된 경우 인덱스를 사용하는 계획을 빌드하여 사용한 다음 SMITH에 다시 사용할 수 있습니다 (테이블 스캔에 더 적합 할 수 있음). 사람들은 전체 테이블을 읽고 인덱스를 읽고 테이블에 호핑하는 것을 직접 알지 못하므로 SMITH의 성능이 좋지 않다는 것을 알지 못할 수도 있습니다.

변경 사항-변경 사항과 관련하여 SQL은 완전히 다른 쿼리로보고 있으며 [일부 숫자]의 가치와 관련된 새로운 계획을 세우고 있다고 생각합니다.

이를 테스트하려면 FOR과 테이블 이름 사이에 공백을 추가하거나 끝에 주석을 추가하는 것과 같이 쿼리를 무의미하게 변경하십시오. 빠른가요? 그렇다면 해당 쿼리가 캐시에있는 것과 약간 다르기 때문에 SQL이 "새"쿼리에 대해 수행 한 작업을 수행 한 것입니다.

해결책을 위해 세 가지를 살펴 보겠습니다. 먼저 통계가 최신인지 확인하십시오. 이것은 쿼리가 이상하거나 무작위로 작동하는 것처럼 보일 때 가장 먼저해야합니다. DBA가이 작업을 수행해야하지만 상황이 발생합니다. 최신 통계를 확인하는 일반적인 방법은 테이블을 다시 색인화하는 것인데, 이는 반드시 간단한 일이 아니지만 통계를 업데이트하는 옵션도 있습니다.

두 번째로 고려해야 할 것은 Remus의 제안에 따라 색인을 추가하는 것입니다. 더 나은 / 다른 인덱스를 사용하면 하나의 값이 다른 값보다 안정적이고 크게 변하지 않을 수 있습니다.

그래도 문제가 해결되지 않으면 세 번째 시도는 RECOMPILE 키워드를 사용하여 명령문을 실행할 때마다 새 계획을 시행하는 것입니다.

test_date 설명에 의한 serial_number = [일부 번호] 순서에서 [큰 테이블]에서 *를 선택하십시오. OPTION (RECOMPILE)

비슷한 상황을 설명하는 기사가 있습니다 . 솔직히 전에 RECOMPILE이 저장 프로 시저에만 적용되는 것을 보았지만 "일반"SELECT 문과 함께 작동하는 것 같습니다. 킴벌리 트립은 결코 나를 잘못 조종하지 않았다.

" plan guides " 라는 기능을 살펴볼 수도 있지만 더 복잡하고 과도 할 수 있습니다.


이러한 우려 중 일부를 다루기 위해 : 1. 통계가 업데이트되었으며 업데이트 중입니다. 2. 색인 작성 등 여러 가지 방법으로 색인 생성을 시도했지만 order by날짜 시간 색인에 대한 사용량과 관련이있는 것으로 보입니다 . 3. RECOMPILE 옵션을 사용하여 아이디어를 시도했지만 여전히 실패했습니다.
DBtheDBA
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.