Відновлення бази даних, виключаючи дані FILESTREAM


20

Контекст
Ми розробляємо систему з великою базою даних внизу. Це база даних MS SQL, що працює на SQL Server 2008 R2. Загальний розмір бази даних - близько 12 ГБ.

З них приблизно 8,5 ГБ знаходиться в одній таблиці 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запит, я отримую помилку:

Дані великих об'єктів (LOB) для таблиці "dbo.BinaryContent" розміщені в автономній файловій групі ("FileStreamContentFG"), до якої не можна отримати доступ.

Наша система обробляє файли, де встановлено вміст null, тож я хотів би зробити це приблизно так:

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

Але це, звичайно, дає мені ту саму помилку, що і вище. На цьому момент я застряг.

Питання:
Чи є можливість відновити базу даних, не маючи необхідності також відновити все з FileStreamContentFGгрупи файлів? Чи оновленням значень до null, як я намагаюся вище, або за замовчуванням до null, коли файл відсутній чи щось?

Або я, можливо, підходжу до проблеми неправильно?

Я за своєю природою розробник і не маю великих знань як DBA, тому вибачте мене, якщо я тут не помічаю якусь дрібницю.


Чи можете ви зробити повне відновлення один раз, щоб у вас з’явилися деякі дані з [BinaryContent] FILEGROUP, а потім зробити відновлення первинної файлової групи, коли ви хочете оновити її?
jgardner04

@ jgardner04: це, здається, не працює. База даних закінчується в непослідовному стані, якщо спочатку я виконаю повне відновлення, після чого слід відновити резервну копію, що містить лише первинну групу файлів (повідомлення про помилку: "Базу даних неможливо відновити, оскільки журнал не був відновлений (...) не вдалося вивести базу даних в Інтернет, оскільки потрібен один або кілька кроків RESTORE " ).
Джуліан

Чи завжди ваш доступ до dbo.BinaryContent завжди зберігається через збережені процедури? Скільки бере участь?
Марк Сторі-Сміт

@ MarkStorey-Smith: Доступ до бази даних в основному здійснюється за допомогою регулярних запитів через NHibernate (як з веб-програми ASP.NET, так і з додатка для форм Windows). Наскільки це актуально?
Джуліан

2
Якщо ваш доступ був за допомогою збережених процедур, ми можемо застосувати підхід із часткового відновлення доступності / частинок, щоб перевірити, які групи файлів є в Інтернеті. Якщо чесно, то в 12 Гб насправді не варто обійтися, просто зробити повне відновлення.
Марк Сторі-Сміт

Відповіді:


10

Те, що ви намагаєтеся зробити, залишило б базу даних у (транзакційно) непослідовному стані, отже, це неможливо.

Часткова доступність бази даних Whitepaper є корисним довідником і включає в себе приклад того , як перевірити , є чи конкретна таблиця або файл в Інтернеті. Якщо ваш доступ до даних здійснювався через збережені процедури, ви можете відносно легко включити цю перевірку.

Одним із альтернативних (але дещо химерних) підходів, на який, можливо, варто звернути увагу у вашому сценарії, було б приховати таблицю та замінити її поданням.

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

Ви можете ізолювати таблицю з a FILESTREAMв окремій базі даних і створити посилання на неї в PRODUCTIONбазі даних за допомогою представлення.

Це дозволило б робити те, що ви хочете, не вдаючись до хак.


Це був би мій підхід, але тоді я зіткнувся з питаннями збереження референтної цілісності між базами даних, оскільки тригери, як правило, не підтримуються таблицями файлових потоків
Джон Дж. Сміт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.