Ripristina database esclusi i dati FILESTREAM


20

Contesto
Stiamo sviluppando un sistema con un database di grandi dimensioni in fondo. È un database MS SQL in esecuzione su SQL Server 2008 R2. La dimensione totale del database è di circa 12 GB.

Di questi, circa 8,5 GB si trovano in una singola tabella BinaryContent. Come suggerisce il nome, questa è una tabella in cui archiviamo file semplici, di qualsiasi tipo, direttamente nella tabella come BLOB. Recentemente abbiamo testato la possibilità di spostare tutti questi file dal database nel file system usando FILESTREAM.

Abbiamo apportato le modifiche necessarie al nostro database senza problemi e il nostro sistema funziona ancora bene dopo la migrazione. La BinaryContenttabella appare più o meno così:

CREATE TABLE [dbo].[BinaryContent](
    [BinaryContentID] [int] IDENTITY(1,1) NOT NULL,
    [FileName] [varchar](50) NOT NULL,
    [BinaryContentRowGUID] [uniqueidentifier] ROWGUIDCOL  NOT NULL
) ON [PRIMARY] FILESTREAM_ON [FileStreamContentFG]
ALTER TABLE [dbo].[BinaryContent] ADD [FileContentBinary] [varbinary](max) FILESTREAM  NULL
ALTER TABLE [dbo].[BinaryContent] ADD  CONSTRAINT [DFBinaryContentRowGUID]  DEFAULT (newsequentialid()) FOR [BinaryContentRowGUID]

Con tutto ciò che risiede nel PRIMARYgruppo di file, tranne il campo FileBinaryContentche si trova in un gruppo di file separato FileStreamContentFG.

Scenario
Dal punto di vista di uno sviluppatore, vorremmo spesso una nuova copia del database dal nostro ambiente di produzione, per poter lavorare con i dati più recenti. In questi casi, siamo raramente interessati ai file archiviati in BinaryContent (ora usando FILESTREAM).

Abbiamo quasi funzionato come vorremmo. Eseguiamo il backup del database, senza il flusso di file in questo modo:

BACKUP DATABASE FileStreamDB
FILEGROUP = 'PRIMARY' 
TO DISK = 'c:\backup\FileStreamDB_WithoutFS.bak' WITH INIT

E ripristinalo in questo modo:

RESTORE DATABASE FileStreamDB
FROM DISK = 'c:\backup\FileStreamDB_WithoutFS.bak'

Questo sembra funzionare correttamente e il nostro sistema funziona fintanto che evitiamo le parti che utilizzano il FileBinaryContentcampo. Ad esempio, possiamo eseguire la seguente query senza problemi:

SELECT TOP 10 [BinaryContentID],[FileName],[BinaryContentRowGUID]
--,[FileContentBinary]
FROM [dbo].[BinaryContent]

Naturalmente, se annullo il commento alla riga precedente, incluso FileContentBinarynella query, viene visualizzato un errore:

I dati di oggetti di grandi dimensioni (LOB) per la tabella "dbo.BinaryContent" risiedono in un filegroup offline ("FileStreamContentFG") a cui non è possibile accedere.

Le nostre maniglie file di sistema in cui il contenuto è impostato null, in modo da quello che avrei piacerebbe fare è qualcosa di simile a questo:

UPDATE [dbo].[BinaryContent]
SET [FileContentBinary] = null

Ma questo ovviamente mi dà lo stesso errore di cui sopra. A questo punto sono bloccato.

Domanda
Esiste un modo per ripristinare il database senza dover ripristinare anche tutto dal FileStreamContentFGgruppo di file? O aggiornando i valori su null come sto provando sopra, o per impostazione predefinita su null quando manca il file o qualcosa del genere?

O forse sto affrontando il problema nel modo sbagliato?

Sono uno sviluppatore per natura e non ho molta conoscenza come DBA, quindi scusami se sto trascurando qualcosa di banale qui.


È possibile eseguire il ripristino completo una volta in modo da disporre di alcuni dati dal FILEGROUP [BinaryContent] e quindi eseguire un ripristino del filegroup primario quando si desidera aggiornarlo?
jgardner04,

@ jgardner04: non sembra funzionare. Il database finisce in uno stato incoerente se prima eseguo un ripristino completo, seguito da un ripristino del backup contenente solo il gruppo di file primario (Messaggio di errore: "Impossibile ripristinare il database perché il registro non è stato ripristinato (...) impossibile portare in linea il database perché sono necessari uno o più passaggi di RIPRISTINO " ).
Giuliano,

Il tuo accesso a dbo.BinaryContent è sempre tramite procedure memorizzate? Quanti ne sono coinvolti?
Mark Storey-Smith,

@ MarkStorey-Smith: l'accesso al database avviene principalmente tramite query regolari, tramite NHibernate (sia da un'applicazione Web ASP.NET che da un'applicazione Windows Form). Quanto è rilevante?
Giuliano,

2
Se l'accesso avvenisse tramite procedure memorizzate, potremmo applicare un approccio da disponibilità parziale / ripristino parziale per verificare quali filegroup sono online. Ad essere onesti, a 12 GB non vale davvero la pena aggirare, solo per fare il ripristino completo.
Mark Storey-Smith,

Risposte:


10

Quello che stai cercando di fare lascerebbe il database in uno stato (transazionale) incoerente, quindi non è possibile.

Il white paper sulla disponibilità del database parziale è un'utile guida di riferimento e include un esempio di come verificare se una determinata tabella o file è online. Se l'accesso ai dati avvenisse tramite procedure memorizzate, è possibile incorporare relativamente facilmente tale controllo.

Un approccio alternativo (ma un po 'confuso) che potrebbe valere la pena dare un'occhiata nel tuo scenario sarebbe quello di nascondere la tabella e sostituirla con una vista.

-- NB: SQLCMD script
:ON ERROR EXIT
:setvar DatabaseName "TestRename"
:setvar FilePath "D:\MSSQL\I3\Data\"

SET STATISTICS TIME OFF;
SET STATISTICS IO OFF;
SET NOCOUNT ON;
GO

USE master;
GO

IF EXISTS (SELECT name FROM sys.databases WHERE name = N'$(DatabaseName)')
  DROP DATABASE $(DatabaseName)
GO

CREATE DATABASE $(DatabaseName) 
ON PRIMARY 
  (
  NAME = N' $(DatabaseName)'
  , FILENAME = N'$(FilePath)$(DatabaseName).mdf'
  , SIZE = 5MB
  , MAXSIZE = UNLIMITED
  , FILEGROWTH = 1MB
  ) 
, FILEGROUP [FG1] DEFAULT
  ( 
  NAME = N' $(DatabaseName)_FG1_File1'
  , FILENAME = N'$(FilePath)$(DatabaseName)_FG1_File1.ndf'
  , SIZE = 1MB
  , MAXSIZE = UNLIMITED
  , FILEGROWTH = 1MB 
  ) 
, FILEGROUP [FG2] CONTAINS FILESTREAM
  ( 
  NAME = N'$(DatabaseName)_FG2'
  , FILENAME = N'$(FilePath)Filestream'
  )
LOG ON 
  ( 
  NAME = N'$(DatabaseName)_log'
  , FILENAME = N'$(FilePath)$(DatabaseName)_log.ldf'
  , SIZE = 1MB
  , MAXSIZE = UNLIMITED
  , FILEGROWTH = 1MB
  )
GO

USE $(DatabaseName);
GO

CREATE TABLE [dbo].[BinaryContent](
    [BinaryContentID] [int] IDENTITY(1,1) NOT NULL
    , [FileName] [varchar](50) NOT NULL
    , [BinaryContentRowGUID] [uniqueidentifier] ROWGUIDCOL UNIQUE DEFAULT (NEWSEQUENTIALID()) NOT NULL
  , [FileContentBinary] VARBINARY(max) FILESTREAM  NULL
) ON [PRIMARY] FILESTREAM_ON [FG2]
GO 

-- Insert test rows
INSERT
  dbo.BinaryContent
  (
  [FileName]
  , [FileContentBinary]
  )
VALUES
  (
  CAST(NEWID() AS VARCHAR(36))
  , CAST(REPLICATE(NEWID(), 100) AS VARBINARY)
  );
GO 100

USE master;
GO

-- Take FILESTREAM filegroup offline
ALTER DATABASE $(DatabaseName)
MODIFY FILE (NAME = '$(DatabaseName)_FG2', OFFLINE)
GO

USE $(DatabaseName);
GO

-- Rename table to make way for view
EXEC sp_rename 'dbo.BinaryContent', 'BinaryContentTable', 'OBJECT';
GO

-- Create view to return content from table but with NULL FileContentBinary
CREATE VIEW dbo.BinaryContent
AS

SELECT
  [BinaryContentID]
    , [FileName] 
    , [BinaryContentRowGUID]
  , [FileContentBinary] = NULL
FROM
  [dbo].[BinaryContentTable];
GO

-- Check results as expected
SELECT TOP 10
  *
FROM
  dbo.BinaryContent;
GO

5

È possibile isolare la tabella con a FILESTREAMin un database separato e creare un riferimento ad essa nel PRODUCTIONdatabase utilizzando una vista.

Ciò ti consentirebbe di fare ciò che desideri senza ricorrere agli hack.


Questo sarebbe stato il mio approccio, ma poi mi sono imbattuto in problemi di mantenimento dell'integrità referenziale tra i database, poiché i trigger non sono generalmente supportati con le tabelle filestream
John J Smith,
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.