DBCC CHECKDB 수정 불가능한 손상 : 인덱싱 된 뷰에 뷰 정의에서 생성되지 않은 행이 포함됨


14

TL; DR : 인덱싱 된보기에서 수정할 수없는 손상이 있습니다. 세부 사항은 다음과 같습니다.


달리는

DBCC CHECKDB([DbName]) WITH EXTENDED_LOGICAL_CHECKS, DATA_PURITY, NO_INFOMSGS, ALL_ERRORMSGS

내 데이터베이스 중 하나에서 다음 오류가 발생합니다.

메시지 8907, 수준 16, 상태 1, 줄 1 공간 인덱스, XML 인덱스 또는 인덱싱 된 뷰 'ViewName'(개체 ID 784109934)에 뷰 정의에 의해 생성되지 않은 행이 포함되어 있습니다. 이것은 반드시이 데이터베이스의 데이터에 대한 무결성 문제를 나타내는 것은 아닙니다. (...)

CHECKDB가 테이블 'ViewName'에서 0 개의 할당 오류와 1 개의 일관성 오류를 발견했습니다.

repair_rebuild는 최소 복구 수준 (...)입니다.

이 메시지는 인덱싱 된 뷰 'ViewName'의 구체화 된 데이터가 기본 쿼리가 생성하는 것과 동일하지 않음을 나타냅니다. 그러나 데이터를 수동으로 확인해도 불일치가 나타나지 않습니다.

SELECT * FROM ViewName WITH (NOEXPAND)
EXCEPT
SELECT ...
from T1 WITH (FORCESCAN)
join T2 on ...

SELECT ...
from T1 WITH (FORCESCAN)
join T2 on ...
EXCEPT
SELECT * FROM ViewName WITH (NOEXPAND)

NOEXPAND에 (만) 색인을 사용하는 데 사용되었습니다 ViewName. FORCESCAN인덱싱 된 뷰 일치가 발생하지 않도록하는 데 사용되었습니다. 실행 계획은 두 측정 값 모두 작동하는지 확인합니다.

여기에 행이 리턴되지 않으므로 두 테이블이 동일하다는 의미입니다. (정수 및 guid 열만 있고 데이터 정렬이 작동하지 않습니다).

보기에서 색인을 다시 작성하거나을 실행하여 오류를 수정할 수 없습니다DBCC CHECKDB REPAIR_ALLOW_DATA_LOSS . 수정을 반복해도 도움이되지 않았습니다. 이 오류 가보고되는 이유는 무엇 DBCC CHECKDB입니까? 그것을 제거하는 방법?

(다시 작성해도 문제가 해결되지 않습니다. 데이터 확인 쿼리가 성공적으로 실행되었지만 왜 오류가보고됩니까?)


업데이트 : 일부 릴리스에서 버그가 수정되었습니다. SQL Server 2014 SP2 CU 5에서 더 이상 재현 할 수 없습니다. 2014 SP2 KB 에는 KB 문서가없는 수정 프로그램이 포함되어 있습니다 Creating non-clustered index causes DBCC CheckDB With Extended_Logical_Checks to raise corruption error. 이것에 대한 두 개의 연결 버그가 닫혔습니다.


1
뷰에서 인덱스를 삭제했다가 다시 만들었는데 DBCC CHECKDB가 여전히 동일한 오류를보고합니까? 뷰를 삭제하고 처음부터 새로 만드는 것은 어떻습니까?
Aaron Bertrand

From BOL : 인덱싱 된 뷰에서 DBCC 오류 문제 해결 If the indexed view does not contain an aggregate over values of type float or real and you receive errors 8907 or 8708, drop the index on the view and re-create it. Do not use ALTER INDEX REBUILD to try to remove the differences between the stored and the computed view, because ALTER INDEX REBUILD does not recalculate the view before rebuilding the index. Then run DBCC CHECKTABLE on the View to verify no differences remain.
Kin Shah

@Kin 댓글을 편집했습니다. [1]표기 댓글 마크 다운에서 작동하지 않습니다.
Aaron Bertrand

나는 모든 것을 다시 만들었다. 또한 DBCC CHECKDB REPAIR_ALLOW_DATA_LOSS를 실행했습니다. 뷰를 다시 작성했다고 말했지만 동일한 오류를보고했습니다.
usr

뷰 정의를 표시 할 수 있습니까 (너무 긴 경우 pastebin으로)?
Aaron Bertrand

답변:


14

쿼리 프로세서는 DBCC에 의해 생성 된 (올바른) 쿼리에 대해 유효하지 않은 실행 계획을 생성하여 뷰 인덱스가 기본 뷰 쿼리와 동일한 행을 생성하는지 확인할 수 있습니다.

쿼리 프로세서에서 생성 된 계획 NULLsImageObjectID열을 잘못 처리 합니다 . NULLs그렇지 않으면 뷰 쿼리 가이 열에 대해 거부하는 이유가 잘못 되었습니다. NULLs제외 된 것으로 생각 하면 필터링 된 Users테이블 의 필터링 된 비 클러스터형 인덱스와 일치 할 수 ImageObjectID IS NOT NULL있습니다.

이 필터링 된 인덱스를 사용하는 계획을 작성하면 NULLin ImageObjectID이 있는 행이 발견되지 않습니다. 이러한 행은 뷰 인덱스에서 (올바로) 반환되므로없는 경우 손상이 발생한 것으로 나타납니다.

뷰 정의는 다음과 같습니다.

SELECT
    dbo.Universities.ID AS Universities_ID, 
    dbo.Users.ImageObjectID AS Users_ImageObjectID
FROM dbo.Universities
JOIN dbo.Users
    ON dbo.Universities.AdminUserID = dbo.Users.ID

ON절 평등 비교 사이 AdminUserIDID거부 NULLs하는 열의가 아니라에서 ImageObjectID열입니다.

DBCC 생성 쿼리의 일부는 다음과 같습니다.

SELECT [Universities_ID], [Users_ImageObjectID], 0 as 'SOURCE'
FROM [dbo].[mv_Universities_Users_ID] tOuter WITH (NOEXPAND) 
WHERE NOT EXISTS
( 
    SELECT 1 
    FROM   [dbo].[mv_Universities_Users_ID] tInner
    WHERE 
    (
        (
            (
                [tInner].[Universities_ID] = [tOuter].[Universities_ID]
            ) 
            OR 
            (
                [tInner].[Universities_ID] IS NULL
                AND [tOuter].[Universities_ID] IS NULL
            )
        )
        AND
        (
            (
                [tInner].[Users_ImageObjectID] = [tOuter].[Users_ImageObjectID]
            ) 
            OR 
            (
                [tInner].[Users_ImageObjectID] IS NULL 
                AND [tOuter].[Users_ImageObjectID] IS NULL
            )
        )
    )
)
OPTION (EXPAND VIEWS);

이것은 NULL인식하는 방식으로 값을 비교하는 일반 코드입니다 . 확실히 장황하지만 논리는 괜찮습니다.

쿼리 프로세서 추론의 버그는 아래의 계획 조각 예제와 같이 필터링 된 인덱스를 잘못 사용하는 쿼리 계획이 생성 될 수 있음을 의미합니다.

잘못된 계획

DBCC 쿼리는 사용자 프로세서와 쿼리 프로세서를 통해 다른 코드 경로를 사용합니다. 이 코드 경로에는 버그가 포함되어 있습니다. 필터링 된 인덱스를 사용하는 계획이 생성되면 USE PLAN사용자 데이터베이스 연결에서 제출 된 동일한 쿼리 텍스트를 사용하여 계획 형태를 강요하는 힌트 와 함께 사용할 수 없습니다 .

기본 쿼리 코드 경로 (사용자 쿼리의 경우)에는이 버그가 포함되어 있지 않으므로 DBCC에서 생성 한 것과 같은 내부 쿼리에만 적용됩니다.


SQL 프로파일 러 실행 계획 XML 이벤트에서 잘못된 계획을 볼 수 있습니다. 이것을 답변으로 표시하겠습니다.; DBCC가 일반 쿼리 프로세서와 다른 방식으로 쿼리를 작성하는 이유는 무엇입니까?; 이 답변에 대한 링크를 연결 항목에 추가하겠습니다.
usr

2
@usr DBCC는 사용자 연결로는 불가능한 모든 종류의 작업을 수행합니다. 나는 그것이 그렇게해야하기 때문에이 방식으로 작동한다고 상상하지만, 당신은 Paul Randal과 같은 누군가에게 그것에 대한 자세한 내용을 요구해야합니다. 물론 그는 말할 자유가 없을 수도 있습니다. DBCC 외부에는 더 이상한 일들이 많이 있다는 것을 알고 있습니다. 일부는 최적화 프로그램을 전혀 거치지 않고 실행 계획을 세우기도합니다!
Paul White 9

6

추가 조사에 따르면 이것이 DBCC CHECKDB의 버그입니다. Microsoft Connect 버그가 열렸습니다 : 수정할 수없는 DBCC CHECKDB 오류 (거짓 긍정적이고 다른 경우도 있습니다) . 다행히도 버그를 찾아서 수정할 수 있도록 재현을 만들 수있었습니다.

데이터베이스 스키마를 사용하여 버그를 숨길 수 있습니다. 관련이없는 필터링 된 인덱스를 삭제하거나 필터를 제거하면 버그가 숨겨집니다. 자세한 내용은 연결 항목을 참조하십시오.

연결 항목에는 또한 DBCC CHECKDB가보기 내용의 유효성을 검사하는 데 사용하는 내부 쿼리가 포함되어 있습니다. 버그가 없음을 나타내는 결과를 반환하지 않습니다.

일부 릴리스에서는 버그가 수정되었습니다. 더 이상 SQL Server 2014 SP2 CU 5에서 재현 할 수 없습니다.


버그를 재현하기 위해 많은 (생산) 데이터가 필요했습니다 (계획 변경이 원인 일 수 있다는 추가 증거입니다). 각 테이블에서 두 열을 제외한 모든 열을 삭제할 수 있었지만 데이터를 공개하는 것이 불편합니다. 연결 한 문제로 인해보기가 손상 될 수 있습니다. DML로 인한 손상이 원인이 될 수 없도록보기를 다시 작성했습니다.; 쿼리가 일반 쿼리 창 대신 DBCC CHECKDB에서 실행되는 경우 다른 계획을 유발할 수있는 사항이 있습니까?
usr

익명의 데이터베이스가 업로드되었습니다. 다음은 모든 인덱스를 다시 작성하고 뷰를 다시 작성하는 스크립트입니다. pastebin.com/jPEALeEw (모든 항목을 재설정하고 물리적 구조가 올바른지 확인하는 데 유용함) 다른 유용한 스크립트 : pastebin.com/KxNSwm2J 스크립트가 실행되고 문제가 즉시 재현됩니다.
usr

.bak의 거울 : mega.co.nz/…
usr

-T272,4199,3604의 11.0.3349 4199 사용 가능한 쿼리 프로세서 수정 방금 그 TF를 제거했습니다.; 올바른 쿼리 계획을 유도해야 할 수도 있습니다. 이제 1GB RAM을 설정하고 인스턴스를 다시 시작했습니다 (기존 8GB). 그것은 내가 NLJ 에보 고있는 두 개의 병합 조인 중 하나를 변경했습니다. 여전히 재현.; 몇 가지 계획 변형을 시도하려면 행을 추가하고 제거했습니다. pastebin.com/y972Sx4d 버그가 쿼리의 "왼쪽 반 세미 조인"부분에서 병합 조인 또는 해시를 얻는 경우이 버그가 트리거되는 것 같습니다. 이것을 시도하십시오 : 사용자에게 100k 행을 추가하십시오. 이것은 확실히 (병렬) 해시 조인을 제공합니다.
usr

방금 DBCC CHECKDB 쿼리에 대한 다른 실행 계획이 포함 된 연결 항목에 "plans.zip"을 업로드했습니다. 대학에서 다른 행 수를 사용하면 적어도 세 가지 다른 계획을 만들 수 있습니다. 루프 조인 계획에서만 문제가 발생하지 않습니다. 병합 및 해시 조인을 사용하면 버그를 재현 할 수 있습니다.
usr
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.