DBCC CHECKDB corruzione irreversibile: la vista indicizzata contiene righe che non sono state prodotte dalla definizione della vista


14

TL; DR: ho una corruzione non riparabile in una vista indicizzata. Ecco i dettagli:


In esecuzione

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

su uno dei miei database produce il seguente errore:

Messaggio 8907, livello 16, stato 1, riga 1 L'indice spaziale, l'indice XML o la vista indicizzata 'ViewName' (ID oggetto 784109934) contiene righe che non sono state prodotte dalla definizione della vista. Ciò non rappresenta necessariamente un problema di integrità con i dati in questo database. (...)

CHECKDB ha trovato 0 errori di allocazione e 1 errori di coerenza nella tabella 'ViewName'.

repair_rebuild è il livello di riparazione minimo (...).

Comprendo che questo messaggio indica che i dati materializzati della vista indicizzata 'ViewName' non sono identici a ciò che produce la query sottostante. Tuttavia, la verifica manuale dei dati non presenta alcuna discrepanza:

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è stato usato per forzare l'uso del (solo) indice su ViewName. FORCESCANè stato utilizzato per impedire la corrispondenza della vista indicizzata. Il piano di esecuzione conferma che entrambe le misure funzionano.

Qui non vengono restituite righe, il che significa che le due tabelle sono identiche. (Esistono solo colonne di numeri interi e guid, le regole di confronto non entrano in gioco).

L'errore non può essere corretto ricreando l'indice sulla vista o eseguendolo DBCC CHECKDB REPAIR_ALLOW_DATA_LOSS. Anche ripetere le correzioni non ha aiutato. Perché DBCC CHECKDBsegnala questo errore? Come sbarazzarsene?

(Anche se la ricostruzione avesse risolto il problema, la mia domanda sarebbe ancora valida: perché viene segnalato un errore anche se le mie query di controllo dei dati vengono eseguite correttamente?)


Aggiornamento: il bug è stato corretto in alcune versioni. Non riesco più a riprodurre in SQL Server 2014 SP2 CU 5. Il 2014 SP2 KB contiene una correzione senza articolo KB: Creating non-clustered index causes DBCC CheckDB With Extended_Logical_Checks to raise corruption error. I due bug di connessione su questo sono stati chiusi:


1
Stai dicendo che hai lasciato cadere e ricreato l'indice nella vista e DBCC CHECKDB riporta ancora lo stesso errore? Che ne dici di abbandonare la vista e crearla da zero?
Aaron Bertrand

Da BOL: risoluzione dei problemi relativi agli errori DBCC nelle visualizzazioni indicizzate 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 Ho modificato il tuo commento. La [1]notazione non funziona nel mark-down dei commenti.
Aaron Bertrand

Ho ricreato tutto. Ho anche lasciato correre DBCC CHECKDB REPAIR_ALLOW_DATA_LOSS. Ha detto che ha ricostruito la vista, ma poi ha riportato lo stesso errore.
usr

Riesci a mostrare la definizione della vista (se troppo a lungo qui, allora in un pastebin)?
Aaron Bertrand

Risposte:


14

Il Query Processor può produrre un piano di esecuzione non valido per la query (corretta) generata da DBCC per verificare che l'indice di visualizzazione produca le stesse righe della query di visualizzazione sottostante.

Il piano prodotto dall'elaboratore di query gestisce erroneamente NULLsla ImageObjectIDcolonna. Motiva erroneamente che la query di visualizzazione rifiuta NULLsper questa colonna, quando non lo fa. Pensando che NULLssono esclusi, è in grado di abbinare l'indice non cluster filtrato sulla Userstabella su cui filtra ImageObjectID IS NOT NULL.

Producendo un piano che utilizza questo indice filtrato, garantisce che le righe con NULLin ImageObjectIDnon vengano rilevate. Queste righe vengono restituite (correttamente) dall'indice della vista, quindi sembra che ci sia un danneggiamento quando non lo è.

La definizione della vista è:

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

Il ONconfronto di uguaglianza della clausola tra AdminUserIDe IDrifiuta NULLsin quelle colonne, ma non dalla ImageObjectIDcolonna.

Parte della query generata da 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);

Questo è un codice generico che confronta i valori in NULLmodo consapevole. È certamente prolisso, ma la logica va bene.

Il bug nel ragionamento del Query Processor significa che può essere prodotto un piano di query che utilizza erroneamente l'indice filtrato, come nel frammento del piano di esempio riportato di seguito:

Piano errato

La query DBCC utilizza un percorso di codice diverso attraverso il Query Processor dalle query dell'utente. Questo percorso del codice contiene il bug. Quando viene generato un piano che utilizza l'indice filtrato, non può essere utilizzato con il USE PLANsuggerimento per forzare quella forma del piano con lo stesso testo di query inviato da una connessione al database utente.

Il percorso del codice dell'ottimizzatore principale (per le query degli utenti) non contiene questo errore, quindi è specifico per le query interne come quelle generate da DBCC.


Riesco a vedere il piano difettoso nell'evento XML Showplan SQL Profiler. Lo segnerò come risposta .; Perché DBCC crea la query in modo diverso rispetto al normale processore di query ?; Aggiungerò un link a questa risposta all'elemento connect.
usr

2
@usr DBCC fa ogni sorta di cose che non sarebbero possibili da una connessione utente. Immagino che funzioni così perché deve, ma dovresti chiedere a qualcuno come Paul Randal di ottenere i dettagli reali su questo. Potrebbe non essere libero di dire, ovviamente. So che ci sono molte cose al di fuori di DBCC che fanno cose ancora più strane; alcuni addirittura costruiscono un piano di esecuzione senza passare attraverso l'ottimizzatore!
Paul White 9

6

Ulteriori indagini dimostrano che si tratta di un bug in DBCC CHECKDB. È stato aperto un bug di Microsoft Connect: errore irreversibile DBCC CHECKDB (che è anche un falso positivo e altrimenti strano) . Fortunatamente, sono stato in grado di produrre una riproduzione in modo che il bug potesse essere trovato e corretto.

Il bug può essere nascosto giocando con lo schema del database. L'eliminazione di un indice filtrato non correlato o la rimozione del filtro nasconde il bug. Per i dettagli, vedere l'elemento di connessione.

L'elemento connect contiene anche la query interna che DBCC CHECKDB utilizza per convalidare il contenuto della vista. Non restituisce risultati, a dimostrazione che si tratta di un bug.

Il bug è stato corretto in alcune versioni. Non riesco più a riprodurlo in SQL Server 2014 SP2 CU 5.


Sono stati necessari molti dati (di produzione) per riprodurre il bug (che è un'ulteriore prova che un cambiamento del piano potrebbe essere la causa). Non mi sento a mio agio nel rilasciare i dati anche se sono stato in grado di eliminare tutte le colonne tranne due da ogni tabella. Il problema a cui si è collegati richiede di causare un danneggiamento nella vista. Ho ricreato la vista in modo che nessuna causa di corruzione dovuta a DML possa essere la causa .; Sei a conoscenza di qualcosa che potrebbe causare un piano diverso se la query viene eseguita in DBCC CHECKDB anziché in una normale finestra di query?
usr

È stato appena caricato un database anonimo. Ecco uno script che ricostruisce tutti gli indici e ricrea la vista: pastebin.com/jPEALeEw (utile per ripristinare tutto e assicurarsi che la struttura fisica sia ok). Altri script utili: pastebin.com/KxNSwm2J Gli script devono essere eseguiti e il problema deve essere ripetuto immediatamente.
usr

Specchio del .bak: mega.co.nz/…
usr

11.0.3349 con -T272,4199,3604. 4199 correzioni di Query Processor abilitate. Ho appena rimosso quel TF .; Forse dobbiamo indurre il piano di query corretto. Ora ho impostato 1 GB di RAM e riavviato l'istanza (era 8 GB). Ciò ha cambiato uno dei due join di unione che stavo vedendo in NLJ. Ancora repros .; Per provare alcune varianti del piano ho aggiunto e rimosso le righe: pastebin.com/y972Sx4d Il bug sembra innescare se ottengo un join di unione o un hash nella parte "sinistra anti semi join" della query. Prova questo: aggiungi 100k righe agli Utenti. Ciò fornisce in modo affidabile un hash join (parallelo) per me.
usr

Ho appena caricato "Plans.zip" nell'elemento connect che contiene diversi piani di esecuzione per la query DBCC CHECKDB. Con conteggi di fila diversi nelle università, posso produrre almeno tre piani diversi. Solo con il piano di join loop il problema non si verifica. Con merge e hash join il bug è riproducibile.
usr
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.