방법: 데이터베이스 백업 및 복원(SQL Server)
이 항목에서는 Sync Framework를 사용하여 동기화된 SQL Server 데이터베이스를 백업 및 복원하는 방법에 대해 설명합니다. 이 응용 프로그램의 코드에서는 다음과 같은 Sync Framework 클래스를 중점적으로 다룹니다.
샘플 코드를 실행하는 방법에 대한 자세한 내용은 SQL Server와 SQL Server Compact 동기화의 "방법 항목의 예제 응용 프로그램"을 참조하십시오.
동기화된 데이터베이스의 백업 및 복원 이해
프로덕션 데이터베이스와 마찬가지로 동기화에 포함되는 서버 데이터베이스도 정기적으로 백업해야 합니다. 백업에서 데이터베이스를 복원한 경우 Sync Framework에서는 동기화 메타데이터를 업데이트하여 백업을 만든 후에 발생할 수 있는 변경 내용과 관련된 문제를 해결해야 합니다. 이러한 변경 내용은 다음 3가지 범주로 나뉩니다.
클라이언트 또는 다른 피어 서버에서 적용된 변경 내용.
서버를 복원한 후 다른 노드와 동기화하면 이러한 원격 변경 내용이 적용되어 서버가 최신 상태가 됩니다.
서버에서 적용되어 적어도 하나의 클라이언트나 피어 서버에 전파된 변경 내용.
서버를 복원한 후 다른 노드와 동기화하면 백업에 포함되지 않은 이러한 로컬 변경 내용이 적용되어 서버가 최신 상태가 됩니다. 이 경우 올바른 처리를 위해 메타데이터가 업데이트됩니다.
서버에서 적용되었지만 클라이언트나 피어 서버에 전파되지 않은 변경 내용.
변경 내용이 백업의 일부가 아닌 다른 경우와 마찬가지로 이 유형의 변경 내용은 복원할 수 없습니다.
서버 복원 후 메타데이터 업데이트
테이블에서 발생하는 모든 변경 내용에 대해 Sync Framework에서는 변경 발생 지점과 시간을 나타내도록 서버의 메타데이터를 업데이트합니다. 다른 노드에서는 서버에서 필요한 행을 결정할 때 이 메타데이터를 사용합니다. 백업 후에 서버에서 변경 내용이 발생하고 복원 전에 다른 노드로 해당 변경 내용이 전파된 경우 다른 노드가 변경된 행 각각에 대한 메타데이터를 포함하게 됩니다. 따라서, 백업에서 서버를 복원한 후 서버에서 발생하는 변경 내용에 다른 노드에 이미 있는 식별자와 충돌하는 식별자가 지정될 수 있습니다. 이로 인해 동기화 중에 충돌이 발생하고 불일치가 발생할 수 있습니다.
이러한 문제를 방지하기 위해 Sync Framework에서는 서버에 새로운 복제본 ID를 할당하고 동기화되는 각 테이블에 대한 여러 메타데이터 열을 업데이트합니다.
서버를 복원하고 메타데이터를 업데이트하려면
서버 데이터베이스에 대한 단독 액세스 권한을 설정합니다. 이렇게 하면 데이터베이스가 준비될 때까지 로컬 작업이나 동기화 작업에서 데이터나 메타데이터를 업데이트하지 못하도록 방지할 수 있습니다.
전체 백업이나 차등 백업(해당되는 경우)에서 SQL Server 데이터베이스를 복원합니다.
PerformPostRestoreFixup을 실행합니다.
데이터베이스에 대한 모든 액세스 권한을 허용합니다. 이 시점부터 서버에서 로컬 변경 내용이 발생할 수 있으며 클라이언트 및 다른 피어 서버가 서버와 동기화될 수 있습니다.
예제
다음 코드 예제에서는 서버 데이터베이스의 백업을 수행하고, 샘플 Utility
클래스의 메서드를 호출하여 데이터베이스를 변경한 다음 해당 변경 내용을 동기화합니다.
// Backup the server database.
Utility.CreateDatabaseBackup();
// Make changes on the server: 1 insert, 1 update, and 1 delete.
Utility.MakeDataChangesOnNode(Utility.ConnStr_SqlSync_Server, "Customer");
// Synchronize the three changes.
syncOrchestrator = new SampleSyncOrchestrator(
new SqlCeSyncProvider("customer", clientSqlCe1Conn),
new SqlSyncProvider("customer", serverConn)
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "subsequent");
' Backup the server database.
Utility.CreateDatabaseBackup()
' Make changes on the server: 1 insert, 1 update, and 1 delete.
Utility.MakeDataChangesOnNode(Utility.ConnStr_SqlSync_Server, "Customer")
' Synchronize the three changes.
syncOrchestrator = New SampleSyncOrchestrator( _
New SqlCeSyncProvider("customer", clientSqlCe1Conn), _
New SqlSyncProvider("customer", serverConn))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "subsequent")
응용 프로그램에서는 서버 데이터베이스를 백업하는 다음과 같은 Transact-SQL 코드를 포함하는 저장 프로시저(usp_SampleDbBackupRestore
)를 호출합니다.
BACKUP DATABASE [SyncSamplesDb_SqlPeer1]
TO DISK = N'C:\Program Files\Microsoft SQL Server\MSSQL10.MSSQLSERVER\MSSQL\Backup\SyncSamplesDb_SqlPeer1.bak'
WITH NOFORMAT, NOINIT, NAME = N'SyncSamplesDb_SqlPeer1-Full Database Backup', SKIP, NOREWIND, NOUNLOAD, STATS = 10
이 프로시저에는 서버 데이터베이스를 복원하는 Transact-SQL 코드도 포함되어 있습니다.
-- Backup the tail of the log.
BACKUP LOG [SyncSamplesDb_SqlPeer1]
TO DISK = N'C:\Program Files\Microsoft SQL Server\MSSQL10.MSSQLSERVER\MSSQL\Backup\SyncSamplesDb_SqlPeer1.bak'
WITH NO_TRUNCATE , NOFORMAT, NOINIT, NAME = N'TestBackupRestore-Transaction Log Backup', SKIP, NOREWIND, NOUNLOAD, NORECOVERY , STATS = 10
-- Restore the database.
RESTORE DATABASE [SyncSamplesDb_SqlPeer1]
FROM DISK = N'C:\Program Files\Microsoft SQL Server\MSSQL10.MSSQLSERVER\MSSQL\Backup\SyncSamplesDb_SqlPeer1.bak'
WITH FILE = 1, NOUNLOAD, STATS = 10
다음 코드 예제에서는 서버 데이터베이스를 복원한 다음 PerformPostRestoreFixup()
을 호출하여 메타데이터를 업데이트합니다. 그런 다음 모든 노드를 다시 동기화합니다.
Utility.RestoreDatabaseFromBackup();
// Call the API to update synchronization metadata to reflect that the database was
// just restored. The restore stored procedure kills the connection to the
// server, so we must re-establish it.
SqlConnection.ClearPool(serverConn);
serverConn = new SqlConnection(Utility.ConnStr_SqlSync_Server);
SqlSyncStoreRestore databaseRestore = new SqlSyncStoreRestore(serverConn);
databaseRestore.PerformPostRestoreFixup();
Utility.RestoreDatabaseFromBackup()
' Call the API to update synchronization metadata to reflect that the database was
' just restored. The restore stored procedure kills the connection to the
' server, so we must re-establish it.
SqlConnection.ClearPool(serverConn)
serverConn = New SqlConnection(Utility.ConnStr_SqlSync_Server)
Dim databaseRestore As New SqlSyncStoreRestore(serverConn)
databaseRestore.PerformPostRestoreFixup()
전체 코드 예제
다음의 전체 코드 예제에는 위에서 설명한 코드 예제와 추가 코드가 포함되어 있습니다. 이 예제를 사용하려면 데이터베이스 공급자용 유틸리티 클래스 방법 항목에 나와 있는 Utility
클래스가 필요합니다.
// NOTE: Before running this application, run the database sample script that is
// available in the documentation. The script drops and re-creates the tables that
// are used in the code, and ensures that synchronization objects are dropped so that
// Sync Framework can re-create them.
using System;
using System.IO;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlServerCe;
using Microsoft.Synchronization;
using Microsoft.Synchronization.Data;
using Microsoft.Synchronization.Data.SqlServer;
using Microsoft.Synchronization.Data.SqlServerCe;
namespace Microsoft.Samples.Synchronization
{
class Program
{
static void Main(string[] args)
{
// Create the connections over which provisioning and synchronization
// are performed. The Utility class handles all functionality that is not
//directly related to synchronization, such as holding connection
//string information and making changes to the server database.
SqlConnection serverConn = new SqlConnection(Utility.ConnStr_SqlSync_Server);
SqlConnection clientSqlConn = new SqlConnection(Utility.ConnStr_SqlSync_Client);
SqlCeConnection clientSqlCe1Conn = new SqlCeConnection(Utility.ConnStr_SqlCeSync1);
// Create a scope named "customer", and add the Customer table to the scope.
// GetDescriptionForTable gets the schema of the table, so that tracking
// tables and triggers can be created for that table.
DbSyncScopeDescription scopeDesc = new DbSyncScopeDescription("customer");
scopeDesc.Tables.Add(
SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.Customer", serverConn));
// Create a provisioning object for "customer" and specify that
// base tables should not be created (They already exist in SyncSamplesDb_SqlPeer1).
SqlSyncScopeProvisioning serverConfig = new SqlSyncScopeProvisioning(scopeDesc);
serverConfig.SetCreateTableDefault(DbSyncCreationOption.Skip);
// Configure the scope and change-tracking infrastructure.
serverConfig.Apply(serverConn);
// Retrieve scope information from the server and use the schema that is retrieved
// to provision the SQL Server and SQL Server Compact client databases.
// This database already exists on the server.
DbSyncScopeDescription clientSqlDesc = SqlSyncDescriptionBuilder.GetDescriptionForScope("customer", serverConn);
SqlSyncScopeProvisioning clientSqlConfig = new SqlSyncScopeProvisioning(clientSqlDesc);
clientSqlConfig.Apply(clientSqlConn);
// This database does not yet exist.
Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync1, true);
DbSyncScopeDescription clientSqlCeDesc = SqlSyncDescriptionBuilder.GetDescriptionForScope("customer", serverConn);
SqlCeSyncScopeProvisioning clientSqlCeConfig = new SqlCeSyncScopeProvisioning(clientSqlCeDesc);
clientSqlCeConfig.Apply(clientSqlCe1Conn);
// Initial synchronization sessions.
SampleSyncOrchestrator syncOrchestrator;
SyncOperationStatistics syncStats;
// Data is downloaded from the server to the SQL Server client.
syncOrchestrator = new SampleSyncOrchestrator(
new SqlSyncProvider("customer", clientSqlConn),
new SqlSyncProvider("customer", serverConn)
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "initial");
// Data is downloaded from the SQL Server client to the
// SQL Server Compact client.
syncOrchestrator = new SampleSyncOrchestrator(
new SqlCeSyncProvider("customer", clientSqlCe1Conn),
new SqlSyncProvider("customer", clientSqlConn)
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "initial");
// Remove the backup file because this application requires a drop and recreation
// of the sample database, and the backup must be recreated each time
// the application runs.
Utility.DeleteDatabaseBackup();
// Backup the server database.
Utility.CreateDatabaseBackup();
// Make changes on the server: 1 insert, 1 update, and 1 delete.
Utility.MakeDataChangesOnNode(Utility.ConnStr_SqlSync_Server, "Customer");
// Synchronize the three changes.
syncOrchestrator = new SampleSyncOrchestrator(
new SqlCeSyncProvider("customer", clientSqlCe1Conn),
new SqlSyncProvider("customer", serverConn)
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "subsequent");
syncOrchestrator = new SampleSyncOrchestrator(
new SqlSyncProvider("customer", clientSqlConn),
new SqlCeSyncProvider("customer", clientSqlCe1Conn)
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "subsequent");
// Restore the server database from backup. The restored version
// does not contain the three changes that were just synchronized.
Utility.RestoreDatabaseFromBackup();
// Call the API to update synchronization metadata to reflect that the database was
// just restored. The restore stored procedure kills the connection to the
// server, so we must re-establish it.
SqlConnection.ClearPool(serverConn);
serverConn = new SqlConnection(Utility.ConnStr_SqlSync_Server);
SqlSyncStoreRestore databaseRestore = new SqlSyncStoreRestore(serverConn);
databaseRestore.PerformPostRestoreFixup();
// Synchronize a final time.
syncOrchestrator = new SampleSyncOrchestrator(
new SqlCeSyncProvider("customer", clientSqlCe1Conn),
new SqlSyncProvider("customer", serverConn)
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "subsequent");
syncOrchestrator = new SampleSyncOrchestrator(
new SqlSyncProvider("customer", clientSqlConn),
new SqlCeSyncProvider("customer", clientSqlCe1Conn)
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "subsequent");
serverConn.Close();
serverConn.Dispose();
clientSqlConn.Close();
clientSqlConn.Dispose();
clientSqlCe1Conn.Close();
clientSqlCe1Conn.Dispose();
Console.Write("\nPress any key to exit.");
Console.Read();
}
}
public class SampleSyncOrchestrator : SyncOrchestrator
{
public SampleSyncOrchestrator(RelationalSyncProvider localProvider, RelationalSyncProvider remoteProvider)
{
this.LocalProvider = localProvider;
this.RemoteProvider = remoteProvider;
this.Direction = SyncDirectionOrder.UploadAndDownload;
}
public void DisplayStats(SyncOperationStatistics syncStatistics, string syncType)
{
Console.WriteLine(String.Empty);
if (syncType == "initial")
{
Console.WriteLine("****** Initial Synchronization ******");
}
else if (syncType == "subsequent")
{
Console.WriteLine("***** Subsequent Synchronization ****");
}
Console.WriteLine("Start Time: " + syncStatistics.SyncStartTime);
Console.WriteLine("Total Changes Uploaded: " + syncStatistics.UploadChangesTotal);
Console.WriteLine("Total Changes Downloaded: " + syncStatistics.DownloadChangesTotal);
Console.WriteLine("Complete Time: " + syncStatistics.SyncEndTime);
Console.WriteLine(String.Empty);
}
}
}
' NOTE: Before running this application, run the database sample script that is
' available in the documentation. The script drops and re-creates the tables that
' are used in the code, and ensures that synchronization objects are dropped so that
' Sync Framework can re-create them.
Imports System
Imports System.IO
Imports System.Text
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.SqlServerCe
Imports Microsoft.Synchronization
Imports Microsoft.Synchronization.Data
Imports Microsoft.Synchronization.Data.SqlServer
Imports Microsoft.Synchronization.Data.SqlServerCe
Namespace Microsoft.Samples.Synchronization
Class Program
Public Shared Sub Main(ByVal args As String())
' Create the connections over which provisioning and synchronization
' are performed. The Utility class handles all functionality that is not
'directly related to synchronization, such as holding connection
'string information and making changes to the server database.
Dim serverConn As New SqlConnection(Utility.ConnStr_SqlSync_Server)
Dim clientSqlConn As New SqlConnection(Utility.ConnStr_SqlSync_Client)
Dim clientSqlCe1Conn As New SqlCeConnection(Utility.ConnStr_SqlCeSync1)
' Create a scope named "customer", and add the Customer table to the scope.
' GetDescriptionForTable gets the schema of the table, so that tracking
' tables and triggers can be created for that table.
Dim scopeDesc As New DbSyncScopeDescription("customer")
scopeDesc.Tables.Add(SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.Customer", serverConn))
' Create a provisioning object for "customer" and specify that
' base tables should not be created (They already exist in SyncSamplesDb_SqlPeer1).
Dim serverConfig As New SqlSyncScopeProvisioning(scopeDesc)
serverConfig.SetCreateTableDefault(DbSyncCreationOption.Skip)
' Configure the scope and change-tracking infrastructure.
serverConfig.Apply(serverConn)
' Retrieve scope information from the server and use the schema that is retrieved
' to provision the SQL Server and SQL Server Compact client databases.
' This database already exists on the server.
Dim clientSqlDesc As DbSyncScopeDescription = SqlSyncDescriptionBuilder.GetDescriptionForScope("customer", serverConn)
Dim clientSqlConfig As New SqlSyncScopeProvisioning(clientSqlDesc)
clientSqlConfig.Apply(clientSqlConn)
' This database does not yet exist.
Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync1, True)
Dim clientSqlCeDesc As DbSyncScopeDescription = SqlSyncDescriptionBuilder.GetDescriptionForScope("customer", serverConn)
Dim clientSqlCeConfig As New SqlCeSyncScopeProvisioning(clientSqlCeDesc)
clientSqlCeConfig.Apply(clientSqlCe1Conn)
' Initial synchronization sessions.
Dim syncOrchestrator As SampleSyncOrchestrator
Dim syncStats As SyncOperationStatistics
' Data is downloaded from the server to the SQL Server client.
syncOrchestrator = New SampleSyncOrchestrator( _
New SqlSyncProvider("customer", clientSqlConn), _
New SqlSyncProvider("customer", serverConn))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "initial")
' Data is downloaded from the SQL Server client to the
' SQL Server Compact client.
syncOrchestrator = New SampleSyncOrchestrator( _
New SqlCeSyncProvider("customer", clientSqlCe1Conn), _
New SqlSyncProvider("customer", clientSqlConn))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "initial")
' Remove the backup file because this application requires a drop and recreation
' of the sample database, and the backup must be recreated each time
' the application runs.
Utility.DeleteDatabaseBackup()
' Backup the server database.
Utility.CreateDatabaseBackup()
' Make changes on the server: 1 insert, 1 update, and 1 delete.
Utility.MakeDataChangesOnNode(Utility.ConnStr_SqlSync_Server, "Customer")
' Synchronize the three changes.
syncOrchestrator = New SampleSyncOrchestrator( _
New SqlCeSyncProvider("customer", clientSqlCe1Conn), _
New SqlSyncProvider("customer", serverConn))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "subsequent")
syncOrchestrator = New SampleSyncOrchestrator( _
New SqlSyncProvider("customer", clientSqlConn), _
New SqlCeSyncProvider("customer", clientSqlCe1Conn))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "subsequent")
' Restore the server database from backup. The restored version
' does not contain the three changes that were just synchronized.
Utility.RestoreDatabaseFromBackup()
' Call the API to update synchronization metadata to reflect that the database was
' just restored. The restore stored procedure kills the connection to the
' server, so we must re-establish it.
SqlConnection.ClearPool(serverConn)
serverConn = New SqlConnection(Utility.ConnStr_SqlSync_Server)
Dim databaseRestore As New SqlSyncStoreRestore(serverConn)
databaseRestore.PerformPostRestoreFixup()
' Synchronize a final time.
syncOrchestrator = New SampleSyncOrchestrator( _
New SqlCeSyncProvider("customer", clientSqlCe1Conn), _
New SqlSyncProvider("customer", serverConn))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "subsequent")
syncOrchestrator = New SampleSyncOrchestrator( _
New SqlSyncProvider("customer", clientSqlConn), _
New SqlCeSyncProvider("customer", clientSqlCe1Conn))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "subsequent")
serverConn.Close()
serverConn.Dispose()
clientSqlConn.Close()
clientSqlConn.Dispose()
clientSqlCe1Conn.Close()
clientSqlCe1Conn.Dispose()
Console.Write(vbLf & "Press any key to exit.")
Console.Read()
End Sub
End Class
Public Class SampleSyncOrchestrator
Inherits SyncOrchestrator
Public Sub New(ByVal localProvider As RelationalSyncProvider, ByVal remoteProvider As RelationalSyncProvider)
Me.LocalProvider = localProvider
Me.RemoteProvider = remoteProvider
Me.Direction = SyncDirectionOrder.UploadAndDownload
End Sub
Public Sub DisplayStats(ByVal syncStatistics As SyncOperationStatistics, ByVal syncType As String)
Console.WriteLine([String].Empty)
If syncType = "initial" Then
Console.WriteLine("****** Initial Synchronization ******")
ElseIf syncType = "subsequent" Then
Console.WriteLine("***** Subsequent Synchronization ****")
End If
Console.WriteLine("Start Time: " & syncStatistics.SyncStartTime)
Console.WriteLine("Total Changes Uploaded: " & syncStatistics.UploadChangesTotal)
Console.WriteLine("Total Changes Downloaded: " & syncStatistics.DownloadChangesTotal)
Console.WriteLine("Complete Time: " & syncStatistics.SyncEndTime)
Console.WriteLine([String].Empty)
End Sub
End Class
End Namespace