Restaurar base de datos excluyendo datos FILESTREAM


20

Contexto
Estamos desarrollando un sistema con una base de datos de gran tamaño en la parte inferior. Es una base de datos MS SQL que se ejecuta en SQL Server 2008 R2. El tamaño total de la base de datos es de aproximadamente 12 GB.

De estos, aproximadamente 8,5 GB están en una sola tabla BinaryContent. Como su nombre indica, esta es una tabla donde almacenamos archivos simples, de cualquier tipo, directamente en la tabla como un BLOB. Recientemente hemos estado probando la posibilidad de mover todos estos archivos de la base de datos al sistema de archivos usando FILESTREAM.

Hicimos las modificaciones necesarias a nuestra base de datos sin ningún problema, y ​​nuestro sistema aún funciona bien después de la migración. La BinaryContenttabla se ve más o menos así:

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 todo lo que reside en el PRIMARYgrupo de archivos, excepto el campo FileBinaryContentque está en un grupo de archivos separado FileStreamContentFG.

Escenario
Desde el punto de vista del desarrollador, a menudo nos gustaría una copia nueva de la base de datos de nuestro entorno de producción, para poder trabajar con los datos más recientes. En esos casos, rara vez estamos interesados ​​en los archivos almacenados en BinaryContent (ahora usando FILESTREAM).

Tenemos esto casi funcionando como nos gustaría. Realizamos una copia de seguridad de la base de datos, sin la secuencia de archivos como esta:

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

Y restaurarlo así:

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

Esto parece estar funcionando bien, y nuestro sistema funciona siempre y cuando evitemos las partes que usan el FileBinaryContentcampo. Por ejemplo, podemos ejecutar la siguiente consulta sin problemas:

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

Naturalmente, si descomento la línea anterior, incluida FileContentBinaryen la consulta, aparece un error:

Los datos de objetos grandes (LOB) para la tabla "dbo.BinaryContent" residen en un grupo de archivos sin conexión ("FileStreamContentFG") al que no se puede acceder.

Nuestro sistema maneja archivos donde el contenido está configurado null, así que lo que me gustaría hacer es algo como esto:

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

Pero esto, por supuesto, me da el mismo error que el anterior. En este punto estoy atascado.

Pregunta
¿Hay alguna forma de restaurar la base de datos sin tener que restaurar todo desde el FileStreamContentFGgrupo de archivos? ¿Actualizando los valores a nulo como lo intento anteriormente, o por defecto a nulo cuando falta el archivo o algo así?

¿O tal vez estoy abordando el problema de manera incorrecta?

Soy desarrollador por naturaleza y no tengo mucho conocimiento como DBA, así que discúlpeme si estoy pasando por alto algo trivial aquí.


¿Podría hacer la restauración completa una vez para tener algunos datos del [BinaryContent] FILEGROUP y luego restaurar el grupo de archivos primario cuando desee actualizarlo?
jgardner04

@ jgardner04: eso no parece funcionar. La base de datos termina en un estado inconsistente si primero hago una restauración completa, seguido de una restauración de la copia de seguridad que contiene solo el grupo de archivos Primario (Mensaje de error: "La base de datos no se puede recuperar porque el registro no se restauró (...) la base de datos no se pudo poner en línea porque se necesitan uno o más pasos de RESTAURACIÓN " ).
Julian

¿Su acceso a dbo.BinaryContent siempre se realiza mediante procedimientos almacenados? ¿Cuántos están involucrados?
Mark Storey-Smith

@ MarkStorey-Smith: se accede principalmente a la base de datos mediante consultas regulares, a través de NHibernate (tanto desde una aplicación web ASP.NET como desde una aplicación de formularios de Windows). ¿Cómo es eso relevante?
Julián

2
Si su acceso fue a través de procedimientos almacenados, podríamos aplicar un enfoque de disponibilidad parcial / restauración gradual para verificar qué grupos de archivos están en línea. Para ser sincero, con 12 GB realmente no vale la pena trabajar, solo para hacer la restauración completa.
Mark Storey-Smith

Respuestas:


10

Lo que intenta hacer dejaría la base de datos en un estado inconsistente (transaccionalmente), por lo tanto, no es posible.

El documento técnico de Disponibilidad parcial de la base de datos es una guía de referencia útil e incluye un ejemplo de cómo verificar si una tabla o archivo en particular está en línea. Si su acceso a los datos fuera a través de procedimientos almacenados, podría incorporar relativamente fácilmente esa verificación.

Un enfoque alternativo (pero algo hacky) que podría valer la pena en su escenario sería ocultar la tabla y reemplazarla 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

Puede aislar la tabla con una FILESTREAMen una base de datos separada y crear una referencia a ella en la PRODUCTIONbase de datos utilizando una vista.

Esto te permitiría hacer lo que quieras sin recurrir a hacks.


Este iba a ser mi enfoque, pero luego me encontré con problemas para mantener la integridad referencial entre las bases de datos, ya que los disparadores generalmente no son compatibles con las tablas de flujo de archivos
John J Smith
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.