排查链接问题 - Azure SQL 托管实例

适用于:Azure SQL 托管实例

本文介绍如何监视和排查 SQL Server 与 Azure SQL 托管实例之间链接的问题。

可以使用 Transact-SQL (T-SQL)、Azure PowerShell 或 Azure CLI 检查链接的状态。 如果遇到问题,可以使用错误代码来排查问题。

通过检查两个实例之间的网络并验证对于链接环境已正确准备好,可以解决创建链接的许多问题。

如果遇到链接问题,可以使用 Transact-SQL (T-SQL)、Azure PowerShell 或 Azure CLI 获取有关链接当前状态的信息。

使用 T-SQL 获取链接状态的快速状态详细信息,然后使用 Azure PowerShell 或 Azure CLI 获取有关链接当前状态的完整信息。

使用 T-SQL 确定种子设定阶段或数据同步开始后链接的状态。

使用以下 T-SQL 查询来确定 SQL Server 或 SQL 托管实例(托管通过链接放置种子的数据库)上种子设定阶段中链接的状态:

SELECT
    ag.local_database_name AS 'Local database name',
    ar.current_state AS 'Current state',
    ar.is_source AS 'Is source',
    ag.internal_state_desc AS 'Internal state desc',
    ag.database_size_bytes / 1024 / 1024 AS 'Database size MB',
    ag.transferred_size_bytes / 1024 / 1024 AS 'Transferred MB',
    ag.transfer_rate_bytes_per_second / 1024 / 1024 AS 'Transfer rate MB/s',
    ag.total_disk_io_wait_time_ms / 1000 AS 'Total Disk IO wait (sec)',
    ag.total_network_wait_time_ms / 1000 AS 'Total Network wait (sec)',
    ag.is_compression_enabled AS 'Compression',
    ag.start_time_utc AS 'Start time UTC',
    ag.estimate_time_complete_utc as 'Estimated time complete UTC',
    ar.completion_time AS 'Completion time',
    ar.number_of_attempts AS 'Attempt No'
FROM sys.dm_hadr_physical_seeding_stats AS ag
    INNER JOIN sys.dm_hadr_automatic_seeding AS ar
    ON local_physical_seeding_id = operation_id

-- Estimated seeding completion time
SELECT DISTINCT CONVERT(VARCHAR(8), DATEADD(SECOND, DATEDIFF(SECOND, start_time_utc, estimate_time_complete_utc) ,0), 108) as 'Estimated complete time'
FROM sys.dm_hadr_physical_seeding_stats

如果查询未返回任何结果,则种子设定过程尚未启动或已完成。

在开始数据同步后,对实例使用以下 T-SQL 查询来检查链接的运行状况:

DECLARE @link_name varchar(max) = '<DAGname>'
SELECT
   rs.synchronization_health_desc [Link sync health]
FROM
   sys.availability_groups ag 
   join sys.dm_hadr_availability_replica_states rs 
   on ag.group_id = rs.group_id 
WHERE 
   rs.is_local = 0 AND rs.role = 2 AND ag.is_distributed = 1 AND ag.name = @link_name 
GO

查询返回以下可能值:

  • 无结果:查询是在辅助实例上执行的。
  • HEALTHY:链接运行正常,数据正在副本之间同步。
  • NOT_HEALTHY:链接运行不正常,数据在副本之间未同步。

replicaState 值描述当前链接。 如果状态还包括“错误”,则在状态中列出的操作期间发生了错误。 例如,LinkCreationError 指示创建链接时出错。

一些可能的 replicaState 值包括:

  • CreatingLink:初始种子设定
  • LinkSynchronizing:数据复制正在进行中
  • LinkFailoverInProgress:正在进行故障转移

有关链接状态属性的完整列表,请查看分布式可用性组 - GET REST API 命令。

使用链接时可能会遇到两个不同的错误类别 - 尝试初始化链接时出错,以及尝试创建链接时出错。

初始化链接时,可能会出现以下错误(链接状态:LinkInitError):

创建链接时,可能会出现以下错误(链接状态:LinkCreationError):

  • 错误 41977:目标数据库未响应。 检查链接参数,然后重试。

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

强制故障转移后,可能会遇到裂脑情况,其中两个副本都处于主角色,导致链接处于不一致状态。 如果在灾难期间故障转移到次要副本,然后主要副本重新联机,则可能会发生这种情况。

首先,确认你处于裂脑应用场景中。 可以使用 SQL Server Management Studio (SSMS) 或 Transact-SQL (T-SQL) 来执行此操作。

在 SSMS 中连接到 SQL Server 和 SQL 托管实例,然后在对象资源管理器中,展开“Always On 高可用性”中“可用性组”节点下的“可用性副本”。 如果两个不同的副本列为“(主要)”,则表明你处于裂脑应用场景中。

或者,可以在 SQL Server SQL 托管实例上运行以下 T-SQL 脚本来检查副本的角色:

-- Execute on SQL Server and SQL Managed Instance 
USE master
DECLARE @link_name varchar(max) = '<DAGName>'
SELECT
   ag.name [Link name], 
   rs.role_desc [Link role] 
FROM
   sys.availability_groups ag 
   JOIN sys.dm_hadr_availability_replica_states rs 
   ON ag.group_id = rs.group_id 
WHERE 
   rs.is_local = 1 AND ag.is_distributed = 1 AND ag.name = @link_name 
GO

如果两个实例在“链接角色”列中列出主要,则表示你处于裂脑应用场景中。

若要解决裂脑状态,请先对原始主副本执行备份。 如果原始主副本是 SQL Server,则执行结尾日志备份。 如果原始主副本是 SQL 托管实例,则执行仅复制完整备份。 备份完成后,将分布式可用性组设置为副本的辅助角色,该副本曾经是原始主要副本,但现在将成为新的次要副本。

例如,如果发生真正的灾难,假设已强制将 SQL Server 工作负载故障转移到 Azure SQL 托管实例,并且打算继续在 SQL 托管实例 上运行工作负载,请在 SQL Server 上执行结尾日志备份,然后将分布式可用性组设置为 SQL Server 上的辅助角色,如以下示例所示:

--Execute on SQL Server 
USE master
ALTER AVAILABILITY GROUP [<DAGName>] 
SET (ROLE = SECONDARY) 
GO 

接下来,使用链接执行从 SQL 托管实例到 SQL Server 的计划内手动故障转移,如以下示例所示:

--Execute on SQL Managed Instance 
USE master
ALTER AVAILABILITY GROUP [<DAGName>] FAILOVER 
GO 

测试网络连接

SQL Server 和 SQL 托管实例之间必须有双向网络连接,链接才能正常工作。 在 SQL Server 端打开端口并在 SQL 托管实例端配置 NSG 规则后,使用 SQL Server Management Studio (SSMS) 或 Transact-SQL 测试连接。

通过在 SQL Server 和 SQL 托管实例上创建临时 SQL 代理作业来测试网络,以检查两个实例之间的连接。 在 SSMS 中使用网络检查器时,系统会自动为你创建作业,并在测试完成后将其删除。 如果使用 T-SQL 测试网络,则需要手动删除 SQL 代理作业。

备注

目前不支持通过 Linux 上 SQL Server 上的 SQL Server 代理执行 PowerShell 脚本,因此目前无法从 Linux 上 SQL Server 上的 SQL Server 代理作业执行 Test-NetConnection

若要使用 SQL 代理测试网络连接,需要满足下面的要求:

  • 执行测试的用户必须具有为 SQL Server 和 SQL 托管实例创建作业的权限(作为 sysadmin 或属于 msdb 的 SQLAgentOperatorrole)。
  • SQL Server 代理服务必须在 SQL Server 上运行。 由于代理在 SQL 托管实例上默认处于启用状态,因此无需采取其他操作。

若要使用 T-SQL 测试连接,必须沿两个方向检查连接。 首先,测试从 SQL Server 到SQL 托管实例的连接,然后测试从 SQL 托管实例到 SQL Server 的连接。

测试从 SQL Server 到 SQL 托管实例的连接

使用 SQL Server 上的 SQL Server 代理运行从 SQL Server 到 SQL 托管实例的连接测试。

  1. 连接到 SQL 托管实例并运行以下脚本以生成稍后需要用到的参数:

    SELECT 'DECLARE @serverName NVARCHAR(512) = N''' + value + ''''
    FROM sys.dm_hadr_fabric_config_parameters
    WHERE parameter_name = 'DnsRecordName'
    
    UNION
    
    SELECT 'DECLARE @node NVARCHAR(512) = N''' + NodeName + '.' + Cluster + ''''
    FROM (
        SELECT SUBSTRING(replica_address, 0, CHARINDEX('\', replica_address)) AS NodeName,
            RIGHT(service_name, CHARINDEX('/', REVERSE(service_name)) - 1) AppName,
            JoinCol = 1
        FROM sys.dm_hadr_fabric_partitions fp
        INNER JOIN sys.dm_hadr_fabric_replicas fr
            ON fp.partition_id = fr.partition_id
        INNER JOIN sys.dm_hadr_fabric_nodes fn
            ON fr.node_name = fn.node_name
        WHERE service_name LIKE '%ManagedServer%'
            AND replica_role = 2
    ) t1
    LEFT JOIN (
        SELECT value AS Cluster,
            JoinCol = 1
        FROM sys.dm_hadr_fabric_config_parameters
        WHERE parameter_name = 'ClusterName'
        ) t2
        ON (t1.JoinCol = t2.JoinCol)
    INNER JOIN (
        SELECT [value] AS AppName
        FROM sys.dm_hadr_fabric_config_parameters
        WHERE section_name = 'SQL'
            AND parameter_name = 'InstanceName'
        ) t3
        ON (t1.AppName = t3.AppName)
    
    UNION
    
    SELECT 'DECLARE @port NVARCHAR(512) = N''' + value + ''''
    FROM sys.dm_hadr_fabric_config_parameters
    WHERE parameter_name = 'HadrPort';
    

    结果应如以下示例所示:

     DECLARE @node NVARCHAR(512) = N'DB123.tr123456.china-east.worker.database.chinacloudapi.cn'
     DECLARE @port NVARCHAR(512) = N'11002'
     DECLARE @serverName NVARCHAR(512) = N'contoso-instance.12345678.database.chinacloudapi.cn'
    

    保存要在后续步骤中使用的结果。 由于这些参数可以在任何故障转移后更改,因此如有必要,请务必再次生成这些参数。

  2. 连接到 SQL Server。

  3. 打开新的查询窗口并粘贴以下脚本:

    --START
    -- Parameters section
    DECLARE @node NVARCHAR(512) = N''
    DECLARE @port NVARCHAR(512) = N''
    DECLARE @serverName NVARCHAR(512) = N''
    
    --Script section
    IF EXISTS (
            SELECT job_id
            FROM msdb.dbo.sysjobs_view
            WHERE name = N'TestMILinkConnection'
            )
        EXEC msdb.dbo.sp_delete_job @job_name = N'TestMILinkConnection',
            @delete_unused_schedule = 1
    
    DECLARE @jobId BINARY (16),
        @cmd NVARCHAR(MAX)
    
    EXEC msdb.dbo.sp_add_job @job_name = N'TestMILinkConnection',
        @enabled = 1,
        @job_id = @jobId OUTPUT
    
    SET @cmd = (N'tnc ' + @serverName + N' -port 5022 | select ComputerName, RemoteAddress, TcpTestSucceeded | Format-List')
    
    EXEC msdb.dbo.sp_add_jobstep @job_id = @jobId,
        @step_name = N'Test Port 5022',
        @step_id = 1,
        @cmdexec_success_code = 0,
        @on_success_action = 3,
        @on_fail_action = 3,
        @subsystem = N'PowerShell',
        @command = @cmd,
        @database_name = N'master'
    
    SET @cmd = (N'tnc ' + @node + N' -port ' + @port + ' | select ComputerName, RemoteAddress, TcpTestSucceeded | Format-List')
    
    EXEC msdb.dbo.sp_add_jobstep @job_id = @jobId,
        @step_name = N'Test HADR Port',
        @step_id = 2,
        @cmdexec_success_code = 0,
        @subsystem = N'PowerShell',
        @command = @cmd,
        @database_name = N'master'
    
    EXEC msdb.dbo.sp_add_jobserver @job_id = @jobId,
        @server_name = N'(local)'
    GO
    
    EXEC msdb.dbo.sp_start_job @job_name = N'TestMILinkConnection'
    GO
    
    --Check status every 5 seconds
    DECLARE @RunStatus INT
    
    SET @RunStatus = 10
    
    WHILE (@RunStatus >= 4)
    BEGIN
        SELECT DISTINCT @RunStatus = run_status
        FROM [msdb].[dbo].[sysjobhistory] JH
        INNER JOIN [msdb].[dbo].[sysjobs] J
            ON JH.job_id = J.job_id
        WHERE J.name = N'TestMILinkConnection'
            AND step_id = 0
    
        WAITFOR DELAY '00:00:05';
    END
    
    --Get logs once job completes
    SELECT [step_name],
        SUBSTRING([message], CHARINDEX('TcpTestSucceeded', [message]), CHARINDEX('Process Exit', [message]) - CHARINDEX('TcpTestSucceeded', [message])) AS    TcpTestResult,
        SUBSTRING([message], CHARINDEX('RemoteAddress', [message]), CHARINDEX('TcpTestSucceeded', [message]) - CHARINDEX('RemoteAddress', [message])) AS    RemoteAddressResult,
        [run_status],
        [run_duration],
        [message]
    FROM [msdb].[dbo].[sysjobhistory] JH
    INNER JOIN [msdb].[dbo].[sysjobs] J
        ON JH.job_id = J.job_id
    WHERE J.name = N'TestMILinkConnection'
        AND step_id <> 0
        --END
    
  4. @node@port@serverName 参数替换为从第一步中获取的值。

  5. 运行脚本并检查结果。 应显示如以下示例所示的结果:

    屏幕截图显示包含 S S M S 中测试结果的输出。

  6. 验证结果:

    • TcpTestSucceeded 的每个测试的结果应为 TcpTestSucceeded : True
    • RemoteAddresses 应属于 SQL 托管实例子网的 IP 范围。

    如果响应为不成功,请验证以下网络设置:

    • 网络防火墙和 SQL Server 主机操作系统 (Windows/Linux) 防火墙中是否都有允许流量发往 SQL 托管实例的整个子网 IP 范围的规则。
    • 托管 SQL 托管实例的虚拟网络是否有一个允许在端口 5022 上进行通信的 NSG 规则。

测试从 SQL 托管实例到 SQL Server 的连接

若要检查 SQL 托管实例是否可以访问 SQL Server,请首先创建一个测试终结点。 然后使用 SQL Server 代理运行 PowerShell 脚本,并通过 tnc 命令从 SQL 托管实例对端口 5022 上的 SQL Server 执行 ping。

若要创建测试终结点,请连接到 SQL Server 并运行以下 T-SQL 脚本:

-- Run on SQL Server
-- Create the certificate needed for the test endpoint
USE MASTER
CREATE CERTIFICATE TEST_CERT
WITH SUBJECT = N'Certificate for SQL Server',
EXPIRY_DATE = N'3/30/2051'
GO

-- Create the test endpoint on SQL Server
USE MASTER
CREATE ENDPOINT TEST_ENDPOINT
    STATE=STARTED
    AS TCP (LISTENER_PORT=5022, LISTENER_IP = ALL)
    FOR DATABASE_MIRRORING (
        ROLE=ALL,
        AUTHENTICATION = CERTIFICATE TEST_CERT,
        ENCRYPTION = REQUIRED ALGORITHM AES
    )

若要验证 SQL Server 终结点是否在端口 5022 上接收连接,请在 SQL Server 实例的主机操作系统上运行以下 PowerShell 命令:

tnc localhost -port 5022

如果测试成功,则会显示 TcpTestSucceeded : True。 然后可以继续在托管实例上创建 SQL Server 代理作业,以尝试从 SQL 托管实例测试端口 5022 上的 SQL Server 测试终结点。

接下来,通过在 SQL 托管实例上运行以下 T-SQL 脚本,在 SQL 托管实例上创建名为 NetHelper 的 SQL Server 代理作业。 替换:

  • <SQL_SERVER_IP_ADDRESS> 替换为可从 SQL 托管实例访问的 SQL Server 的 IP 地址。
-- Run on SQL managed instance
-- SQL_SERVER_IP_ADDRESS should be an IP address that could be accessed from the SQL Managed Instance host machine.
DECLARE @SQLServerIpAddress NVARCHAR(MAX) = '<SQL_SERVER_IP_ADDRESS>'; -- insert your SQL Server IP address in here
DECLARE @tncCommand NVARCHAR(MAX) = 'tnc ' + @SQLServerIpAddress + ' -port 5022 -InformationLevel Quiet';
DECLARE @jobId BINARY(16);

IF EXISTS (
        SELECT *
        FROM msdb.dbo.sysjobs
        WHERE name = 'NetHelper'
        ) THROW 70000,
    'Agent job NetHelper already exists. Please rename the job, or drop the existing job before creating it again.',
    1
    -- To delete NetHelper job run: EXEC msdb.dbo.sp_delete_job @job_name=N'NetHelper'
    EXEC msdb.dbo.sp_add_job @job_name = N'NetHelper',
        @enabled = 1,
        @description = N'Test SQL Managed Instance to SQL Server network connectivity on port 5022.',
        @category_name = N'[Uncategorized (Local)]',
        @owner_login_name = N'sa',
        @job_id = @jobId OUTPUT;

EXEC msdb.dbo.sp_add_jobstep @job_id = @jobId,
    @step_name = N'TNC network probe from SQL MI to SQL Server',
    @step_id = 1,
    @os_run_priority = 0,
    @subsystem = N'PowerShell',
    @command = @tncCommand,
    @database_name = N'master',
    @flags = 40;

EXEC msdb.dbo.sp_update_job @job_id = @jobId,
    @start_step_id = 1;

EXEC msdb.dbo.sp_add_jobserver @job_id = @jobId,
    @server_name = N'(local)';

提示

若要修改用于从 SQL 托管实例进行连接探测的 SQL Server IP 地址,请通过运行 EXEC msdb.dbo.sp_delete_job @job_name=N'NetHelper' 删除 NetHelper 作业,并使用上一个脚本重新创建 NetHelper 作业。

然后,创建一个存储过程 ExecuteNetHelper,以帮助运行作业,并从网络探测中获取结果。 在 SQL 托管实例上运行以下 T-SQL 脚本:

-- Run on managed instance
IF EXISTS(SELECT * FROM sys.objects WHERE name = 'ExecuteNetHelper')
    THROW 70001, 'Stored procedure ExecuteNetHelper already exists. Rename or drop the existing procedure before creating it again.', 1
GO
CREATE PROCEDURE ExecuteNetHelper AS
-- To delete the procedure run: DROP PROCEDURE ExecuteNetHelper
BEGIN
    -- Start the job.
    DECLARE @NetHelperstartTimeUtc DATETIME = GETUTCDATE();
    DECLARE @stop_exec_date DATETIME = NULL;

    EXEC msdb.dbo.sp_start_job @job_name = N'NetHelper';

    -- Wait for job to complete and then see the outcome.
    WHILE (@stop_exec_date IS NULL)
    BEGIN
        -- Wait and see if the job has completed.
        WAITFOR DELAY '00:00:01'

        SELECT @stop_exec_date = sja.stop_execution_date
        FROM msdb.dbo.sysjobs sj
        INNER JOIN msdb.dbo.sysjobactivity sja
            ON sj.job_id = sja.job_id
        WHERE sj.name = 'NetHelper'

        -- If job has completed, get the outcome of the network test.
        IF (@stop_exec_date IS NOT NULL)
        BEGIN
            SELECT sj.name JobName,
                sjsl.date_modified AS 'Date executed',
                sjs.step_name AS 'Step executed',
                sjsl.log AS 'Connectivity status'
            FROM msdb.dbo.sysjobs sj
            LEFT JOIN msdb.dbo.sysjobsteps sjs
                ON sj.job_id = sjs.job_id
            LEFT JOIN msdb.dbo.sysjobstepslogs sjsl
                ON sjs.step_uid = sjsl.step_uid
            WHERE sj.name = 'NetHelper'
        END

        -- In case of operation timeout (90 seconds), print timeout message.
        IF (datediff(second, @NetHelperstartTimeUtc, getutcdate()) > 90)
        BEGIN
            SELECT 'NetHelper timed out during the network check. Please investigate SQL Agent logs for more information.'

            BREAK;
        END
    END
END;

对 SQL 托管实例运行以下查询以执行将执行 NetHelper 代理作业的存储过程,并显示生成的日志:

-- Run on managed instance
EXEC ExecuteNetHelper;

如果连接成功,日志将显示 True。 如果连接失败,日志将显示 False

显示 NetHelper SQL 代理作业预期输出的屏幕截图。

如果连接不成功,请验证以下各项:

  • 主机 SQL Server 实例上的防火墙是否允许端口 5022 上的入站和出站通信。
  • 托管 SQL 托管实例的虚拟网络是否有一个允许在端口 5022 上进行通信的 NSG 规则。
  • 如果 SQL Server 实例位于 Azure VM 上,那么托管该 VM 的虚拟网络是否有一个允许在端口 5022 上进行通信的 NSG 规则。
  • SQL Server 是否正在运行。
  • SQL Server 上是否存在测试终结点。

解决问题后,通过在托管实例上运行 EXEC ExecuteNetHelper 来重新运行 NetHelper 网络探测。

最后,在网络测试成功完成后,请在 SQL Server 上使用以下 T-SQL 命令删除测试终结点和证书:

-- Run on SQL Server
DROP ENDPOINT TEST_ENDPOINT;
GO
DROP CERTIFICATE TEST_CERT;
GO

注意

仅当源环境和目标环境之间有已经过验证的网络连接时,才继续执行后续步骤。 否则,请先排查网络连接问题,然后再继续。

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