Restaurer la base de données à l'exclusion des données FILESTREAM


20

Contexte
Nous développons un système avec une base de données de grande taille en bas. Il s'agit d'une base de données MS SQL exécutée sur SQL Server 2008 R2. La taille totale de la base de données est d'environ 12 Go.

Parmi ceux-ci, environ 8,5 Go se trouvent dans une seule table BinaryContent. Comme son nom l'indique, il s'agit d'un tableau dans lequel nous stockons des fichiers simples, de toute nature, directement dans le tableau en tant que BLOB. Récemment, nous avons testé la possibilité de déplacer tous ces fichiers de la base de données vers le système de fichiers à l'aide de FILESTREAM.

Nous avons apporté les modifications nécessaires à notre base de données sans aucun problème, et notre système fonctionne toujours bien après la migration. Le BinaryContenttableau ressemble à peu près à ceci:

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]

Avec tout ce qui réside dans le PRIMARYgroupe de fichiers, sauf le champ FileBinaryContentqui se trouve dans un groupe de fichiers distinct FileStreamContentFG.

Scénario
Du point de vue d'un développeur, nous souhaitons souvent une nouvelle copie de la base de données de notre environnement de production, pour pouvoir travailler les dernières données. Dans ces cas, nous sommes rarement intéressés par les fichiers stockés dans BinaryContent (utilisant désormais FILESTREAM).

Nous fonctionnons presque comme nous le souhaiterions. Nous sauvegardons la base de données, sans le flux de fichiers comme ceci:

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

Et restaurez-le comme ceci:

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

Cela semble fonctionner correctement, et notre système fonctionne tant que nous évitons les pièces qui utilisent le FileBinaryContentchamp. Nous pouvons par exemple exécuter la requête suivante sans problème:

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

Naturellement, si je commente la ligne ci-dessus, y compris FileContentBinarydans la requête, j'obtiens une erreur:

Les données d'objets volumineux (LOB) pour la table "dbo.BinaryContent" résident sur un groupe de fichiers hors ligne ("FileStreamContentFG") auquel il n'est pas possible d'accéder.

Nos fichiers poignées de système où le contenu est défini sur null, donc ce que je veux faire est quelque chose comme ceci:

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

Mais cela me donne bien sûr la même erreur que ci-dessus. À ce stade, je suis coincé.

Question
Existe-t-il un moyen de restaurer la base de données sans avoir à tout restaurer également à partir duFileStreamContentFG groupe de fichiers? Soit en mettant à jour les valeurs à null comme j'essaye ci-dessus, soit par défaut à null lorsque le fichier est manquant ou quelque chose?

Ou est-ce que j'aborde peut-être le problème de la mauvaise façon?

Je suis un développeur par nature et je n'ai pas beaucoup de connaissances en tant que DBA, alors excusez-moi si je néglige quelque chose de trivial ici.


Pourriez-vous effectuer la restauration complète une fois afin de disposer de certaines données du [BinaryContent] FILEGROUP, puis effectuer une restauration du groupe de fichiers principal lorsque vous souhaitez le mettre à jour?
jgardner04

@ jgardner04: cela ne semble pas fonctionner. La base de données se retrouve dans un état incohérent si je fais d'abord une restauration complète, suivie d'une restauration de la sauvegarde contenant uniquement le groupe de fichiers principal (Message d'erreur: "La base de données ne peut pas être récupérée car le journal n'a pas été restauré (...) la base de données n'a pas pu être mise en ligne car une ou plusieurs étapes RESTORE sont nécessaires " ).
Julian

Votre accès à dbo.BinaryContent est-il toujours via des procédures stockées? Combien sont impliqués?
Mark Storey-Smith

@ MarkStorey-Smith: la base de données est principalement accessible à l'aide de requêtes régulières, via NHibernate (à la fois à partir d'une application Web ASP.NET et d'une application Windows Forms). En quoi est-ce pertinent?
Julian

2
Si votre accès se faisait via des procédures stockées, nous pourrions appliquer une approche de disponibilité partielle / restauration fragmentaire pour vérifier quels groupes de fichiers sont en ligne. Pour être honnête, à 12 Go, cela ne vaut vraiment pas la peine de travailler, juste pour faire la restauration complète.
Mark Storey-Smith

Réponses:


10

Ce que vous essayez de faire laisserait la base de données dans un état (transactionnellement) incohérent, donc ce n'est pas possible.

Le livre blanc sur la disponibilité partielle de la base de données est un guide de référence utile et comprend un exemple de vérification de la connexion en ligne d'une table ou d'un fichier particulier. Si votre accès aux données se faisait par le biais de procédures stockées, vous pourriez relativement facilement intégrer cette vérification.

Une approche alternative (mais quelque peu hacky) qui pourrait valoir la peine d'être examinée dans votre scénario serait de masquer la table et de la remplacer par une vue.

-- 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

Vous pouvez isoler la table avec un FILESTREAMdans une base de données distincte et créer une référence à celle-ci dans la PRODUCTIONbase de données à l'aide d'une vue.

Cela vous permettrait de faire ce que vous voulez sans avoir recours à des hacks.


Cela allait être mon approche, mais je me suis ensuite heurté à des problèmes de maintien de l'intégrité référentielle entre les bases de données, car les déclencheurs ne sont généralement pas pris en charge avec les tables de flux de fichiers: dba.stackexchange.com/questions/58208/…
John J Smith
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.