SQL Server 诊断支持脚本 - Amazon 数据库迁移服务
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

SQL Server 诊断支持脚本

接下来,您可以找到诊断支持脚本的说明,该脚本可用于分析 Amazon DMS 迁移配置中的本地数据库或 Amazon RDS for SQL Server 数据库。这些脚本可以与源端点或目标端点一起使用。对于本地数据库,请在 sqlcmd 命令行实用程序中运行这些脚本。有关使用此实用程序的更多信息,请参阅 Microsoft 文档中的 sqlcmd – 使用实用程序

对于 Amazon RDS 数据库,您无法使用 sqlcmd 命令行实用程序进行连接。而是使用连接到 Amazon RDS SQL Server 的任意客户端工具运行这些脚本。

在运行脚本之前,请确保您使用的用户账户具有所需的访问权限,可以访问 SQL Server 数据库。无论是本地数据库还是 Amazon RDS 数据库,您都可以使用与访问 SQL Server 数据库相同的权限,无需 SysAdmin 角色。

为本地 SQL Server 数据库设置最低权限

为运行本地 SQL Server 数据库设置最低权限
  1. 使用 SQL Server Management Studio (SSMS) 创建采用密码身份验证的新 SQL Server 账户,例如 on-prem-user

  2. 在 SSMS 的用户映射部分中,选择 MSDBMASTER 数据库(将提供公有权限),然后为您要运行脚本的数据库分配 DB_OWNER 角色。

  3. 打开新账户的上下文(右键单击)菜单,选择安全性,然后明确授予 Connect SQL 权限。

  4. 运行以下授权命令。

    GRANT VIEW SERVER STATE TO on-prem-user; USE MSDB; GRANT SELECT ON MSDB.DBO.BACKUPSET TO on-prem-user; GRANT SELECT ON MSDB.DBO.BACKUPMEDIAFAMILY TO on-prem-user; GRANT SELECT ON MSDB.DBO.BACKUPFILE TO on-prem-user;

为 Amazon RDS SQL Server 数据库设置最低权限

以最低权限运行 Amazon RDS SQL Server 数据库
  1. 使用 SQL Server Management Studio (SSMS) 创建采用密码身份验证的新 SQL Server 账户,例如 rds-user

  2. 在 SSMS 的用户映射部分中,选择 MSDB 数据库(将提供公有权限),然后为您要运行脚本的数据库分配 DB_OWNER 角色。

  3. 打开新账户的上下文(右键单击)菜单,选择安全性,然后明确授予 Connect SQL 权限。

  4. 运行以下授权命令。

    GRANT VIEW SERVER STATE TO rds-user; USE MSDB; GRANT SELECT ON MSDB.DBO.BACKUPSET TO rds-user; GRANT SELECT ON MSDB.DBO.BACKUPMEDIAFAMILY TO rds-user; GRANT SELECT ON MSDB.DBO.BACKUPFILE TO rds-user;

在独立 SQL Server 上设置持续复制:不使用 sysadmin 角色

本节介绍如何针对 SQL Server 数据库源设置不需要用户账户具有 sysadmin 权限的持续复制。

注意

运行本节中的步骤后,非 sysadmin DMS 用户将有权执行以下操作:

  • 从联机事务日志文件中读取更改

  • 通过访问磁盘从事务日志备份文件中读取更改

  • 添加或更改 DMS 使用的发布内容

  • 向发布内容中添加文章

  1. 按照捕获本地或 Amazon EC2 上的自管理 SQL Server 的数据更改中所述设置用于复制的 Microsoft SQL Server。

  2. 在源数据库上启用 MS-REPLICATION。这可以手动完成,也可以通过 sysadmin 用户身份运行一次任务来完成。

  3. 使用以下脚本,在源数据库上创建 awsdms 架构:

    use master go create schema awsdms go -- Create the table valued function [awsdms].[split_partition_list] on the Master database, as follows: USE [master] GO set ansi_nulls on go set quoted_identifier on go if (object_id('[awsdms].[split_partition_list]','TF')) is not null drop function [awsdms].[split_partition_list]; go create function [awsdms].[split_partition_list] ( @plist varchar(8000), —A delimited list of partitions @dlm nvarchar(1) —Delimiting character ) returns @partitionsTable table —Table holding the BIGINT values of the string fragments ( pid bigint primary key ) as begin declare @partition_id bigint; declare @dlm_pos integer; declare @dlm_len integer; set @dlm_len = len(@dlm); while (charindex(@dlm,@plist)>0) begin set @dlm_pos = charindex(@dlm,@plist); set @partition_id = cast( ltrim(rtrim(substring(@plist,1,@dlm_pos-1))) as bigint); insert into @partitionsTable (pid) values (@partition_id) set @plist = substring(@plist,@dlm_pos+@dlm_len,len(@plist)); end set @partition_id = cast (ltrim(rtrim(@plist)) as bigint); insert into @partitionsTable (pid) values ( @partition_id ); return end GO
  4. 使用以下脚本,在 Master 数据库上创建 [awsdms].[rtm_dump_dblog] 过程:

    use [MASTER] go if (object_id('[awsdms].[rtm_dump_dblog]','P')) is not null drop procedure [awsdms].[rtm_dump_dblog]; go set ansi_nulls on go set quoted_identifier on GO CREATE procedure [awsdms].[rtm_dump_dblog] ( @start_lsn varchar(32), @seqno integer, @filename varchar(260), @partition_list varchar(8000), — A comma delimited list: P1,P2,... Pn @programmed_filtering integer, @minPartition bigint, @maxPartition bigint ) as begin declare @start_lsn_cmp varchar(32); — Stands against the GT comparator SET NOCOUNT ON — – Disable "rows affected display" set @start_lsn_cmp = @start_lsn; if (@start_lsn_cmp) is null set @start_lsn_cmp = '00000000:00000000:0000'; if (@partition_list is null) begin RAISERROR ('Null partition list waspassed',16,1); return end if (@start_lsn) is not null set @start_lsn = '0x'+@start_lsn; if (@programmed_filtering=0) SELECT [Current LSN], [operation], [Context], [Transaction ID], [Transaction Name], [Begin Time], [End Time], [Flag Bits], [PartitionID], [Page ID], [Slot ID], [RowLog Contents 0], [Log Record], [RowLog Contents 1] FROM fn_dump_dblog ( @start_lsn, NULL, N'DISK', @seqno, @filename, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default) where [Current LSN] collate SQL_Latin1_General_CP1_CI_AS > @start_lsn_cmp collate SQL_Latin1_General_CP1_CI_AS and ( ( [operation] in ('LOP_BEGIN_XACT','LOP_COMMIT_XACT','LOP_ABORT_XACT') ) or ( [operation] in ('LOP_INSERT_ROWS','LOP_DELETE_ROWS','LOP_MODIFY_ROW') and ( ( [context] in ('LCX_HEAP','LCX_CLUSTERED','LCX_MARK_AS_GHOST') ) or ([context] = 'LCX_TEXT_MIX' and (datalength([RowLog Contents 0]) in (0,1)))) and [PartitionID] in ( select * from master.awsdms.split_partition_list (@partition_list,',')) ) or ([operation] = 'LOP_HOBT_DDL') ) else SELECT [Current LSN], [operation], [Context], [Transaction ID], [Transaction Name], [Begin Time], [End Time], [Flag Bits], [PartitionID], [Page ID], [Slot ID], [RowLog Contents 0], [Log Record], [RowLog Contents 1] — After Image FROM fn_dump_dblog ( @start_lsn, NULL, N'DISK', @seqno, @filename, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default) where [Current LSN] collate SQL_Latin1_General_CP1_CI_AS > @start_lsn_cmp collate SQL_Latin1_General_CP1_CI_AS and ( ( [operation] in ('LOP_BEGIN_XACT','LOP_COMMIT_XACT','LOP_ABORT_XACT') ) or ( [operation] in ('LOP_INSERT_ROWS','LOP_DELETE_ROWS','LOP_MODIFY_ROW') and ( ( [context] in ('LCX_HEAP','LCX_CLUSTERED','LCX_MARK_AS_GHOST') ) or ([context] = 'LCX_TEXT_MIX' and (datalength([RowLog Contents 0]) in (0,1)))) and ([PartitionID] is not null) and ([PartitionID] >= @minPartition and [PartitionID]<=@maxPartition) ) or ([operation] = 'LOP_HOBT_DDL') ) SET NOCOUNT OFF — Re-enable "rows affected display" end GO
  5. 使用以下脚本,在 Master 数据库上创建证书:

    Use [master] Go CREATE CERTIFICATE [awsdms_rtm_dump_dblog_cert] ENCRYPTION BY PASSWORD = N'@5trongpassword' WITH SUBJECT = N'Certificate for FN_DUMP_DBLOG Permissions';
  6. 使用以下脚本,从证书创建登录名:

    Use [master] Go CREATE LOGIN awsdms_rtm_dump_dblog_login FROM CERTIFICATE [awsdms_rtm_dump_dblog_cert];
  7. 使用以下脚本,将登录名添加到 sysadmin 服务器角色:

    ALTER SERVER ROLE [sysadmin] ADD MEMBER [awsdms_rtm_dump_dblog_login];
  8. 通过以下脚本,使用证书将签名添加到 [master].[awsdms].[rtm_dump_dblog]。

    Use [master] GO ADD SIGNATURE TO [master].[awsdms].[rtm_dump_dblog] BY CERTIFICATE [awsdms_rtm_dump_dblog_cert] WITH PASSWORD = '@5trongpassword';
    注意

    如果重新创建存储过程,您需要再次添加签名。

  9. 使用以下脚本在 Master 数据库上创建 [awsdms].[rtm_position_1st_timestamp]:

    use [master] if object_id('[awsdms].[rtm_position_1st_timestamp]','P') is not null DROP PROCEDURE [awsdms].[rtm_position_1st_timestamp]; go create procedure [awsdms].[rtm_position_1st_timestamp] ( @dbname sysname, -- Database name @seqno integer, -- Backup set sequence/position number within file @filename varchar(260), -- The backup filename @1stTimeStamp varchar(40) -- The timestamp to position by ) as begin SET NOCOUNT ON -- Disable "rows affected display" declare @firstMatching table ( cLsn varchar(32), bTim datetime ) declare @sql nvarchar(4000) declare @nl char(2) declare @tb char(2) declare @fnameVar nvarchar(254) = 'NULL' set @nl = char(10); -- New line set @tb = char(9) -- Tab separator if (@filename is not null) set @fnameVar = ''''+@filename +'''' set @sql='use ['+@dbname+'];'+@nl+ 'select top 1 [Current LSN],[Begin Time]'+@nl+ 'FROM fn_dump_dblog (NULL, NULL, NULL, '+ cast(@seqno as varchar(10))+','+ @fnameVar+','+@nl+ @tb+'default, default, default, default, default, default, default,'+@nl+ @tb+'default, default, default, default, default, default, default,'+@nl+ @tb+'default, default, default, default, default, default, default,'+@nl+ @tb+'default, default, default, default, default, default, default,'+@nl+ @tb+'default, default, default, default, default, default, default,'+@nl+ @tb+'default, default, default, default, default, default, default,'+@nl+ @tb+'default, default, default, default, default, default, default,'+@nl+ @tb+'default, default, default, default, default, default, default,'+@nl+ @tb+'default, default, default, default, default, default, default)'+@nl+ 'where operation=''LOP_BEGIN_XACT''' +@nl+ 'and [Begin Time]>= cast('+''''+@1stTimeStamp+''''+' as datetime)'+@nl --print @sql delete from @firstMatching insert into @firstMatching exec sp_executesql @sql -- Get them all select top 1 cLsn as [matching LSN],convert(varchar,bTim,121) as [matching Timestamp] from @firstMatching; SET NOCOUNT OFF -- Re-enable "rows affected display" end GO
  10. 使用以下脚本,在 Master 数据库上创建证书:

    Use [master] Go CREATE CERTIFICATE [awsdms_rtm_position_1st_timestamp_cert] ENCRYPTION BY PASSWORD = '@5trongpassword' WITH SUBJECT = N'Certificate for FN_POSITION_1st_TIMESTAMP Permissions';
  11. 使用以下脚本,从证书创建登录名:

    Use [master] Go CREATE LOGIN awsdms_rtm_position_1st_timestamp_login FROM CERTIFICATE [awsdms_rtm_position_1st_timestamp_cert];
  12. 使用以下脚本,将登录名添加到 sysadmin 角色:

    ALTER SERVER ROLE [sysadmin] ADD MEMBER [awsdms_rtm_position_1st_timestamp_login];
  13. 通过以下脚本,使用证书将签名添加到 [master].[awsdms].[rtm_position_1st_timestamp]:

    Use [master] GO ADD SIGNATURE TO [master].[awsdms].[rtm_position_1st_timestamp] BY CERTIFICATE [awsdms_rtm_position_1st_timestamp_cert] WITH PASSWORD = '@5trongpassword';
  14. 使用以下脚本向 DMS 用户授予对新存储过程的执行访问权限:

    use master go GRANT execute on [awsdms].[rtm_position_1st_timestamp] to dms_user;
  15. 在以下每个数据库中,创建具有以下权限和角色的用户:

    注意

    您应该在每个副本上使用相同的 SID 创建 dmsnosysadmin 用户账户。以下 SQL 查询可以帮助验证每个副本上的 dmsnosysadmin 账户 SID 值。有关创建用户的更多信息,请参阅 Microsoft SQL Server 文档中的 CREATE USER (Transact-SQL)。有关为 Azure SQL 数据库创建 SQL 用户账户的更多信息,请参阅活动异地复制

    use master go grant select on sys.fn_dblog to [DMS_user] grant view any definition to [DMS_user] grant view server state to [DMS_user]—(should be granted to the login). grant execute on sp_repldone to [DMS_user] grant execute on sp_replincrementlsn to [DMS_user] grant execute on sp_addpublication to [DMS_user] grant execute on sp_addarticle to [DMS_user] grant execute on sp_articlefilter to [DMS_user] grant select on [awsdms].[split_partition_list] to [DMS_user] grant execute on [awsdms].[rtm_dump_dblog] to [DMS_user]
    use MSDB go grant select on msdb.dbo.backupset to [DMS_user] grant select on msdb.dbo.backupmediafamily to [DMS_user] grant select on msdb.dbo.backupfile to [DMS_user]

    在源数据库上运行以下脚本:

    EXEC sp_addrolemember N'db_owner', N'DMS_user' use Source_DB go
  16. 最后,向源 SQL Server 端点添加额外连接属性 (ECA):

    enableNonSysadminWrapper=true;

在可用性组环境中的 SQL Server 上设置持续复制:无 sysadmin 角色

本节介绍如何在不需要用户账户具有 sysadmin 权限的可用性组环境中为 SQL Server 数据库源设置持续复制。

注意

运行本节中的步骤后,非 sysadmin DMS 用户将有权执行以下操作:

  • 从联机事务日志文件中读取更改

  • 通过访问磁盘从事务日志备份文件中读取更改

  • 添加或更改 DMS 使用的发布内容

  • 向发布内容中添加文章

在可用性组环境中设置持续复制而不使用 sysadmin 用户
  1. 按照捕获本地或 Amazon EC2 上的自管理 SQL Server 的数据更改中所述设置用于复制的 Microsoft SQL Server。

  2. 在源数据库上启用 MS-REPLICATION。这可以手动完成,也可以使用 sysadmin 用户身份运行一次任务来完成。

    注意

    您应该将 MS-REPLICATION 分发服务器配置为本地分发服务器,或者配置为允许非 sysadmin 用户通过关联的链接服务器进行访问。

  3. 如果启用在单个任务中独占使用 sp_repldone 端点选项,请停止 MS-REPLICATION 日志读取器作业。

  4. 在每个副本上,执行以下步骤:

    1. 在 Master 数据库中创建 [awsdms][awsdms] 架构:

      CREATE SCHEMA [awsdms]
    2. 在 Master 数据库中创建 [awsdms].[split_partition_list] 表值函数:

      USE [master] GO SET ansi_nulls on GO SET quoted_identifier on GO IF (object_id('[awsdms].[split_partition_list]','TF')) is not null DROP FUNCTION [awsdms].[split_partition_list]; GO CREATE FUNCTION [awsdms].[split_partition_list] ( @plist varchar(8000), --A delimited list of partitions @dlm nvarchar(1) --Delimiting character ) RETURNS @partitionsTable table --Table holding the BIGINT values of the string fragments ( pid bigint primary key ) AS BEGIN DECLARE @partition_id bigint; DECLARE @dlm_pos integer; DECLARE @dlm_len integer; SET @dlm_len = len(@dlm); WHILE (charindex(@dlm,@plist)>0) BEGIN SET @dlm_pos = charindex(@dlm,@plist); SET @partition_id = cast( ltrim(rtrim(substring(@plist,1,@dlm_pos-1))) as bigint); INSERT into @partitionsTable (pid) values (@partition_id) SET @plist = substring(@plist,@dlm_pos+@dlm_len,len(@plist)); END SET @partition_id = cast (ltrim(rtrim(@plist)) as bigint); INSERT into @partitionsTable (pid) values ( @partition_id ); RETURN END GO
    3. 在 Master 数据库中创建 [awsdms].[rtm_dump_dblog] 过程:

      USE [MASTER] GO IF (object_id('[awsdms].[rtm_dump_dblog]','P')) is not null DROP PROCEDURE [awsdms].[rtm_dump_dblog]; GO SET ansi_nulls on GO SET quoted_identifier on GO CREATE PROCEDURE [awsdms].[rtm_dump_dblog] ( @start_lsn varchar(32), @seqno integer, @filename varchar(260), @partition_list varchar(8000), -- A comma delimited list: P1,P2,... Pn @programmed_filtering integer, @minPartition bigint, @maxPartition bigint ) AS BEGIN DECLARE @start_lsn_cmp varchar(32); -- Stands against the GT comparator SET NOCOUNT ON -- Disable "rows affected display" SET @start_lsn_cmp = @start_lsn; IF (@start_lsn_cmp) is null SET @start_lsn_cmp = '00000000:00000000:0000'; IF (@partition_list is null) BEGIN RAISERROR ('Null partition list was passed',16,1); return --set @partition_list = '0,'; -- A dummy which is never matched END IF (@start_lsn) is not null SET @start_lsn = '0x'+@start_lsn; IF (@programmed_filtering=0) SELECT [Current LSN], [operation], [Context], [Transaction ID], [Transaction Name], [Begin Time], [End Time], [Flag Bits], [PartitionID], [Page ID], [Slot ID], [RowLog Contents 0], [Log Record], [RowLog Contents 1] -- After Image FROM fn_dump_dblog ( @start_lsn, NULL, N'DISK', @seqno, @filename, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default) WHERE [Current LSN] collate SQL_Latin1_General_CP1_CI_AS > @start_lsn_cmp collate SQL_Latin1_General_CP1_CI_AS -- This aims for implementing FN_DBLOG based on GT comparator. AND ( ( [operation] in ('LOP_BEGIN_XACT','LOP_COMMIT_XACT','LOP_ABORT_XACT') ) OR ( [operation] in ('LOP_INSERT_ROWS','LOP_DELETE_ROWS','LOP_MODIFY_ROW') AND ( ( [context] in ('LCX_HEAP','LCX_CLUSTERED','LCX_MARK_AS_GHOST') ) or ([context] = 'LCX_TEXT_MIX') ) AND [PartitionID] in ( select * from master.awsdms.split_partition_list (@partition_list,',')) ) OR ([operation] = 'LOP_HOBT_DDL') ) ELSE SELECT [Current LSN], [operation], [Context], [Transaction ID], [Transaction Name], [Begin Time], [End Time], [Flag Bits], [PartitionID], [Page ID], [Slot ID], [RowLog Contents 0], [Log Record], [RowLog Contents 1] -- After Image FROM fn_dump_dblog ( @start_lsn, NULL, N'DISK', @seqno, @filename, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default) WHERE [Current LSN] collate SQL_Latin1_General_CP1_CI_AS > @start_lsn_cmp collate SQL_Latin1_General_CP1_CI_AS -- This aims for implementing FN_DBLOG based on GT comparator. AND ( ( [operation] in ('LOP_BEGIN_XACT','LOP_COMMIT_XACT','LOP_ABORT_XACT') ) OR ( [operation] in ('LOP_INSERT_ROWS','LOP_DELETE_ROWS','LOP_MODIFY_ROW') AND ( ( [context] in ('LCX_HEAP','LCX_CLUSTERED','LCX_MARK_AS_GHOST') ) or ([context] = 'LCX_TEXT_MIX') ) AND ([PartitionID] is not null) and ([PartitionID] >= @minPartition and [PartitionID]<=@maxPartition) ) OR ([operation] = 'LOP_HOBT_DDL') ) SET NOCOUNT OFF -- Re-enable "rows affected display" END GO
    4. 在 Master 数据库上创建证书:

      USE [master] GO CREATE CERTIFICATE [awsdms_rtm_dump_dblog_cert] ENCRYPTION BY PASSWORD = N'@hardpassword1' WITH SUBJECT = N'Certificate for FN_DUMP_DBLOG Permissions'
    5. 从证书创建登录名:

      USE [master] GO CREATE LOGIN awsdms_rtm_dump_dblog_login FROM CERTIFICATE [awsdms_rtm_dump_dblog_cert];
    6. 将登录名添加到 sysadmin 服务器角色:

      ALTER SERVER ROLE [sysadmin] ADD MEMBER [awsdms_rtm_dump_dblog_login];
    7. 使用证书将签名添加到 [master].[awsdms].[rtm_dump_dblog] 过程:

      USE [master] GO ADD SIGNATURE TO [master].[awsdms].[rtm_dump_dblog] BY CERTIFICATE [awsdms_rtm_dump_dblog_cert] WITH PASSWORD = '@hardpassword1';
      注意

      如果重新创建存储过程,您需要再次添加签名。

    8. 在 Master 数据库中创建 [awsdms].[rtm_position_1st_timestamp] 过程:

      USE [master] IF object_id('[awsdms].[rtm_position_1st_timestamp]','P') is not null DROP PROCEDURE [awsdms].[rtm_position_1st_timestamp]; GO CREATE PROCEDURE [awsdms].[rtm_position_1st_timestamp] ( @dbname sysname, -- Database name @seqno integer, -- Backup set sequence/position number within file @filename varchar(260), -- The backup filename @1stTimeStamp varchar(40) -- The timestamp to position by ) AS BEGIN SET NOCOUNT ON -- Disable "rows affected display" DECLARE @firstMatching table ( cLsn varchar(32), bTim datetime ) DECLARE @sql nvarchar(4000) DECLARE @nl char(2) DECLARE @tb char(2) DECLARE @fnameVar sysname = 'NULL' SET @nl = char(10); -- New line SET @tb = char(9) -- Tab separator IF (@filename is not null) SET @fnameVar = ''''+@filename +'''' SET @filename = ''''+@filename +'''' SET @sql='use ['+@dbname+'];'+@nl+ 'SELECT TOP 1 [Current LSN],[Begin Time]'+@nl+ 'FROM fn_dump_dblog (NULL, NULL, NULL, '+ cast(@seqno as varchar(10))+','+ @filename +','+@nl+ @tb+'default, default, default, default, default, default, default,'+@nl+ @tb+'default, default, default, default, default, default, default,'+@nl+ @tb+'default, default, default, default, default, default, default,'+@nl+ @tb+'default, default, default, default, default, default, default,'+@nl+ @tb+'default, default, default, default, default, default, default,'+@nl+ @tb+'default, default, default, default, default, default, default,'+@nl+ @tb+'default, default, default, default, default, default, default,'+@nl+ @tb+'default, default, default, default, default, default, default,'+@nl+ @tb+'default, default, default, default, default, default, default)'+@nl+ 'WHERE operation=''LOP_BEGIN_XACT''' +@nl+ 'AND [Begin Time]>= cast('+''''+@1stTimeStamp+''''+' as datetime)'+@nl --print @sql DELETE FROM @firstMatching INSERT INTO @firstMatching exec sp_executesql @sql -- Get them all SELECT TOP 1 cLsn as [matching LSN],convert(varchar,bTim,121) AS[matching Timestamp] FROM @firstMatching; SET NOCOUNT OFF -- Re-enable "rows affected display" END GO
    9. 在 Master 数据库上创建证书:

      USE [master] GO CREATE CERTIFICATE [awsdms_rtm_position_1st_timestamp_cert] ENCRYPTION BY PASSWORD = N'@hardpassword1' WITH SUBJECT = N'Certificate for FN_POSITION_1st_TIMESTAMP Permissions';
    10. 从证书创建登录名:

      USE [master] GO CREATE LOGIN awsdms_rtm_position_1st_timestamp_login FROM CERTIFICATE [awsdms_rtm_position_1st_timestamp_cert];
    11. 将登录名添加到 sysadmin 服务器角色:

      ALTER SERVER ROLE [sysadmin] ADD MEMBER [awsdms_rtm_position_1st_timestamp_login];
    12. 使用证书将签名添加到 [master].[awsdms].[rtm_position_1st_timestamp] 过程:

      USE [master] GO ADD SIGNATURE TO [master].[awsdms].[rtm_position_1st_timestamp] BY CERTIFICATE [awsdms_rtm_position_1st_timestamp_cert] WITH PASSWORD = '@hardpassword1';
      注意

      如果重新创建存储过程,您需要再次添加签名。

    13. 在以下每个数据库中,创建具有以下权限/角色的用户:

      注意

      您应该在每个副本上使用相同的 SID 创建 dmsnosysadmin 用户账户。以下 SQL 查询可以帮助验证每个副本上的 dmsnosysadmin 账户 SID 值。有关创建用户的更多信息,请参阅 Microsoft SQL Server 文档中的 CREATE USER (Transact-SQL)。有关为 Azure SQL 数据库创建 SQL 用户账户的更多信息,请参阅活动异地复制

      SELECT @@servername servername, name, sid, create_date, modify_date FROM sys.server_principals WHERE name = 'dmsnosysadmin';
    14. 在每个副本上授予对 Master 数据库的权限:

      USE master GO GRANT select on sys.fn_dblog to dmsnosysadmin; GRANT view any definition to dmsnosysadmin; GRANT view server state to dmsnosysadmin -- (should be granted to the login). GRANT execute on sp_repldone to dmsnosysadmin; GRANT execute on sp_replincrementlsn to dmsnosysadmin; GRANT execute on sp_addpublication to dmsnosysadmin; GRANT execute on sp_addarticle to dmsnosysadmin; GRANT execute on sp_articlefilter to dmsnosysadmin; GRANT select on [awsdms].[split_partition_list] to dmsnosysadmin; GRANT execute on [awsdms].[rtm_dump_dblog] to dmsnosysadmin; GRANT execute on [awsdms].[rtm_position_1st_timestamp] to dmsnosysadmin;
    15. 在每个副本上授予对 MSDB 数据库的权限:

      USE msdb GO GRANT select on msdb.dbo.backupset to dmsnosysadmin GRANT select on msdb.dbo.backupmediafamily to dmsnosysadmin GRANT select on msdb.dbo.backupfile to dmsnosysadmin
    16. 在源数据库上将 db_owner 角色添加到 dmsnosysadmin。由于数据库已同步,您只能在主副本上添加角色。

      use <source DB> GO EXEC sp_addrolemember N'db_owner', N'dmsnosysadmin'

SQL Server 支持脚本

以下主题介绍如何下载、查看和运行可用于 SQL Server 的各个支持脚本。您还可以了解如何查看脚本输出并将其上传到您的 Amazon Support 案例中。

awsdms_support_collector_sql_server.sql 脚本

下载 awsdms_support_collector_sql_server.sql 脚本。

注意

仅在 SQL Server 2014 及更高版本上运行此 SQL Server 诊断支持脚本。

此脚本收集有关您的 SQL Server 数据库配置的信息。请记得验证脚本的校验和,如果校验和验证成功,请查看脚本中的 SQL 代码,注释掉任何您不愿意运行的代码。在您确定脚本的完整性和内容没有问题之后,可以运行该脚本。

运行适用于本地 SQL Server 数据库的脚本
  1. 使用以下 sqlcmd 命令行运行脚本。

    sqlcmd -Uon-prem-user -Ppassword -SDMS-SQL17AG-N1 -y 0 -iC:\Users\admin\awsdms_support_collector_sql_server.sql -oC:\Users\admin\DMS_Support_Report_SQLServer.html -dsqlserverdb01

    指定的 sqlcmd 命令参数如下所示:

    • -U – 数据库用户名

    • -P – 数据库用户密码。

    • -S – SQL Server 数据库服务器名称。

    • -y – 从 sqlcmd 实用程序输出的最大列宽度。如果值为 0,则表示列宽度不受限制。

    • -i – 要运行的支持脚本的路径,在本例中为 awsdms_support_collector_sql_server.sql

    • -o – 输出 HTML 文件的路径,文件名由您指定,其中包含收集的数据库配置信息。

    • -d – SQL Server 数据库名称。

  2. 脚本完成后,查看输出的 HTML 文件并删除任何您不愿意分享的信息。当您觉得 HTML 可以分享时,请将该文件上传到您的 Amazon Support 案例中。有关上传此文件的更多信息,请参阅使用 Amazon DMS 中的诊断支持脚本

对于 Amazon RDS for SQL Server,您无法使用 sqlcmd 命令行实用程序进行连接,因此请使用以下过程。

运行适用于 RDS SQL Server 数据库的脚本
  1. 使用任何允许您以 Master 用户身份连接到 RDS SQL Server 的客户端工具运行脚本,并将输出保存为 HTML 文件。

  2. 您可以查看输出 HTML 文件并删除任何不愿意分享的信息。当您觉得 HTML 可以分享时,请将该文件上传到您的 Amazon Support 案例中。有关上传此文件的更多信息,请参阅使用 Amazon DMS 中的诊断支持脚本