Realizar copias de seguridad y restauración de una base de datos (SQL Server)
En este tema se describe la forma de realizar una copia de seguridad y restaurar una base de datos de SQL Server que está sincronizada utilizando Sync Framework. El código de esta aplicación se centra en las clases de Sync Framework siguientes:
Para obtener información acerca de cómo se ejecuta el código de ejemplo, vea "Aplicaciones de ejemplo en los temas sobre procedimientos" en Sincronizar SQL Server y SQL Server Compact.
Descripción de la realización de copias de seguridad y restauración de bases de datos que están sincronizadas
Como haría con cualquier base de datos de producción, debe crear copias de seguridad regulares de una base de datos servidor involucrada en una sincronización. Si restaura una base de datos a partir de una copia de seguridad, Sync Framework debe actualizar los metadatos de sincronización para resolver los problemas con los cambios que pueden producirse después de realizarse la copia de seguridad. Los cambios se clasifican en tres categorías:
El cambio se realizó en un cliente u otro servidor del mismo nivel.
Una vez restaurado el servidor, la sincronización con otros nodos actualizará finalmente el servidor con estos cambios remotos.
El cambio se realizó en el servidor y se difundió al menos a un cliente o servidor del mismo nivel.
Una vez restaurado el servidor, la sincronización con otros nodos actualizará finalmente el servidor con estos cambios locales que no se incluyeron en la copia de seguridad. Los metadatos se actualizan para administrar este caso correctamente.
El cambio se realizó en el servidor, pero no se difundió a ningún cliente o servidor del mismo nivel.
Al igual que en otras situaciones en que un cambio no forma parte de una copia de seguridad, los cambios de este tipo no se pueden restaurar.
Actualizar los metadatos después de restaurar un servidor
En cada cambio que se produce en una tabla, Sync Framework actualiza los metadatos en el servidor para identificar el momento y origen del cambio. Estos metadatos son utilizados por otros nodos para determinar qué filas requiere cada nodo del servidor. Si los cambios se producen en el servidor después de realizarse una copia de seguridad y dichos cambios se difunden a otros nodos antes de una restauración, los demás nodos contendrán los metadatos para cada una de las filas cambiadas. Una vez restaurado el servidor a partir de la copia de seguridad, podrían asignarse a los cambios subsiguientes en el servidor identificadores que entrarían en colisión con los que los demás nodos ya contienen. Esto puede producir conflictos durante la sincronización y una posible no convergencia.
Para evitar estos problemas, Sync Framework asigna un nuevo identificador de réplica al servidor y actualiza varias columnas de metadatos para cada tabla sincronizada.
Para restaurar el servidor y actualizar los metadatos
Establezca acceso exclusivo a la base de datos servidor. Esto evita que cualquier operación local o de sincronización actualice los datos o los metadatos hasta que la base de datos esté lista.
Restaure la base de datos de SQL Server a partir de una copia de seguridad completa y las copias de seguridad diferenciales pertinentes.
Ejecute PerformPostRestoreFixup.
Habilite el acceso total a la base de datos. En este punto pueden producirse cambios locales en el servidor, y los clientes y demás servidores del mismo nivel pueden sincronizar con el servidor.
Ejemplos
En el ejemplo de código siguiente se realiza una copia de seguridad de la base de datos servidor y se realizan cambios en la base de datos llamando a métodos de la clase Utility
de ejemplo; a continuación, se sincronizan esos cambios.
// 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")
La aplicación llama a un procedimiento almacenado (usp_SampleDbBackupRestore
) que contiene el código Transact-SQL siguiente para realizar una copia de seguridad de la base de datos servidor.
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
El mismo procedimiento contiene también el código Transact-SQL requerido para restaurar la base de datos servidor.
-- 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
En el ejemplo del código siguiente se restaura la base de datos servidor y se llama a PerformPostRestoreFixup()
para actualizar los metadatos. A continuación, la aplicación vuelve a sincronizar todos los nodos.
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()
Ejemplo de código completo
El ejemplo de código completo siguiente incluye los ejemplos de código descritos anteriormente y código adicional. Para el ejemplo, se requiere que la clase Utility
esté disponible en Clase de utilidad para los temas de procedimientos del proveedor de bases de datos.
// 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