你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

使用脚本配置链接 - Azure SQL 托管实例

适用于:Azure SQL 托管实例

本文介绍如何通过 Transact-SQL (T-SQL) 和 PowerShell 或 Azure CLI 脚本配置 SQL Server 与 Azure SQL 托管实例之间的链接。 通过该链接,初始主要副本中的数据库将近乎实时地复制到次要副本。

创建链接后,可以故障转移到次要副本以进行迁移或灾难恢复。

注意

概述

使用链接功能将初始主要副本中的数据库复制到次要副本。 对于 SQL Server 2022,初始主要副本可以是 SQL Server 或 Azure SQL 托管实例。 对于 SQL Server 2019 及更早版本,初始主要副本必须是 SQL Server。 配置链接后,将初始主要副本中的数据库复制到次要副本。

可以选择在主要副本和次要副本之间的混合环境中保留该链接的位置以便进行连续数据复制,也可以将数据库故障转移到次要副本、迁移到 Azure 或进行灾难恢复。 对于 SQL Server 2019 及更早版本,将故障转移到 Azure SQL 托管实例即可断开链接,不支持故障回复功能。 使用 SQL Server 2022 时,可以选择保留该链接并在两个副本之间进行故障回复 - 目前可在预览版中使用此功能。

如果计划仅使用辅助托管实例进行灾难恢复,可以通过激活混合故障转移权益来节省许可成本。

使用本文中的说明手动设置 SQL Server 与 Azure SQL 托管实例之间的链接。 创建链接后,会在目标次要副本上创建源数据库的只读副本。

提示

  • 若要简化使用具有适用于环境的正确参数的 T-SQL 脚本,强烈建议使用 SQL Server Management Studio (SSMS) 中的托管实例链接向导来生成脚本以创建链接。 在“新建托管实例链接”窗口的“摘要”页上,选择“脚本”,而不是“完成”。

先决条件

注意

链接的某些功能已正式发布,还有一些当前为预览版。 查看版本可支持性,了解详细信息。

若要复制数据库,需要满足以下先决条件:

考虑以下情况:

  • 链接功能支持每个链接有一个数据库。 若要在一个实例上复制多个数据库,请为每个单独的数据库创建一个链接。 例如,若要将 10 个数据库复制到 SQL 托管实例,请创建 10 个单独链接。
  • SQL Server 和 SQL 托管实例之间的排序规则应相同。 排序规则不匹配可能会导致服务器名称大小写不匹配,并阻止从 SQL Server 到 SQL 托管实例的成功连接。
  • SQL Server 初始主要副本上的错误 1475 指示需要通过在不使用 COPY ONLY 选项的情况下创建完整备份来启动新的备份链。

权限

对于SQL Server,应具有 sysadmin 权限。

对于 Azure SQL 托管实例,你应是 SQL 托管实例参与者的一位成员,或具有自定义角色的以下权限:

Microsoft.Sql/ 资源 必要的权限
Microsoft.Sql/managedInstances /read、/write
Microsoft.Sql/managedInstances/hybridCertificate /action
Microsoft.Sql/managedInstances/databases /read、/delete、/write、/completeRestore/action、/readBackups/action、/restoreDetails/read
Microsoft.Sql/managedInstances/distributedAvailabilityGroups /read、/write、/delete、/setRole/action
Microsoft.Sql/managedInstances/endpointCertificates /read
Microsoft.Sql/managedInstances/hybridLink /read、/write、/delete
Microsoft.Sql/managedInstances/serverTrustCertificates /write、/delete、/read

术语和命名约定

在运行本用户指南中的脚本时,切勿将 SQL Server 或 SQL 托管实例名称与它们的完全限定域名 (FQDN) 混淆,这一点很重要。 下表说明了各种名称确切表示的内容,以及如何获取其值:

术语 说明 如何找出
初始主 1 最初创建链接的 SQL Server 或 SQL 托管实例,用于将数据库复制到次要副本。
主要副本 当前托管主数据库的 SQL Server 或 SQL 托管实例。
辅助副本 从当前主要副本接收近实时复制数据的 SQL Server 或 SQL 托管实例。
SQL Server 名称 简短的单字 SQL Server 名称。 例如:“sqlserver1”。 通过 T-SQL 运行 SELECT @@SERVERNAME
SQL Server FQDN SQL Server 的完全限定域名 (FQDN)。 例如:“sqlserver1.domain.com”。 查看你的本地网络 (DNS) 配置,或者服务器名称(如果使用 Azure 虚拟机 (VM))。
SQL 托管实例名称 简短的单字 SQL 托管实例名称。 例如:“managedinstance1”。 请在 Azure 门户中查看你的托管实例的名称。
SQL 托管实例 FQDN SQL 托管实例的完全限定域名 (FQDN)。 例如:“managedinstance1.6d710bcf372b.database.windows.net”。 请在 Azure 门户中的 SQL 托管实例概述页上查看该主机名。
可解析域名 可解析为 IP 地址的 DNS 名称。 例如,运行 nslookup sqlserver1.domain.com 应返回一个 IP 地址,例如 10.0.0.1。 通过命令提示符运行 nslookup 命令。
SQL Server IP SQL Server 的 IP 地址。 如果 SQL Server 有多个 IP,请选择可从 Azure 访问的 IP 地址。 通过运行 SQL Server 的主机操作系统的命令提示符运行 ipconfig 命令。

1 目前只能在预览版中将 Azure SQL 托管实例配置为初始主要副本,并且仅从 SQL Server 2022 CU10 开始支持此功能。

设置数据库恢复和备份

如果 SQL Server 是初始主要副本,要通过链接复制的所有数据库必须处于完整恢复模式,并且至少有一个备份。 由于 Azure SQL 托管实例会自动执行备份,因此如果 SQL 托管实例是初始主要副本,请跳过此步骤。 primary

针对要复制的所有数据库,在 SQL Server 上运行以下代码。 将 <DatabaseName> 替换为数据库的实际名称。

-- Run on SQL Server
-- Set full recovery model for all databases you want to replicate.
ALTER DATABASE [<DatabaseName>] SET RECOVERY FULL
GO

-- Execute backup for all databases you want to replicate.
BACKUP DATABASE [<DatabaseName>] TO DISK = N'<DiskPath>'
GO

有关详细信息,请参阅创建完整数据库备份

注意

该链接仅支持复制用户数据库。 不支持复制系统数据库。 要复制实例级对象(存储在 mastermsdb 数据库中),我们建议编写 T-SQL 脚本,并在目标实例上运行这些脚本。

在实例之间建立信任

首先,必须在两个实例之间建立信任,并保护用于在网络上进行数据通信和加密的终结点。 分布式可用性组使用现有的可用性组数据库镜像终结点,而不是使用自己的专用终结点。 因此需要通过可用性组数据库镜像终结点在两个实例之间配置安全性和信任。

注意

此链接基于 Always On 可用性组技术。 数据库镜像终结点是用途特殊的终结点,专门由可用性组接收来自其他实例的连接。 请不要误解数据库镜像终结点一词,它与旧版 SQL Server 数据库镜像功能不同。

基于证书的信任是用于保护 SQL Server 和 SQL 托管实例的数据库镜像终结点的唯一受支持方法。 如果现有的可用性组使用 Windows 身份验证,则需要将基于证书的信任作为辅助身份验证选项添加到现有镜像终结点。 可以使用 ALTER ENDPOINT 语句来执行此操作,如本文后面部分所示。

重要

生成的证书有过期日期和时间。 证书需要在过期之前进行续订和轮换。

以下列出了用于保护 SQL Server 和 SQL 托管实例的数据库镜像端点的过程概述:

  1. 在 SQL Server 上生成证书并获取其公钥。
  2. 获取 SQL 托管实例证书的公钥。
  3. 在 SQL Server 和 SQL 托管实例之间交换公钥。
  4. 将 Azure 受信任的根证书颁发机构密钥导入到 SQL Server

以下各部分详细介绍了这些步骤。

在 SQL Server 上创建证书并将其公钥导入 SQL 托管实例

首先,在 master 数据库中创建数据库主密钥(如果尚不存在)。 在下面的脚本中插入密码替代 <strong_password>,并将其保存在机密且安全的地方。 在 SQL Server 上运行此 T-SQL 脚本:

-- Run on SQL Server
-- Create a master key encryption password
-- Keep the password confidential and in a secure place
USE MASTER
IF NOT EXISTS (SELECT * FROM sys.symmetric_keys WHERE symmetric_key_id = 101)
BEGIN
    PRINT 'Creating master key.' + CHAR(13) + 'Keep the password confidential and in a secure place.'
    CREATE MASTER KEY ENCRYPTION BY PASSWORD = '<strong_password>'
END
ELSE
    PRINT 'Master key already exists.'
GO

然后,在 SQL Server 生成身份验证证书。 在以下代码中,进行以下替换:

  • @cert_expiry_date 替换为所需证书到期日期(未来日期)。

记录此日期并设置提醒,以便在 SQL 服务器证书到期前进行轮换(更新),从而确保链接持续运行。

重要

强烈建议使用此脚本中自动生成的证书名称。 尽管允许在 SQL Server 上自定义自己的证书名称,但名称不应包含任何 \ 字符。

-- Create the SQL Server certificate for the instance link
USE MASTER

-- Customize SQL Server certificate expiration date by adjusting the date below
DECLARE @cert_expiry_date AS varchar(max)='03/30/2025'

-- Build the query to generate the certificate
DECLARE @sqlserver_certificate_name NVARCHAR(MAX) = N'Cert_' + @@servername  + N'_endpoint'
DECLARE @sqlserver_certificate_subject NVARCHAR(MAX) = N'Certificate for ' + @sqlserver_certificate_name
DECLARE @create_sqlserver_certificate_command NVARCHAR(MAX) = N'CREATE CERTIFICATE [' + @sqlserver_certificate_name + '] ' + char (13) +
'    WITH SUBJECT = ''' + @sqlserver_certificate_subject + ''',' + char (13) +
'    EXPIRY_DATE = '''+ @cert_expiry_date + ''''+ char (13)
IF NOT EXISTS (SELECT name from sys.certificates WHERE name = @sqlserver_certificate_name)
BEGIN
    PRINT (@create_sqlserver_certificate_command)
    -- Execute the query to create SQL Server certificate for the instance link
    EXEC sp_executesql @stmt = @create_sqlserver_certificate_command
END
ELSE
    PRINT 'Certificate ' + @sqlserver_certificate_name + ' already exists.'
GO

然后,在 SQL Server 上使用以下 T-SQL 查询来验证是否已创建该证书:

-- Run on SQL Server
USE MASTER
GO
SELECT * FROM sys.certificates WHERE pvt_key_encryption_type = 'MK'

在查询结果中,你将看到该证书已用主密钥进行了加密。

现在,可以在 SQL Server 上获取生成的证书的公钥:

-- Run on SQL Server
-- Show the name and the public key of generated SQL Server certificate
USE MASTER
GO
DECLARE @sqlserver_certificate_name NVARCHAR(MAX) = N'Cert_' + @@servername  + N'_endpoint'
DECLARE @PUBLICKEYENC VARBINARY(MAX) = CERTENCODED(CERT_ID(@sqlserver_certificate_name));
SELECT @sqlserver_certificate_name as 'SQLServerCertName'
SELECT @PUBLICKEYENC AS SQLServerPublicKey;

保存来自输出的 SQLServerCertNameSQLServerPublicKey 的值,因为导入证书的下一步需要用到。

首先,请确保已登录到 Azure,并且已选择托管实例所在的订阅。 如果帐户上有多个 Azure 订阅,则务必选择适当的订阅。

<SubscriptionID> 替换为你的 Azure 订阅 ID。

# Run in Azure Cloud Shell (select PowerShell console)

# Enter your Azure subscription ID
$SubscriptionID = "<SubscriptionID>"

# Login to Azure and select subscription ID
if ((Get-AzContext ) -eq $null)
{
    echo "Logging to Azure subscription"
    Login-AzAccount
}
Select-AzSubscription -SubscriptionName $SubscriptionID

然后使用 New-AzSqlInstanceServerTrustCertificate PowerShell 或 az sql mi partner-cert create Azure CLI 命令将身份验证证书的公钥从 SQL Server 上传到 Azure,例如以下 PowerShell 示例。

填写必要的用户信息,复制、粘贴信息,然后运行脚本。 将:

  • <SQLServerPublicKey> 替换为在上一步骤中记录的二进制格式的 SQL Server 证书的公共部分。 它是以 0x 开头的长字符串值。
  • <SQLServerCertName> 替换为在上一步中记录的 SQL Server 证书名称。
  • <ManagedInstanceName> 替换为你的托管实例的短名称。
# Run in Azure Cloud Shell (select PowerShell console)
# ===============================================================================
# POWERSHELL SCRIPT TO IMPORT SQL SERVER PUBLIC CERTIFICATE TO SQL MANAGED INSTANCE
# ===== Enter user variables here ====

# Enter the name for the server SQLServerCertName certificate – for example, "Cert_sqlserver1_endpoint"
$CertificateName = "<SQLServerCertName>"

# Insert the certificate public key blob that you got from SQL Server – for example, "0x1234567..."
$PublicKeyEncoded = "<SQLServerPublicKey>"

# Enter your managed instance short name – for example, "sqlmi"
$ManagedInstanceName = "<ManagedInstanceName>"

# ==== Do not customize the below cmdlets====

# Find out the resource group name
$ResourceGroup = (Get-AzSqlInstance -InstanceName $ManagedInstanceName).ResourceGroupName

# Upload the public key of the authentication certificate from SQL Server to Azure.
New-AzSqlInstanceServerTrustCertificate -ResourceGroupName $ResourceGroup -InstanceName $ManagedInstanceName -Name $CertificateName -PublicKey $PublicKeyEncoded 

此操作会生成上传到 Azure 的 SQL Server 证书的摘要。

如果需要查看上传到托管实例的所有 SQL Server 证书,请在 Azure Cloud Shell 中使用 Get-AzSqlInstanceServerTrustCertificate PowerShell 或 az sql mi partner-cert list Azure CLI 命令。 若要移除上传到 SQL 托管实例的 SQL Server 证书,请在 Azure Cloud Shell 中使用 Remove-AzSqlInstanceServerTrustCertificate PowerShell 或 az sql mi partner-cert delete Azure CLI 命令。

获取 SQL 托管实例的证书公钥并将其导入到 SQL Server

用于保护链接终结点的证书将在 Azure SQL 托管实例上自动生成。 从 SQL 托管实例获取证书公钥,并使用 Get-AzSqlInstanceEndpointCertificate PowerShell 或 az sql mi endpoint-cert show Azure CLI 命令将其导入到 SQL Server,例如以下 PowerShell 示例。

注意

使用 Azure CLI 时,若要在后续步骤中使用 PublicKey 输出,则需要将 0x 手动添加到 PublicKey 输出的前面。 例如,PublicKey 将类似于“0x3082033E30...”。

运行以下脚本。 将:

  • <SubscriptionID> 替换为 Azure 订阅 ID。
  • <ManagedInstanceName> 替换为你的托管实例的短名称。
# Run in Azure Cloud Shell (select PowerShell console)
# ===============================================================================
# POWERSHELL SCRIPT TO EXPORT MANAGED INSTANCE PUBLIC CERTIFICATE
# ===== Enter user variables here ====

# Enter your managed instance short name – for example, "sqlmi"
$ManagedInstanceName = "<ManagedInstanceName>"

# ==== Do not customize the following cmdlet ====

# Find out the resource group name
$ResourceGroup = (Get-AzSqlInstance -InstanceName $ManagedInstanceName).ResourceGroupName

# Fetch the public key of the authentication certificate from Managed Instance. Outputs a binary key in the property PublicKey.
Get-AzSqlInstanceEndpointCertificate -ResourceGroupName $ResourceGroup -InstanceName $ManagedInstanceName -EndpointType "DATABASE_MIRRORING" | out-string   

复制整个 PublicKey 输出(以 0x 开头),下一步将需要它。

或者,如果在复制粘贴 PublicKey 时遇到问题,还可以在托管实例上运行 T-SQL 命令 EXEC sp_get_endpoint_certificate 4 以获取其用于链接终结点的公钥。

接下来,将已获取的托管实例安全证书公钥导入 SQL Server。 在 SQL Server 上运行以下查询。 将:

  • <ManagedInstanceFQDN> 替换为托管实例的完全限定的域名。
  • <PublicKey> 替换为上一步中获取的 PublicKey 值(在 Azure Cloud Shell 中,从 0x 开始)。 无需使用引号。

重要

证书名称必须是 SQL 托管实例 FQDN,并且不应修改。 如果使用自定义名称,则链接将不起作用。

-- Run on SQL Server
USE MASTER
CREATE CERTIFICATE [<ManagedInstanceFQDN>]
FROM BINARY = <PublicKey> 

将 Azure 受信任的根证书颁发机构密钥导入到 SQL Server

需要将 Microsoft 和 DigiCert 证书颁发机构 (CA) 的公共根证书密钥导入 SQL Server,SQL Server 才能信任 Azure 为 database.windows.net 域颁发的证书。

注意

确保 PublicKey 以 0x 开头。 如果 PublicKey 尚不存在,则可能需要将其手动添加到 PublicKey 的开头。

首先,在 SQL Server 上导入 Microsoft PKI 根颁发机构证书:

-- Run on SQL Server
-- Import Microsoft PKI root-authority certificate (trusted by Azure), if not already present
IF NOT EXISTS (SELECT name FROM sys.certificates WHERE name = N'MicrosoftPKI')
BEGIN
    PRINT 'Creating MicrosoftPKI certificate.'
    CREATE CERTIFICATE [MicrosoftPKI] FROM BINARY = 0x308205A830820390A00302010202101ED397095FD8B4B347701EAABE7F45B3

    --Trust certificates issued by Microsoft PKI root authority for Azure database.windows.net domains
    DECLARE @CERTID int
    SELECT @CERTID = CERT_ID('MicrosoftPKI')
    EXEC sp_certificate_add_issuer @CERTID, N'*.database.windows.net'
END
ELSE
    PRINT 'Certificate MicrosoftPKI already exsits.'
GO

然后,在 SQL Server 上导入 DigiCert PKI 根颁发机构证书:

-- Run on SQL Server
-- Import DigiCert PKI root-authority certificate trusted by Azure to SQL Server, if not already present
IF NOT EXISTS (SELECT name FROM sys.certificates WHERE name = N'DigiCertPKI')
BEGIN
    PRINT 'Creating DigiCertPKI certificate.'
    CREATE CERTIFICATE [DigiCertPKI] FROM BINARY = 0x3082038E30820276A0030201020210033AF1E6A711A9A0BB2864B11D0

    --Trust certificates issued by DigiCert PKI root authority for Azure database.windows.net domains
    DECLARE @CERTID int
    SELECT @CERTID = CERT_ID('DigiCertPKI')
    EXEC sp_certificate_add_issuer @CERTID, N'*.database.windows.net'
END
ELSE
    PRINT 'Certificate DigiCertPKI already exsits.'
GO

最后,通过使用以下动态管理视图 (DMV) 验证所有已创建的证书:

-- Run on SQL Server
SELECT * FROM sys.certificates

保护数据库镜像终结点

如果 SQL Server 上既没有现有的可用性组,也没有数据库镜像终结点,则下一步需要在 SQL Server 上创建数据库镜像终结点,并使用之前生成的 SQL Server 证书对其进行保护。 如果确实有现有的可用性组或镜像终结点,请跳转到更改现有终结点部分。

在 SQL Server 上创建和保护数据库镜像终结点

若要验证现有数据库镜像终结点是否未创建,请使用以下脚本:

-- Run on SQL Server
-- View database mirroring endpoints on SQL Server
SELECT * FROM sys.database_mirroring_endpoints WHERE type_desc = 'DATABASE_MIRRORING'

如果上述查询未显示现有数据库镜像终结点,请在 SQL Server 上运行以下脚本以获取之前生成的 SQL Server 证书的名称。

-- Run on SQL Server
-- Show the name and the public key of generated SQL Server certificate
USE MASTER
GO
DECLARE @sqlserver_certificate_name NVARCHAR(MAX) = N'Cert_' + @@servername  + N'_endpoint'
SELECT @sqlserver_certificate_name as 'SQLServerCertName'

从输出中保存 SQLServerCertName,下一步中将需要它。

使用以下脚本在端口 5022 上创建新的数据库镜像终结点,并使用 SQL Server 证书保护该终结点。 将:

  • <SQL_SERVER_CERTIFICATE> 替换为上一步中获取的 SQLServerCertName 的名称。
-- Run on SQL Server
-- Create a connection endpoint listener on SQL Server
USE MASTER
CREATE ENDPOINT database_mirroring_endpoint
    STATE=STARTED   
    AS TCP (LISTENER_PORT=5022, LISTENER_IP = ALL)
    FOR DATABASE_MIRRORING (
        ROLE=ALL,
        AUTHENTICATION = CERTIFICATE [<SQL_SERVER_CERTIFICATE>],
        ENCRYPTION = REQUIRED ALGORITHM AES
    )  
GO

通过在 SQL Server 上运行以下脚本来验证镜像终结点是否已创建:

-- Run on SQL Server
-- View database mirroring endpoints on SQL Server
SELECT
    name, type_desc, state_desc, role_desc,
    connection_auth_desc, is_encryption_enabled, encryption_algorithm_desc
FROM 
    sys.database_mirroring_endpoints

成功创建的终结点 state_desc 列应显示为 STARTED

新的镜像终结点是通过 CERTIFICATE 身份验证创建的,并且已启用 AES 加密。

更改现有终结点

注意

如果刚创建了一个新的镜像终结点,请跳过此步骤。 仅当使用现有的可用性组和现有的数据库镜像终结点时,才使用此步骤。

如果将现有可用性组用于链接,或者存在现有的数据库镜像终结点,请先验证它是否满足链接的以下必要条件:

  • 类型必须为 DATABASE_MIRRORING
  • 连接身份验证必须为 CERTIFICATE
  • 必须启用加密。
  • 加密算法必须是 AES

在 SQL Server 上运行以下查询,以查看现有数据库镜像终结点的详细信息:

-- Run on SQL Server
-- View database mirroring endpoints on SQL Server
SELECT
    name, type_desc, state_desc, role_desc, connection_auth_desc,
    is_encryption_enabled, encryption_algorithm_desc
FROM
    sys.database_mirroring_endpoints

如果输出显示现有 DATABASE_MIRRORING 终结点的 connection_auth_desc 不是 CERTIFICATE,或者 encryption_algorthm_desc 不是 AES,则需要更改终结点以满足要求。

在 SQL Server 上,同一个数据库镜像终结点既用于可用性组,又用于分布式可用性组。 如果 connection_auth_desc 终结点是 NTLM(Windows 身份验证)或 KERBEROS,并且需要对现有可用性组进行 Windows 身份验证,则有可能可以通过将身份验证选项切换为 NEGOTIATE CERTIFICATE,将终结点更改为使用多种身份验证方法。 此更改将在对 SQL 托管实例使用证书身份验证的同时允许现有可用性组使用 Windows 身份验证。

同样,如果加密不包含 AES 并且你需要 RC4 加密,也有可能可以将终结点更改为使用这两种算法。 有关更改终结点的可能选项的详细信息,请参阅 sys.database_mirroring_endpoints

以下脚本是如何在 SQL Server 上更改现有数据库镜像终结点的示例。 将:

  • <YourExistingEndpointName> 替换为现有的终结点名称。
  • <SQLServerCertName> 替换为生成的 SQL Server 证书的名称(在上述的先前某一步骤中获得)。

根据你的特定配置,可能需要进一步自定义脚本。 还可以使用 SELECT * FROM sys.certificates 来获取 SQL Server 上创建的证书的名称。

-- Run on SQL Server
-- Alter the existing database mirroring endpoint to use CERTIFICATE for authentication and AES for encryption
USE MASTER
ALTER ENDPOINT [<YourExistingEndpointName>]   
    STATE=STARTED   
    AS TCP (LISTENER_PORT=5022, LISTENER_IP = ALL)
    FOR DATABASE_MIRRORING (
        ROLE=ALL,
        AUTHENTICATION = WINDOWS NEGOTIATE CERTIFICATE [<SQLServerCertName>],
        ENCRYPTION = REQUIRED ALGORITHM AES
    )
GO

运行 ALTER 终结点查询并将双重身份验证模式设置为 Windows 和 Certificate 后,再次在 SQL Server 上使用此查询来显示数据库镜像终结点的详细信息:

-- Run on SQL Server
-- View database mirroring endpoints on SQL Server
SELECT
    name, type_desc, state_desc, role_desc, connection_auth_desc,
    is_encryption_enabled, encryption_algorithm_desc
FROM
    sys.database_mirroring_endpoints

你已成功修改了 SQL 托管实例链接的数据库镜像终结点。

在 SQL Server 上创建可用性组

如果没有现有的可用性组,下一步就是在 SQL Server 上创建一个,无论哪个可用性组将是初始主要副本。 如果 SQL 托管实例为初始主要副本,则用于创建可用性组的命令有所不同,仅从 SQL Server 2022 CU10 开始才支持该功能。

虽然可以为同一数据库建立多个链接,但该链接仅支持每个链接复制一个数据库。 如果要为同一数据库创建多个链接,请对所有链接使用相同的可用性组,然后为 SQL Server 和 SQL 托管实例之间的每个数据库链接创建一个新的分布式可用性组。

如果 SQL Server 是初始主要副本,请使用链接的以下参数创建可用性组:

  • 初始主服务器名称
  • 数据库名称
  • 故障转移模式 MANUAL
  • 种子设定模式 AUTOMATIC

首先,通过运行以下 T-SQL 语句找出 SQL Server 名称:

-- Run on the initial primary
SELECT @@SERVERNAME AS SQLServerName 

使用以下脚本在 SQL Server 上创建可用性组。 将:

  • <AGName> 替换为可用性组的名称。 托管实例链接需要每个可用性组有一个数据库。 对于多个数据库,需要创建多个可用性组。 请考虑命名每个可用性组,使其名称反映相应的数据库 - 例如 AG_<db_name>
  • <DatabaseName> 替换为要复制的数据库的名称。
  • <SQLServerName> 替换为在上一步中获取的 SQL Server 实例的名称。
  • <SQLServerIP> 替换为 SQL Server IP 地址。 或者,可以使用可解析的 SQL Server 主机名,但需确保名称可从 SQL 托管实例虚拟网络进行解析。
-- Run on SQL Server
-- Create the primary availability group on SQL Server
USE MASTER
CREATE AVAILABILITY GROUP [<AGName>]
WITH (CLUSTER_TYPE = NONE) -- <- Delete this line for SQL Server 2016 only. Leave as-is for all higher versions.
    FOR database [<DatabaseName>]  
    REPLICA ON   
        N'<SQLServerName>' WITH   
            (  
            ENDPOINT_URL = 'TCP://<SQLServerIP>:5022',
            AVAILABILITY_MODE = SYNCHRONOUS_COMMIT,
            FAILOVER_MODE = MANUAL,
            SEEDING_MODE = AUTOMATIC
            );
GO

重要

对于 SQL Server 2016,请从上述 T-SQL 语句中删除 WITH (CLUSTER_TYPE = NONE)。 对于所有更高版本的 SQL Server,请保留原样。

接下来,在 SQL Server 上创建分布式可用性组。 如果计划创建多个链接,则需要为每个链接创建分布式可用性组,即使要为同一数据库建立多个链接也是如此。

替换以下值,然后运行 T-SQL 脚本来创建分布式可用性组。

  • <DAGName> 替换为分布式可用性组的名称。 由于可以通过为每个链接创建分布式可用性组来为同一数据库配置多个链接,因此请考虑相应地命名每个分布式可用性组,例如 DAG1_<db_name>DAG2_<db_name>
  • <AGName> 替换为上一步中创建的可用性组的名称。
  • <SQLServerIP> 替换为来自上一步骤的 SQL Server IP 地址。 或者,可以使用可解析的 SQL Server 主机名,但请确保名称可通过 SQL 托管实例虚拟网络进行解析(这需要为托管实例的子网配置自定义 Azure DNS)。
  • <ManagedInstanceName> 替换为你的托管实例的短名称。
  • <ManagedInstanceFQDN> 替换为托管实例的完全限定域名。
-- Run on SQL Server
-- Create a distributed availability group for the availability group and database
-- ManagedInstanceName example: 'sqlmi1'
-- ManagedInstanceFQDN example: 'sqlmi1.73d19f36a420a.database.windows.net'
USE MASTER
CREATE AVAILABILITY GROUP [<DAGName>]
WITH (DISTRIBUTED) 
    AVAILABILITY GROUP ON  
    N'<AGName>' WITH 
    (
      LISTENER_URL = 'TCP://<SQLServerIP>:5022',
      AVAILABILITY_MODE = ASYNCHRONOUS_COMMIT,
      FAILOVER_MODE = MANUAL,
      SEEDING_MODE = AUTOMATIC,
      SESSION_TIMEOUT = 20
    ),
    N'<ManagedInstanceName>' WITH
    (
      LISTENER_URL = 'tcp://<ManagedInstanceFQDN>:5022;Server=[<ManagedInstanceName>]',
      AVAILABILITY_MODE = ASYNCHRONOUS_COMMIT,
      FAILOVER_MODE = MANUAL,
      SEEDING_MODE = AUTOMATIC
    );
GO

验证可用性组

使用以下脚本列出 SQL Server 实例上所有可用的可用性组和分布式可用性组。 此时,可用性组状态需要是 connected,并且分布式可用性组状态需要是 disconnected。 分布式可用性组的状态只有在它与 SQL 托管实例联接后才会转换为 connected

-- Run on SQL Server
-- This will show that the availability group and distributed availability group have been created on SQL Server.
SELECT * FROM sys.availability_groups

或者,可以使用 SSMS 对象资源管理器来查找可用性组和分布式可用性组。 依次展开“Always On 高可用性”文件夹和“可用性组”文件夹。

最后,可以创建链接。 根据哪个实例为初始主要副本,这些命令会有所不同。 使用 New-AzSqlInstanceLink PowerShell 或 az sql mi link create Azure CLI 命令创建链接,例如本部分中的 PowerShell 示例。 Azure CLI 当前不支持从 SQL 托管实例主要副本创建链接。

如果需要查看托管实例上的所有链接,请在 Azure Cloud Shell 中使用 Get-AzSqlInstanceLink PowerShell 或 az sql mi link show Azure CLI 命令。

若要简化此过程,请登录到 Azure 门户并在 Azure Cloud Shell 中运行以下脚本。 将:

  • <ManagedInstanceName> 替换为你的托管实例的短名称。
  • <AGName> 替换为 SQL Server 上创建的可用性组的名称。
  • <DAGName> 替换为 SQL Server 上创建的分布式可用性组的名称。
  • <DatabaseName> 替换为 SQL Server 上可用性组中复制的数据库。
  • <SQLServerIP> 替换为 SQL Server 的 IP 地址。 托管实例必须可以访问提供的 IP 地址。
#  Run in Azure Cloud Shell (select PowerShell console)
# =============================================================================
# POWERSHELL SCRIPT TO CREATE MANAGED INSTANCE LINK
# Instructs Managed Instance to join distributed availability group on SQL Server
# ===== Enter user variables here ====

# Enter your managed instance name – for example, "sqlmi1"
$ManagedInstanceName = "<ManagedInstanceName>"

# Enter the availability group name that was created on SQL Server
$AGName = "<AGName>"

# Enter the distributed availability group name that was created on SQL Server
$DAGName = "<DAGName>"

# Enter the database name that was placed in the availability group for replication
$DatabaseName = "<DatabaseName>"

# Enter the SQL Server IP
$SQLServerIP = "<SQLServerIP>"

# ==== Do not customize the following cmdlet ====

# Find out the resource group name
$ResourceGroup = (Get-AzSqlInstance -InstanceName $ManagedInstanceName).ResourceGroupName

# Build properly formatted connection endpoint
$SourceIP = "TCP://" + $SQLServerIP + ":5022"

# Create link on managed instance. Join distributed availability group on SQL Server.
New-AzSqlInstanceLink -ResourceGroupName $ResourceGroup -InstanceName $ManagedInstanceName -Name $DAGName |
-PrimaryAvailabilityGroupName $AGName -SecondaryAvailabilityGroupName $ManagedInstanceName |
-TargetDatabase $DatabaseName -SourceEndpoint $SourceIP

此操作的结果会是成功执行创建链接请求时的时间戳。

若要验证是否已在 SQL 托管实例和 SQL Server 之间建立连接,请在 SQL Server 上运行以下查询。 连接为非即时连接。 最多可能需要一分钟的时间,DMV 才会开始显示成功的连接。 请持续刷新 DMV,直到 SQL 托管实例副本的连接显示为 CONNECTED。

-- Run on SQL Server
SELECT
    r.replica_server_name AS [Replica],
    r.endpoint_url AS [Endpoint],
    rs.connected_state_desc AS [Connected state],
    rs.last_connect_error_description AS [Last connection error],
    rs.last_connect_error_number AS [Last connection error No],
    rs.last_connect_error_timestamp AS [Last error timestamp]
FROM
    sys.dm_hadr_availability_replica_states rs
    JOIN sys.availability_replicas r
    ON rs.replica_id = r.replica_id

建立连接后,SSMS 中的对象资源管理器最初会将在次要副本上复制的数据库显示为正在还原状态,因为初始种子设定阶段移动并还原数据库的完整备份。 数据库还原后,复制必须跟上,使两个数据库保持同步状态。 初始种子设定完成后,该数据库将不再处于“正在还原”。 种子设定小型数据库可能会很快,以至于在 SSMS 中看不到初始的“正在还原”状态。

重要

  • 除非 SQL Server 和 SQL 托管实例之间存在网络连接,否则此链接将不起作用。 若要解决网络连接问题,请按照测试网络连接中的步骤操作。
  • 定期备份 SQL Server 上的日志文件。 如果使用的日志空间达到 100%,则复制到 SQL 托管实例将会停止,直到空间使用率降低。 强烈建议通过设置每日作业来自动执行日志备份。 有关详细信息,请参阅在 SQL Server 上备份日志文件

停止工作负载

若要将数据库故障转移到次要副本,请先在维护时段停止主要副本上的任何应用程序工作负载。 这使数据库复制能够赶上次要副本,让你能在不丢失数据的情况下迁移或故障转移到 Azure。 尽管主数据库是 Always On 可用性组的一部分,但无法将其设置为只读模式。 需要确保在故障转移前,应用程序不会向主要副本提交事务。

切换复制模式

默认情况下,SQL Server 和 SQL 托管实例之间的复制是异步的。 将数据库故障转移到次要副本之前,请将链接切换到同步模式。 大范围网络距离的同步复制可能会减慢主要副本上的事务处理速度。

从异步模式切换为同步模式需要同时在 SQL 托管实例和 SQL Server 上更改复制模式。

切换复制模式(SQL 托管实例)

使用 Azure PowerShell 或 Azure CLI 在 SQL 托管实例上切换复制模式。

首先,请确保已登录到 Azure,并且已使用 Select-AzSubscription PowerShell 或 az account set Azure CLI 命令选择托管实例所在的订阅。 如果帐户上有多个 Azure 订阅,则务必选择适当的订阅。

在以下 PowerShell 示例中,将 <SubscriptionID> 替换为 Azure 订阅 ID。

# Run in Azure Cloud Shell (select PowerShell console)

# Enter your Azure subscription ID
$SubscriptionID = "<SubscriptionID>"

# Login to Azure and select subscription ID
if ((Get-AzContext ) -eq $null)
{
    echo "Logging to Azure subscription"
    Login-AzAccount
}
Select-AzSubscription -SubscriptionName $SubscriptionID

确保知道要进行故障转移的链接的名称。 可以使用 Get-AzSqlInstanceLink PowerShell 或 az sql mi link list Azure CLI 命令。

使用以下 PowerShell 脚本列出 SQL 托管实例上的所有活动链接。 将 <ManagedInstanceName> 替换为你的托管实例的短名称。

# Run in Azure Cloud Shell (select PowerShell console)
# =============================================================================
# POWERSHELL SCRIPT TO LIST ALL LINKS ON MANAGED INSTANCE
# ===== Enter user variables here ====

# Enter your managed instance name – for example, "sqlmi1"
$ManagedInstanceName = "<ManagedInstanceName>"

# ==== Do not customize the following cmdlet ====

# Find out the resource group name
$ResourceGroup = (Get-AzSqlInstance -InstanceName $ManagedInstanceName).ResourceGroupName

# List all links on the specified managed instance
Get-AzSqlInstanceLink -ResourceGroupName $ResourceGroup -InstanceName $ManagedInstanceName 

在上述脚本的输出中,记录要故障转移的链接的 Name 属性。

然后,使用 Update-AzSqlInstanceLink PowerShell 或 az sql mi link update Azure CLI 命令,,在已标识链接的 SQL 托管实例上将复制模式从异步切换到同步。

在以下 PowerShell 示例中,替换:

  • <ManagedInstanceName> 替换为你的托管实例的短名称。
  • <DAGName> 替换为在上一步中找到的链接的名称(上一步中的 Name 属性)。
# Run in Azure Cloud Shell (select PowerShell console)
# =============================================================================
# POWERSHELL SCRIPT TO SWITCH LINK REPLICATION MODE (ASYNC\SYNC)
# ===== Enter user variables here ====

# Enter the link name 
$LinkName = "<DAGName>"  

# Enter your managed instance name – for example, "sqlmi1" 
$ManagedInstanceName = "<ManagedInstanceName>" 

# ==== Do not customize the following cmdlet ====

# Find out the resource group name
$ResourceGroup = (Get-AzSqlInstance -InstanceName $ManagedInstanceName).ResourceGroupName

# Update replication mode of the specified link
Update-AzSqlInstanceLink -ResourceGroupName $ResourceGroup -InstanceName $ManagedInstanceName |
-Name $LinkName -ReplicationMode "Sync"

上述命令通过显示操作摘要来指示成功,属性 ReplicationMode 显示为 Sync

如果需要还原此操作,请执行前面的脚本,切换复制模式,但将 -ReplicationMode 中的 Sync 字符串替换为 Async

切换复制模式 (SQL Server)

使用 SQL Server 上的以下 T-SQL 脚本,将 SQL Server 上的分布式可用性组的复制模式从异步更改为同步。进行以下替换:

  • <DAGName> 替换为分布式可用性组的名称(用于创建链接)。
  • <AGName> 替换为在 SQL Server 上创建的可用性组的名称(用于创建链接)。
  • <ManagedInstanceName> 替换为托管实例的名称。
-- Run on SQL Server
-- Sets the distributed availability group to a synchronous commit.
-- ManagedInstanceName example: 'sqlmi1'
USE master
GO
ALTER AVAILABILITY GROUP [<DAGName>] 
MODIFY 
AVAILABILITY GROUP ON
    '<AGName>' WITH
    (AVAILABILITY_MODE = SYNCHRONOUS_COMMIT),
    '<ManagedInstanceName>' WITH
    (AVAILABILITY_MODE = SYNCHRONOUS_COMMIT);

若要确认是否已成功更改链接的复制模式,请使用以下动态管理视图。 结果指示 SYNCHRONOUS_COMIT 状态。

-- Run on SQL Server
-- Verifies the state of the distributed availability group
SELECT
    ag.name, ag.is_distributed, ar.replica_server_name,
    ar.availability_mode_desc, ars.connected_state_desc, ars.role_desc,
    ars.operational_state_desc, ars.synchronization_health_desc
FROM
    sys.availability_groups ag
    join sys.availability_replicas ar
    on ag.group_id=ar.group_id
    left join sys.dm_hadr_availability_replica_states ars
    on ars.replica_id=ar.replica_id
WHERE
    ag.is_distributed=1

现在,你已将 SQL 托管实例和 SQL Server 均切换为同步模式,这两个实例之间的复制现在是同步的。 如果需要反转此状态,请对 SQL Server 和 SQL 托管实例执行上述步骤并将状态设置为 async

检查 SQL Server 和 SQL 托管实例上的 LSN 值

若要完成故障转移或迁移,请确认复制已完成。 为此,请确保 SQL Server 和 SQL 托管实例的日志记录中的日志序列号 (LSN) 是相同的。

最初,预计主要副本上的 LSN 将高于次要副本上的 LSN。 网络延迟可能会导致复制速度落后于主要副本。 由于主要副本上的工作负载已停止,因此你应期望 LSN 匹配并在一段时间后停止更改。

在 SQL Server 上使用以下 T-SQL 查询来读取最近记录的事务日志的 LSN。 将:

  • <DatabaseName> 替换为数据库名称并查找最后一个强化的 LSN 编号。
-- Run on SQL Server
-- Obtain the last hardened LSN for the database on SQL Server.
SELECT
    ag.name AS [Replication group],
    db.name AS [Database name], 
    drs.database_id AS [Database ID], 
    drs.group_id, 
    drs.replica_id, 
    drs.synchronization_state_desc AS [Sync state], 
    drs.end_of_log_lsn AS [End of log LSN],
    drs.last_hardened_lsn AS [Last hardened LSN] 
FROM
    sys.dm_hadr_database_replica_states drs
    inner join sys.databases db on db.database_id = drs.database_id
    inner join sys.availability_groups ag on drs.group_id = ag.group_id
WHERE
    ag.is_distributed = 1 and db.name = '<DatabaseName>'

在 SQL 托管实例上使用以下 T-SQL 查询来读取数据库的最后一个强化的 LSN。 将 <DatabaseName> 替换为你的数据库名称。

此查询将适用于常规用途 SQL 托管实例。 对于业务关键 SQL 托管实例,需要取消注释脚本末尾的 and drs.is_primary_replica = 1。 在业务关键服务层级,此筛选器可确保仅读取主要副本的详细信息。

-- Run on SQL managed instance
-- Obtain the LSN for the database on SQL Managed Instance.
SELECT
    db.name AS [Database name],
    drs.database_id AS [Database ID], 
    drs.group_id, 
    drs.replica_id, 
    drs.synchronization_state_desc AS [Sync state],
    drs.end_of_log_lsn AS [End of log LSN],
    drs.last_hardened_lsn AS [Last hardened LSN]
FROM
    sys.dm_hadr_database_replica_states drs
    inner join sys.databases db on db.database_id = drs.database_id
WHERE
    db.name = '<DatabaseName>'
    -- for Business Critical, add the following as well
    -- AND drs.is_primary_replica = 1

或者,还可以使用 Get-AzSqlInstanceLink PowerShell 或 az sql mi link show Azure CLI 命令获取 SQL 托管实例上链接的 LastHardenedLsn 属性,该属性将提供与之前的 T-SQL 查询相同的信息。

重要

再次验证你的工作负载是否已在主要副本上停止。 检查 SQL Server 和 SQL 托管实例上的 LSN 是否匹配,并且在一段时间内保持匹配和不变。 如果两种实例上的 LSN 稳定,则表示已将尾部日志复制到次要副本,并且工作负载已实际停止。

故障转移数据库

如果想要使用 PowerShell 在 SQL Server 2022 和 SQL 托管实例之间故障转移数据库,同时仍保留链接,或者要对任何版本的 SQL Server 执行故障转移(但会丢失数据),请在 SSMS 中使用 SQL Server 和托管实例之间的故障转移向导来生成适合你的环境的脚本。 可以从主要副本或次要副本执行计划的故障转移。 若要执行强制故障转移,请连接到次要副本。

若要在故障转移或迁移数据库时断开链接并停止复制,而无论 SQL Server 版本如何,请使用 Remove-AzSqlInstanceLink PowerShell 或 az sql mi link delete Azure CLI 命令。

注意

  • 在进行故障转移之前,请停止源数据库上的工作负载,使复制数据库能够完全赶上进度并完成故障转移,且不会丢失数据。 如果执行强制故障转移,或者在 LSN 匹配之前断开链接,可能会丢失数据。
  • 若在 SQL Server 2019 及更早版本中故障转移数据库,会断开并删除两个副本之间的链接。 无法故障回复到初始主要副本。
  • 目前可在预览版中故障转移数据库,同时保留与 SQL Server 2022 的链接。

以下示例脚本会断开链接并结束副本之间的复制,使数据库可在两个实例上执行读/写操作。 将:

  • <ManagedInstanceName> 替换为托管实例的名称。
  • <DAGName> 替换为要进行故障转移的链接的名称(上述 Get-AzSqlInstanceLink 命令的属性 Name 的输出)。
# Run in Azure Cloud Shell (select PowerShell console) 
# =============================================================================
# POWERSHELL SCRIPT TO FAIL OVER OR MIGRATE DATABASE TO AZURE
# ===== Enter user variables here ====

# Enter your managed instance name – for example, "sqlmi1"
$ManagedInstanceName = "<ManagedInstanceName>"
$LinkName = "<DAGName>"

# ==== Do not customize the following cmdlet ====

# Find out the resource group name
$ResourceGroup = (Get-AzSqlInstance -InstanceName $ManagedInstanceName).ResourceGroupName

# Failover the specified link
Remove-AzSqlInstanceLink -ResourceGroupName $ResourceGroup |
-InstanceName $ManagedInstanceName -Name $LinkName -Force

故障转移成功后,链接将被删除,不再存在。 SQL Server 数据库和 SQL 托管实例数据库都可以执行读/写工作负荷。 它们是完全独立的。 将应用程序连接字符串重新指向你要主动使用的数据库。

重要

成功故障转移到 SQL 托管实例后,手动将应用程序连接字符串重新指向 SQL 托管实例 FQDN,以完成迁移或故障转移过程,并继续在 Azure 中运行。

清理可用性组

由于对 SQL Server 2022 进行故障转移时不会断开链接,因此可以选择保留链接和可用性组。

如果决定断开链接,或者如果要对 SQL Server 2019 及更早版本进行故障转移,则必须删除分布式可用性组以删除 SQL Server 中的链接元数据。 但是,可以选择将可用性组保留在 SQL Server 上。

若要清理可用性组资源,请替换以下值,然后运行示例代码:在以下代码中,替换:

  • <DAGName> 替换为 SQL Server 上分布式可用性组的名称(用于创建链接)。
  • <AGName> 替换为 SQL Server 上可用性组的名称(用于创建链接)。
-- Run on SQL Server
USE MASTER
GO
DROP AVAILABILITY GROUP <DAGName> --mandatory
GO
-- DROP AVAILABILITY GROUP <AGName> --optional
-- GO

疑难解答

本部分提供有关解决配置和使用链接时所遇到问题的指南。

错误

如果在创建链接或故障转移数据库时遇到错误消息,请查看查询输出窗口中的错误消息以了解详细信息。

如果在使用链接时遇到错误,查询将在失败的步骤处停止执行。 解决错误情况后,再次重新运行该命令以继续执行操作。

强制故障转移后的状态不一致

使用强制故障转移可能会导致主要副本和次要副本之间的状态不一致,从而导致两个副本处于同一角色时出现“脑裂”情况。 数据副本在此状态下失败,直到用户通过手动将一个副本指定为主要副本,将另一个副本指定为次要副本来解决这种情况。

有关链接功能的详细信息,请参阅以下资源: