如何以编程方式将SQL数据库直接导出到blob存储

时间:2017-08-03 22:32:15

标签: c# azure-sql-database azure-storage

我需要以编程方式将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)。

最好的办法是什么?

4 个答案:

答案 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存储

enter image description here

2.继续发送请求以监控数据库导出的操作状态。

enter image description here

3.完成出口业务。

enter image description here

答案 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;

Nuget

在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中。为此,您只需要做几件事。

  1. 创建可以访问资源的AZAD(Azure Active Directory)应用程序和服务主体。遵循this link以获得全面指南。
  2. 从第一步开始,您将需要“应用程序(客户端)ID”,“客户端机密”和“租户ID”。
  3. 安装Microsoft.Azure.Management.Fluent NuGet程序包,并导入Microsoft.Azure.Management.FluentMicrosoft.Azure.Management.ResourceManager.FluentMicrosoft.Azure.Management.ResourceManager.Fluent.Authentication名称空间。
  4. 用适合您的用例的值替换以下代码段中的占位符。
  5. 享受!

        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();