web-dev-qa-db-ja.com

FILESTREAMデータを除くデータベースを復元する

コンテキスト
大規模なデータベースが下部にあるシステムを開発しています。これは、SQL Server 2008 R2で実行されるMS SQLデータベースです。データベースの合計サイズは約12 GBです。

これらのうち、約8.5 GBが単一のテーブルBinaryContentにあります。名前が示すように、これはあらゆる種類の単純なファイルをBLOBとしてテーブルに直接格納するテーブルです。最近、FILESTREAMを使用して、これらのすべてのファイルをデータベースからファイルシステムに移動する可能性をテストしています。

問題なくデータベースに必要な変更を加えましたが、移行後もシステムは正常に動作しています。 BinaryContentテーブルは、おおよそ次のようになります。

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]

別のファイルグループPRIMARYにあるフィールドFileBinaryContentを除き、すべてがFileStreamContentFGファイルグループにあります。

シナリオ
開発者の観点からは、最新のデータを処理できるように、実稼働環境からデータベースの新しいコピーが必要になることがよくあります。これらの場合、BinaryContentに格納されているファイルに関心を持つことはほとんどありません(現在はFILESTREAMを使用しています)。

ほぼ問題なく動作しています。次のようなファイルストリームなしでデータベースをバックアップします。

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

次のように復元します。

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

これは問題なく動作しているようで、FileBinaryContentフィールドを使用する部分を避ければ、システムは機能します。たとえば、次のクエリを問題なく実行できます。

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

当然、クエリにFileContentBinaryを含めて上記の行のコメントを外すと、エラーが発生します。

テーブル "dbo.BinaryContent"のラージオブジェクト(LOB)データは、アクセスできないオフラインファイルグループ( "FileStreamContentFG")にあります。

私たちのシステムは、コンテンツがnullに設定されているファイルを処理するため、likeを実行すると、次のようになります。

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

しかし、もちろんこれは上記と同じエラーを私に与えます。この時点で私は行き詰まっています。

質問
FileStreamContentFGファイルグループからすべてを復元することなくデータベースを復元する方法はありますか?上記のように値をnullに更新するか、ファイルが見つからない場合などにデフォルトでnullに更新しますか?

それとも私はおそらく間違った方法で問題に取り組んでいますか?

私は本質的に開発者であり、DBAとしての知識があまりないため、ここで些細なことを見落としている場合は失礼します。

20
Julian

あなたがしようとしていることは、データベースを(トランザクション的に)一貫性のない状態のままにするため、それは不可能です。

部分的なデータベースの可用性に関するホワイトペーパー は有用なリファレンスガイドであり、特定のテーブルまたはファイルがオンラインかどうかを確認する方法の例が含まれています。データアクセスがストアドプロシージャを介して行われた場合、そのチェックを比較的簡単に組み込むことができます。

シナリオで一見の価値がある1つの代替(ただし多少ハックな)アプローチは、テーブルを非表示にしてビューに置き換えることです。

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

別のデータベースのFILESTREAMを使用してテーブルを分離し、ビューを使用してPRODUCTIONデータベースにテーブルへの参照を作成できます。

これにより、ハッキングに頼らずに、やりたいことができるようになります。

5
Bob