我需要以编程方式将SQL数据库(在Azure中,或在兼容的本地内部)备份/导出到Azure存储,并将其还原到另一个SQL数据库。我想只使用NuGet包来代码依赖,因为我不能保证构建服务器或生产服务器都安装了Azure SDK。我找不到任何代码示例,我认为这是一个常见的操作。我找到的最接近的是:
https://blog.hompus.nl/2013/03/13/backup-your-azure-sql-database-to-blob-storage-using-code/
但是,此代码导出到本地bacpac文件(需要RoleEnvironment,一个仅限SDK的对象)。我认为应该有一种方法可以直接导出到Blob存储,而不需要中间文件。一种想法是创建一个Stream,然后运行:
services.ExportBacpac(stream, "dbnameToBackup")
然后将流写入存储;但是内存流不起作用 - 这可能是一个庞大的数据库(100-200 GB)。
最好的办法是什么?
答案 0 :(得分:4)
根据我的测试,sql Microsoft Azure SQL Management Library 0.51.0-prerelease支持直接将sql数据库.bacpac文件导出到azure存储。
我们可以使用sqlManagementClient.ImportExport.Export(resourceGroup, azureSqlServer, azureSqlDatabase,exportRequestParameters)
将。 bacpac 文件导出 azure storage 。
但我们无法在最新版本的Microsoft Azure SQL Management Library SDK中找到ImportExport。所以我们只能使用sql Microsoft Azure SQL Management Library 0.51.0-prerelease SDK。
有关如何使用sql Microsoft Azure SQL Management Library将sql备份导出到azure blob存储的更多详细信息,您可以参考以下步骤和代码。
先决条件:
在Azure AD中注册应用程序并为其创建服务原则。有关如何注册应用和获取访问令牌的更多详细信息,请参阅document。
详细信息代码:
注意:将clientId,tenantId,secretKey,subscriptionId替换为您注册的azure AD信息。用您自己的sql数据库和存储替换azureSqlDatabase,resourceGroup,azureSqlServer,adminLogin,adminPassword,storageKey,storageAccount。
static void Main(string[] args)
{
var subscriptionId = "xxxxxxxx";
var clientId = "xxxxxxxxx";
var tenantId = "xxxxxxxx";
var secretKey = "xxxxx";
var azureSqlDatabase = "data base name";
var resourceGroup = "Resource Group name";
var azureSqlServer = "xxxxxxx"; //testsqlserver
var adminLogin = "user";
var adminPassword = "password";
var storageKey = "storage key";
var storageAccount = "storage account";
var baseStorageUri = $"https://{storageAccount}.blob.core.windows.net/brandotest/";//with container name endwith "/"
var backName = azureSqlDatabase + "-" + $"{DateTime.UtcNow:yyyyMMddHHmm}" + ".bacpac"; //back up sql file name
var backupUrl = baseStorageUri + backName;
ImportExportOperationStatusResponse exportStatus = new ImportExportOperationStatusResponse();
try
{
ExportRequestParameters exportRequestParameters = new ExportRequestParameters
{
AdministratorLogin = adminLogin,
AdministratorLoginPassword = adminPassword,
StorageKey = storageKey,
StorageKeyType = "StorageAccessKey",
StorageUri = new Uri(backupUrl)
};
SqlManagementClient sqlManagementClient = new SqlManagementClient(new Microsoft.Azure.TokenCloudCredentials(subscriptionId, GetAccessToken(tenantId, clientId, secretKey)));
var export = sqlManagementClient.ImportExport.Export(resourceGroup, azureSqlServer, azureSqlDatabase,
exportRequestParameters); //do export operation
while (exportStatus.Status != Microsoft.Azure.OperationStatus.Succeeded) // until operation successed
{
Thread.Sleep(1000 * 60);
exportStatus = sqlManagementClient.ImportExport.GetImportExportOperationStatus(export.OperationStatusLink);
}
Console.WriteLine($"Export DataBase {azureSqlDatabase} to Storage {storageAccount} Succesfully");
}
catch (Exception exception)
{
//todo
}
}
private static string GetAccessToken(string tenantId, string clientId, string secretKey)
{
var authenticationContext = new AuthenticationContext($"https://login.windows.net/{tenantId}");
var credential = new ClientCredential(clientId, secretKey);
var result = authenticationContext.AcquireTokenAsync("https://management.core.windows.net/",
credential);
if (result == null)
{
throw new InvalidOperationException("Failed to obtain the JWT token");
}
var token = result.Result.AccessToken;
return token;
}
结果如下:
1.发送请求告诉sql server开始导出到azure blob存储
2.继续发送请求以监控数据库导出的操作状态。
3.完成出口业务。
答案 1 :(得分:1)
这是一个想法:
将流传递给.ExportBacPac
方法但在另一个线程上保留对它的引用,在该线程中您经常清空并重置流,以便没有内存溢出。我在这里假设Dac
在填充流时无法访问流。
你必须要照顾好自己的事情是线程安全 - 默认情况下,MemoryStreams不是线程安全的。因此,您必须围绕.Position
和.CopyTo
编写自己的锁定机制。我没有对此进行测试,但如果您正确处理锁定,我会假设.ExportBacPac
方法在其他线程访问流时不会抛出任何错误。
这是一个非常简单的例子,伪代码概述了我的想法:
ThreadSafeStream stream = new ThreadSafeStream();
Task task = new Task(async (exitToken) => {
MemoryStream partialStream = new MemoryStream();
// Check if backup completed
if (...)
{
exitToken.Trigger();
}
stream.CopyToThreadSafe(partialStream);
stream.PositionThreadSafe = 0;
AzureService.UploadToStorage(partialStream);
await Task.Delay(500); // Play around with this - it shouldn't take too long to copy the stream
});
services.ExportBacpac(stream, "dbnameToBackup");
await TimerService.RunTaskPeriodicallyAsync(task, 500);
答案 2 :(得分:1)
这与Brando的答案类似,但这个使用了一个稳定的包装:
using Microsoft.WindowsAzure.Management.Sql;
在Brando的答案中使用相同的变量,代码将是这样的:
var azureSqlServer = "xxxxxxx"+".database.windows.net";
var azureSqlServerName = "xxxxxxx";
SqlManagementClient managementClient = new SqlManagementClient(new TokenCloudCredentials(subscriptionId, GetAccessToken(tenantId, clientId, secretKey)));
var exportParams = new DacExportParameters()
{
BlobCredentials = new DacExportParameters.BlobCredentialsParameter()
{
StorageAccessKey = storageKey,
Uri = new Uri(baseStorageUri)
},
ConnectionInfo = new DacExportParameters.ConnectionInfoParameter()
{
ServerName = azureSqlServer,
DatabaseName = azureSqlDatabase,
UserName = adminLogin,
Password = adminPassword
}
};
var exportResult = managementClient.Dac.Export(azureSqlServerName, exportParams);
答案 3 :(得分:0)
您可以使用Microsoft.Azure.Management.Fluent
将数据库导出到.bacpac
文件并将其存储在blob中。为此,您只需要做几件事。
Microsoft.Azure.Management.Fluent
NuGet程序包,并导入Microsoft.Azure.Management.Fluent
,Microsoft.Azure.Management.ResourceManager.Fluent
和Microsoft.Azure.Management.ResourceManager.Fluent.Authentication
名称空间。享受!
var principalClientID = "<Applicaiton (Client) ID>";
var principalClientSecret = "<ClientSecret>";
var principalTenantID = "<TenantID>";
var sqlServerName = "<SQL Server Name> (without '.database.windows.net'>";
var sqlServerResourceGroupName = "<SQL Server Resource Group>";
var databaseName = "<Database Name>";
var databaseLogin = "<Database Login>";
var databasePassword = "<Database Password>";
var storageResourceGroupName = "<Storage Resource Group>";
var storageName = "<Storage Account>";
var storageBlobName = "<Storage Blob Name>";
var bacpacFileName = "myBackup.bacpac";
var credentials = new AzureCredentialsFactory().FromServicePrincipal(principalClientID, principalClientSecret, principalTenantID, AzureEnvironment.AzureGlobalCloud);
var azure = await Azure.Authenticate(credentials).WithDefaultSubscriptionAsync();
var storageAccount = await azure.StorageAccounts.GetByResourceGroupAsync(storageResourceGroupName, storageName);
var sqlServer = await azure.SqlServers.GetByResourceGroupAsync(sqlServerResourceGroupName, sqlServerName);
var database = await sqlServer.Databases.GetAsync(databaseName);
await database.ExportTo(storageAccount, storageBlobName, bacpacFileName)
.WithSqlAdministratorLoginAndPassword(databaseLogin, databasePassword)
.ExecuteAsync();