Cosa dobbiamo fare in questo malaugurato caso ?
Dobbiamo ripristinare la/le data pages danneggiate, semplice no ?!?!
Creo un DB di esempio e lo popolo...
Use Master Go Create database RestorePage Go Alter database RestorePage Set Recovery Full Go Use RestorePage Go Create table DamagePage ( id int primary key clustered ,valoreA varchar(25) ) Go ;With Cte as (Select 1 Riga Union all Select 1) ,Cte4 as (Select A.Riga from Cte A, Cte B) ,Cte16 as (Select A.Riga from Cte4 A, Cte4 B) ,Cte256 as (Select A.Riga from Cte16 A, Cte16 B) ,Results as (Select Row_Number() Over(Order by riga)Riga From Cte256) insert into DamagePage Select Riga , 'Valore ' + Cast(Riga as Varchar(5)) from Results Go
Simulo un'attività di Backup composta da Backup Full e T-Log
Backup database RestorePage to disk = 'C:\SQL 2012\Backup\BckFull.bak' With compression GO Backup Log RestorePage to disk = 'C:\SQL 2012\Backup\Log1.trn' With compression GO Backup Log RestorePage to disk = 'C:\SQL 2012\Backup\Log2.trn' With compression GO
Recupero l'id pagina in cui risiede la riga con id 128
Select *,sys.fn_PhysLocFormatter( %%Physloc%% ) from DamagePage Where id = 128 Go
Danneggio la data page contenente la riga con id = 128
Alter Database RestorePage set single_user with rollback immediate Go Dbcc writepage(RestorePage,1,217,1,1,0x66,1) Go Alter Database RestorePage set multi_user Go
Ok.... Il danno è fatto...
Come faccio ad accorgermene ?
Se non uso strumenti di diagnosi me ne accorgo solo quando una applicazione deve reperire la riga con id 128 dalla tabella
Select * from DamagePage Where id = 128 Go
Otterrà questo errore
Quindi cosa posso fare ora ?
Avendo i backup del Db posso fare il restore della singola data page corrotta
Gli step da attuare sono
1. Restore della data page con il comando Restore lasciando il db in recovery(Il DB resta accessibile, solo la data page oggetto del restore è inaccessibile....)
2. Backup del tail log
3. Restore di tutti i t-log backup necessari lasciando il db in recovery
4. Restore del tail-log backup riportando il db on line
Use master Go --Restore data page e DB in recovery restore database [RestorePage] page='1:217' from disk = 'C:\SQL 2012\Backup\BckFull.bak' with norecovery Go --Backup del tail-log backup log [RestorePage] to disk = 'C:\SQL 2012\Backup\Tail_log.trn' with No_truncate Go --Restore dei T-Log restore log [RestorePage] from disk = 'C:\SQL 2012\Backup\Log1.trn' with norecovery Go restore log [RestorePage] from disk = 'C:\SQL 2012\Backup\Log2.trn' with norecovery Go --Restore del tail-log restore log [RestorePage] from disk = 'C:\SQL 2012\Backup\Tail_log.trn' Go
Riproviamo ora a reperire la riga con id 128 dalla tabella
Select * from DamagePage Where id = 128 Go
Come vedete il problema è risolto...
Semplicissimo vero ??
Ma siamo certi che i nostri backup non contengano errori ?
Se il backup è perfettamente integro siamo in grado di risolvere il problema, altrimenti la questione si complica, e di parecchio....
Immaginiamo che il backup dell'esempio precedente non fosse stato integro... Avremmo fatto il restore della data page già danneggiata, complicando ancora di più le cose....
Cosa potremmo tentare allora ?
Possiamo tentare di aggiustare il Db con il comando Dbcc CheckDb() che mette a disposizione 2 opzioni di "riparazione"
1. REPAIR_REBUILD
2. REPAIR_ALLOW_DATA_LOSS
L'opzione REPAIR_FAST è mantenuta per retro compatibilità ma di fatto non implementa alcunché....
Proviamo a ridanneggiare la data page con lo script usato poco fa e vediamo cosa accade se tentiamo di ripararla con la dbcc checkDb()
Provo prima l'opzione REPAIR_REBUILD che non comporta perdita di dati
Alter Database RestorePage set single_user with rollback immediate Go dbcc checkdb(RestorePage,REPAIR_REBUILD ) Go Alter Database RestorePage set multi_user Go
Ed ecco il risultato
Non ha fatto nulla.....
Proviamo con l'opzione REPAIR_ALLOW_DATA_LOSS
Alter Database RestorePage set single_user with rollback immediate Go dbcc checkdb(RestorePage,REPAIR_ALLOW_DATA_LOSS )with no_infomsgs Go Alter Database RestorePage set multi_user Go
Ha corretto gli errori... benissimo !!!!
Proviamo a verificare se la ricerca della riga con id 128 funziona
In effetti la query funziona, ma non mi restituisce i dati perché per riparare la data page la dbcc checkdb() l'ha definitivamente eliminata !!!
Addio integrita dei dati quindi.....
Per evitare questi spiacevoli inconvenienti prima di ogni Backup full o differenziale è indispensabile effettuare un'operazione di diagnosi utilizzando la
Dbcc CheckDb() che verifica l'integrità strutturale, logica e fisica del Db.
Se la diagnosi non evidenzia errori posso effettuare il backup, se invece vengono segnalate anomalie che senso ha fare il backup ?
Senza il check del Db faremmo il backup anche degli errori e un backup contenente errori non serve a nulla visto che il nostro obiettivo è di avere i db in piena efficienza.
Se i nostri backup sono creati utilizzando i maintenance plan possiamo avvalerci del Check Database Integrity Task ed eseguire il backup solo se il primo task si è concluso senza errori, magari inviando una mail in caso di esito negativo.....
Se il backup lo gestiamo attraverso il t-sql possiamo utilizzare questo script
CREATE TABLE #DBCC_CHECKDB ( Error int NULL, [Level] int NULL, State int NULL, MessageText nvarchar(2048) NULL, RepairLevel nvarchar(22) NULL, Status int NULL, DbId int NULL, DbFragId int NULL, ObjectId int NULL, IndexId int NULL, PartitionId bigint NULL, AllocUnitId bigint NULL, RidDbId smallint NULL, RidPruId smallint NULL, [File] smallint NULL, Page int NULL, Slot int NULL, RefDbId smallint NULL, RefPruId smallint NULL, RefFile smallint NULL, RefPage int NULL, RefSlot int NULL, Allocation smallint NULL ) INSERT INTO #DBCC_CHECKDB ( Error, [Level], State, MessageText, RepairLevel, Status, DbId, DbFragId, ObjectId, IndexId, PartitionId, AllocUnitId, RidDbId, RidPruId, [File], Page, Slot, RefDbId, RefPruId, RefFile, RefPage, RefSlot, Allocation ) EXEC('DBCC CHECKDB(RestorePage) WITH TABLERESULTS, ALL_ERRORMSGS ') If ( Select count(*) from #DBCC_CHECKDB Where Error >= 8000 and level >= 16 ) = 0 Backup Database RestorePage To disk = 'C:\SQL 2012\Backup\BackupFull.bak' Else throw 50000,'Errori!! Impossibile implementare il backup!!!',1 Drop table #DBCC_CHECKDB
NB: Lo script l'ho creato per SQL Server 2012.
Facciamo sempre un check dei nostri db, almeno prima dei backup full, se vogliamo evitare spiacevoli sorprese !!!
Ciao
Luca
Nessun commento:
Posta un commento