Corrupção não corrigível do DBCC CHECKDB: a exibição indexada contém linhas que não foram produzidas pela definição de exibição

14

TL; DR: Eu tenho uma corrupção não corrigível em uma exibição indexada. Aqui estão os detalhes:


Corrida

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

em um dos meus bancos de dados produz o seguinte erro:

Msg 8907, Nível 16, Estado 1, Linha 1 O índice espacial, o índice XML ou a exibição indexada 'ViewName' (ID do objeto 784109934) contém linhas que não foram produzidas pela definição da exibição. Isso não representa necessariamente um problema de integridade com os dados desse banco de dados. (...)

O CHECKDB encontrou 0 erros de alocação e 1 erros de consistência na tabela 'ViewName'.

repair_rebuild é o nível mínimo de reparo (...).

Entendo que esta mensagem indica que os dados materializados da exibição indexada 'ViewName' não são idênticos ao que a consulta subjacente produz. No entanto, verificar manualmente os dados não gera discrepâncias:

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)

NOEXPANDfoi usado para forçar o uso do (somente) índice ViewName.FORCESCANfoi usado para impedir que a correspondência de exibição indexada aconteça. O plano de execução confirma que ambas as medidas estão funcionando.

Nenhuma linha está sendo retornada aqui, o que significa que as duas tabelas são idênticas. (Existem apenas colunas de número inteiro e guia, os agrupamentos não entram em jogo).

O erro não pode ser corrigido recriando o índice na exibição ou executando DBCC CHECKDB REPAIR_ALLOW_DATA_LOSS. Repetir as correções também não ajudou. Por que DBCC CHECKDBrelatar esse erro? Como se livrar dele?

(Mesmo que a reconstrução o corrigisse, minha pergunta ainda permaneceria - por que um erro é relatado, embora minhas consultas de verificação de dados sejam executadas com êxito?)


Atualização: O bug foi corrigido em algumas versões. Já não posso reproduzi-lo no SQL Server 2014 SP2 CU 5. A KB 2014 SP2 contém uma correção sem artigo KB: Creating non-clustered index causes DBCC CheckDB With Extended_Logical_Checks to raise corruption error. Os dois erros de conexão sobre isso foram fechados:

usr
fonte
1
Você está dizendo que eliminou e recriou o índice na exibição e o DBCC CHECKDB ainda relata o mesmo erro? Que tal abandonar a visualização e criá-la do zero?
Aaron Bertrand
De BOL: Erros de DBCC em cadastradas Visualizações 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 eu editei seu comentário. A [1]notação não funciona na marcação de comentário.
Aaron Bertrand
Eu recriei tudo. Também deixei o DBCC CHECKDB REPAIR_ALLOW_DATA_LOSS ser executado. Ele disse que reconstruiu a exibição, mas relatou o mesmo erro.
usr
Você pode mostrar a definição de exibição (se for muito longa aqui e depois em uma pasta)?
Aaron Bertrand

Respostas:

14

O processador de consultas pode produzir um plano de execução inválido para a consulta (correta) gerada pelo DBCC para verificar se o índice de visualização produz as mesmas linhas que a consulta de visualização subjacente.

O plano produzido pelo processador de consultas manipula incorretamente NULLspara oImageObjectID coluna. Razão incorreta para que a consulta de exibição rejeite NULLsessa coluna, quando não. Pensando que NULLssão excluídos, ele pode corresponder ao índice não clusterizado filtrado na Userstabela que filtra noImageObjectID IS NOT NULL .

Ao produzir um plano que usa esse índice filtrado, garante que as linhas com NULLin ImageObjectIDnão sejam encontradas. Essas linhas são retornadas (corretamente) a partir do índice de exibição; portanto, parece que há uma corrupção quando não há.

A definição da visualização é:

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

A ONcomparação de cláusulas de igualdade entre AdminUserIDeID rejeita NULLsnessas colunas, mas não a partir da ImageObjectIDcoluna.

Parte da consulta gerada pelo 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);

Este é um código genérico que compara valores em um NULL consciente. Certamente é detalhado, mas a lógica é boa.

O erro no raciocínio do processador de consultas significa que um plano de consulta que usa incorretamente o índice filtrado pode ser produzido, como no exemplo de fragmento do plano abaixo:

Plano errado

A consulta DBCC usa um caminho de código diferente através do processador de consultas das consultas do usuário. Este caminho de código contém o erro. Quando um plano usando o índice filtrado é gerado, ele não pode ser usado com a USE PLANdica para forçar a forma do plano com o mesmo texto de consulta enviado a partir de uma conexão com o banco de dados do usuário.

O caminho principal do código do otimizador (para consultas do usuário) não contém esse bug, portanto é específico para consultas internas como as geradas pelo DBCC.

Paul White 9
fonte
Eu posso ver o plano com defeito no evento XML do SQL Profiler Showplan. Vou marcar isso como a resposta .; Por que o DBCC cria a consulta de uma maneira diferente do processador de consultas normal ?; Vou adicionar um link para esta resposta no item de conexão.
usr
2
O @usr DBCC faz todo o tipo de coisas que não seriam possíveis a partir de uma conexão do usuário. Eu imagino que funciona dessa maneira porque precisa, mas você teria que pedir a alguém como Paul Randal para obter os detalhes reais sobre isso. Ele pode não ter a liberdade de dizer, é claro. Eu sei que existem muitas coisas fora do DBCC que fazem coisas ainda mais estranhas; alguns até constroem um plano de execução sem passar pelo otimizador!
Paul White 9
6

Investigações adicionais mostram que este é um erro no DBCC CHECKDB. Um erro do Microsoft Connect foi aberto: Erro não corrigível do DBCC CHECKDB (que também é um falso positivo e, de outra forma, estranho) . Felizmente, consegui produzir uma reprodução para que o bug pudesse ser encontrado e corrigido.

O bug pode ser oculto, jogando com o esquema do banco de dados. A exclusão de um índice filtrado não relacionado ou a remoção do filtro oculta o erro. Para detalhes, consulte o item de conexão.

O item de conexão também contém a consulta interna que DBCC CHECKDB usa para validar o conteúdo da exibição. Ele não retorna resultados, mostrando que isso é um bug.

O bug foi corrigido em algumas versões. Não consigo mais reproduzi-lo no SQL Server 2014 SP2 CU 5.

usr
fonte
Muitos dados (de produção) foram necessários para reproduzir o erro (que é mais uma evidência de que uma mudança no plano pode ser a causa). Não me sinto confortável em liberar os dados, embora tenha sido capaz de excluir todas as colunas, exceto duas. O problema ao qual você vinculou requer uma corrupção na exibição. Recriei a exibição para que nenhuma corrupção devido ao DML possa ser a causa .; Você está ciente de algo que possa causar um plano diferente se a consulta estiver sendo executada no DBCC CHECKDB em vez de em uma janela de consulta normal?
usr
Um banco de dados anônimo acabou de ser carregado. Aqui está um script que reconstrói todos os índices e recria a exibição: pastebin.com/jPEALeEw (útil para redefinir tudo e garantir que a estrutura física esteja correta). Outros scripts úteis: pastebin.com/KxNSwm2J Os scripts devem ser executados e o problema deve ser reproduzido imediatamente.
usr
Espelho do .bak: mega.co.nz/…
usr
Em 11.0.3349 com -T272,4199,3604. 4199 correções do processador de consultas ativadas. Acabei de remover esse TF .; Talvez seja necessário induzir o plano de consulta correto. Agora configurei 1 GB de RAM e reiniciei a instância (era de 8 GB). Isso mudou uma das duas junções de mesclagem que eu estava vendo no NLJ. Ainda repros .; Para tentar algumas variações de plano, adicionei e removi linhas: pastebin.com/y972Sx4d O bug parece disparar se eu receber uma junção de mesclagem ou um hash na parte "anti-junção esquerda" da consulta. Tente isso: adicione 100 mil linhas aos usuários. Isso fornece uma junção hash (paralela) confiável para mim.
usr
Acabei de enviar "planos.zip" para o item de conexão que contém planos de execuções diferentes para a consulta DBCC CHECKDB. Com diferentes contagens de linhas nas universidades, posso produzir pelo menos três planos diferentes. Somente com o plano de junções de loop, o problema não ocorre. Com junções de mesclagem e hash, o bug é reproduzível.
usr