Pagine

mercoledì 6 marzo 2013

SQL Server 2012 : Datapage Repair

A causa di errori del disco, del controller, sbalzi di tensione o altri casi particolari i dati contenuti nei DB potrebbero danneggiarsi e diventare inutilizzabili. Il problema è che potremmo non accorgerci del danno fino a che quei dati precisi non ci servono.
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