Consider a very large, multi-terabyte
database in use 24/7. One of the challenges with databases of this size
is the length of time taken for various administration tasks such as
backups and the effect such operations have on database users.
In
this section we'll walk through the process of an online piecemeal
restore using filegroups.
The database used for our examples is structured as shown in listing 1. This code creates a database with three filegroups.
Example 1. Creating a database with multiple filegroups for online restore
-- Create "Sales" database with 3 secondary filegroups -- Each filegroup has 2 files and 1 table
CREATE DATABASE [Sales] ON PRIMARY ( NAME = N'Sales' , FILENAME = N'E:\SQL Data\Sales.mdf' , SIZE = 51200KB , FILEGROWTH = 1024KB )
, FILEGROUP [FG1] ( NAME = N'Sales1' , FILENAME = N'E:\SQL Data\Sales1.ndf' , SIZE = 51200KB , FILEGROWTH = 10240KB ), ( NAME = N'Sales2' , FILENAME = N'E:\SQL Data\Sales2.ndf' , SIZE = 51200KB , FILEGROWTH = 10240KB )
, FILEGROUP [FG2] ( NAME = N'Sales3' , FILENAME = N'E:\SQL Data\Sales3.ndf' , SIZE = 51200KB , FILEGROWTH = 10240KB ), ( NAME = N'Sales4' , FILENAME = N'E:\SQL Data\Sales4.ndf' , SIZE = 51200KB , FILEGROWTH = 10240KB )
, FILEGROUP [FG3] ( NAME = N'Sales5' , FILENAME = N'E:\SQL Data\Sales5.ndf' , SIZE = 51200KB , FILEGROWTH = 10240KB ), ( NAME = N'Sales6' , FILENAME = N'E:\SQL Data\Sales6.ndf' , SIZE = 51200KB , FILEGROWTH = 10240KB ) LOG ON ( NAME = N'Sales_log' , FILENAME = N'F:\SQL Log\Sales_log.ldf' , SIZE = 10240KB , FILEGROWTH = 10% ) GO
-- Set FG1 to be the default filegroup ALTER DATABASE [Sales] MODIFY FILEGROUP [FG1] DEFAULT GO
USE [SALES] GO
-- Create a table on each filegroup CREATE TABLE dbo.Table_1 ( Col1 nchar(10) NULL ) ON FG1 GO
CREATE TABLE dbo.Table_2 ( Col1 nchar(10) NULL ) ON FG2 GO
CREATE TABLE dbo.Table_3 ( Col1 nchar(10) NULL ) ON FG3 GO
|
As you can see in listing 1,
we've created a database with three filegroups and one table on each.
We've also ensured that user objects won't be created in the primary
filegroup by marking the secondary filegroup, FG1, as the default. Listing 2
sets up the basis for our restore by seeding the tables and making a
filegroup backup of the primary and secondary filegroups. For this
example, all of the filegroup backups occur in sequence, but in a real
example, we'd perform the filegroup backups over a number of nights to
reduce the nightly backup impact. Once the filegroup backups are
complete, we'll modify some data for a transaction log backup in a
later step.
Example 2. Filegroup backups
-- Seed tables INSERT table_1 VALUES ('one') GO
INSERT table_2 VALUES ('two') GO
INSERT table_3 VALUES ('three') GO
-- Take FileGroup Backups BACKUP DATABASE [Sales] FILEGROUP = N'PRIMARY' TO DISK = N'G:\SQL Backup\Sales_Primary_FG.bak' WITH INIT GO
BACKUP DATABASE [Sales] FILEGROUP = N'FG1' TO DISK = N'G:\SQL Backup\Sales_FG1_FG.bak' WITH INIT GO
BACKUP DATABASE [Sales] FILEGROUP = N'FG2' TO DISK = N'G:\SQL Backup\Sales_FG2_FG.bak' WITH INIT GO
BACKUP DATABASE [Sales] FILEGROUP = N'FG3' TO DISK = N'G:\SQL Backup\Sales_FG3_FG.bak' WITH INIT GO
-- Modify data on FG2 INSERT table_2 VALUES ('two - two') GO
|
At
this point, let's imagine that the disk(s) containing the filegroups is
completely destroyed, but the transaction log disk is okay. Restoring a
multi-terabyte database as a single unit would take a fair amount of
time, during which the entire database would be unavailable. With
filegroup restores, what we can do is prioritize the restores in order
to make the most important data available as soon as possible. So for
this example, let's imagine that filegroup 2 was the most important
filegroup from a user perspective. Let's get filegroup 2 back up and
running first (see listing 3).
Example 3. Online piecemeal restore: restoring the most important filegroup first
-- Disaster at this point. Prioritize Restore of Filegroup 2 USE MASTER GO
-- Start by performing a tail backup BACKUP LOG [Sales] TO DISK = N'G:\SQL Backup\Sales_log_tail.bak' WITH NORECOVERY, NO_TRUNCATE GO
-- recover Primary and FG2 RESTORE DATABASE [Sales] FILEGROUP='Primary' FROM DISK = N'G:\SQL Backup\Sales_Primary_FG.bak' WITH PARTIAL, NORECOVERY
RESTORE DATABASE [Sales] FILEGROUP='FG2' FROM DISK = N'G:\SQL Backup\Sales_FG2_FG.bak' WITH NORECOVERY
RESTORE LOG [Sales]
FROM DISK = N'G:\SQL Backup\Sales_log_tail.bak' WITH RECOVERY GO
-- At this point the database is up and running for Filegroup 2 only -- Other filegroups can now be restored in the order required
|
As shown in listing 3,
the first step in performing a piecemeal restore is to back up the tail
of the transaction log. This will enable us to restore up to the point
of failure. Once this backup is completed, we can then start the
restore process by restoring the primary filegroup. According to our
best practice, this is very small as it contains system objects only.
As a result, the primary filegroup restore is quick, and as soon as it
completes, the database is online and available for us to prioritize
the remainder of the filegroup restores.
In
line with our priorities, we proceed with a restore of the FG2
filegroup. The last statement restores the transaction log tail backup,
which rolls forward transactions for FG2. At this point, FG2 is online
and available for users to query. Attempting to query tables 1 and 3 at
this moment will fail as these filegroups are offline pending restore.
An error message will appear when you attempt to access these tables:
Msg 8653, Level 16, State 1, Line 1
The query processor is unable to produce a plan for the table or view
'table_3' because the table resides in a filegroup which is not online.
Let's recover the remaining filegroups now, as shown in listing 4.
Example 4. Online piecemeal restore for remaining filegroups
-- restore FG1 RESTORE DATABASE [Sales] FILEGROUP='FG1' FROM DISK = N'G:\SQL Backup\Sales_FG1_FG.bak' WITH NORECOVERY
RESTORE LOG [Sales] FROM DISK = N'G:\SQL Backup\Sales_log_tail.bak' WITH RECOVERY GO
-- restore FG3 RESTORE DATABASE [Sales] FILEGROUP='FG3' FROM DISK = N'G:\SQL Backup\Sales_FG3_FG.bak' WITH NORECOVERY
RESTORE LOG [Sales] FROM DISK = N'G:\SQL Backup\Sales_log_tail.bak' WITH RECOVERY GO
|
Listing 4 assumed all
the filegroups were damaged and needed to be restored. Should some of
the filegroups be undamaged, then a restore for those filegroups is
unnecessary. Let's imagine that filegroups 1 and 3 were undamaged.
After FG2 is restored, we can bring the remaining filegroups online
with a simple recovery statement such as this:
RESTORE DATABASE [Sales] FILEGROUP='FG1', FILEGROUP='FG3' WITH RECOVERY
While
normal full backup/restores on single filegroup databases may be
acceptable for small to medium databases, very large databases require
more thought to reduce the backup impact and minimize user downtime
during recovery scenarios. By placing user objects on secondary
filegroups, filegroup backups and the online piecemeal restore process
enable both of these goals to be met.
As
we covered earlier, online restores are available only in the
Enterprise edition of SQL Server 2005 and 2008. Another Enterprise-only
feature is the database snapshot, which we explore next.