跳到主要内容 跳到补充内容

使用 Microsoft SQL Server AlwaysOn 可用性组

Microsoft SQL Server AlwaysOn 可用性组功能是一种高可用性、灾难恢复解决方案,它为数据库镜像提供了企业级的替代方案。

先决条件

  • 用于连接到单个副本的凭据应与用于连接到 AlwaysOn Listener 的凭据相同。
  • 应配置副本以允许连接。

    要对此进行配置,DBA 应运行以下脚本:

    ALTER AVAILABILITY GROUP [{ag_name}]
    MODIFY REPLICA ON '{replica_name}'
    WITH (SECONDARY_ROLE (ALLOW_CONNECTIONS = ALL, READ_ONLY_ROUTING_URL = 
    'TCP://{replica_name}:{Port}'))

配置 数据移动网关 以访问 AlwaysOn Listener

连接属性中的 使用 AlwaysOn 可用性组时,需要指定 AlwaysOn Listener 的 IP 地址/主机名和访问凭据。

限制

不支持 DAG(分布式可用性组)。

访问 AlwaysOn 可用性组中的备份日志

与跨 AlwaysOn Availability Group 同步的活动事务日志不同,备份事务日志对于每个副本都是不同的。因此,当 数据移动网关 需要访问备份事务日志时,它会依次“询问”每个副本是否具有所需的日志,直到找到备份事务日志。请注意,如果数据移动网关 试图访问的副本之一脱机,数据移动网关 将等待该副本恢复联机,然后查询备份事务日志。

数据移动网关在以下情况下, 需要访问每个副本上的备份事务日志(或直到找到所需的日志):

  • 仅在备份模式下工作。

    有关该模式的更多信息,请参阅 Microsoft SQL Server(基于日志)

  • 由于延迟,即如果存在 数据移动网关 无法仅使用活动日志处理的高事件(更改)率。
信息注释

从 AlwaysOn 可用性组读取备份日志需要打开到所有副本的连接。如果不想这样做,请将连接属性中的更改处理模式设置为仅在线日志

使用 AlwaysOn 可用性组时设置非 sysadmin 用户

本部分介绍如何在使用 AlwaysOn 可用性组时支持非 sysadmin 用户。有关在标准环境中设置非系统管理员的信息,请参阅在独立环境中设置非 sysadmin 用户

要在使用 AlwayOn 可用性组时支持非 sysadmin 用户,请执行以下操作:

  1. 按照 为复制设置 Microsoft SQL Server 中所述设置 Microsoft SQL Server for Replication。
  2. 在源数据库上启用 MS-REPLICATION。这可以手动完成,也可以使用sysadmin用户运行一次任务。

    信息注释

    MS-REPLICATION 分发服务器应配置为本地分发服务器,或以允许非 sysadmin 用户通过关联的链接服务器访问的方式配置。

  1. 在每个复制副本上执行以下步骤(a-l):
    1. 在主数据库中创建模式 [attrep]
    2. 在主数据库上创建表值函数 [attrep].[split_partition_list],如下所示:

      USE [master]
      GO
      
      set ansi_nulls on
      go
      
      set quoted_identifier on
      go
      
      if (object_id('[attrep].[split_partition_list]','TF')) is not null
      drop  function [attrep].[split_partition_list];
      go
      
      create function [attrep].[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. 在主数据库上创建过程 [attrep].[rtm_dump_dblog],如下所示:

      USE [master]
      GO
      
      use [MASTER] 
      go 
      
      if (object_id('[attrep].[rtm_dump_dblog]','P')) is not null
      drop procedure [attrep].[rtm_dump_dblog]; 
      go
      
      set ansi_nulls on
      go 
      
      set quoted_identifier on 
      go
      							
      create procedure [attrep].[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
                      --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.attrep.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. 在主数据库上创建证书:

      Use [master]
      Go
      CREATE CERTIFICATE [attrep_rtm_dump_dblog_cert]
      ENCRYPTION BY PASSWORD = N'choose_your_own_pwd'
      WITH SUBJECT = N'Certificate for FN_DUMP_DBLOG Permissions';
    5. 从证书创建登录,如下所示:

      Use [master]
      Go
      CREATE LOGIN attrep_rtm_dump_dblog_login FROM CERTIFICATE
      [attrep_rtm_dump_dblog_cert];
    6. 将登录名添加到 sysadmin 服务器角色,如下所示:

      ALTER SERVER ROLE [sysadmin] ADD MEMBER [attrep_rtm_dump_dblog_login];
    7. 通过证书将签名添加到 [master].[attrep].[rtm_dump_dblog],如下所示:

      Use [master]
      GO
      ADD SIGNATURE
      TO [master].[attrep].[rtm_dump_dblog]
      BY CERTIFICATE [attrep_rtm_dump_dblog_cert]
      					WITH PASSWORD = 'choose_your_own_pwd';
      信息注释

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

    8. 在主数据库上创建过程 [attrep].[rtm_position_1st_timestamp],如下所示:

      use [master]
      if object_id('[attrep].[rtm_position_1st_timestamp]','P') is not null
      DROP PROCEDURE [attrep].[rtm_position_1st_timestamp];
      go
      create procedure [attrep].[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
      
    9. 在主数据库上创建证书:

      Use [master]
      Go
      CREATE CERTIFICATE [attrep_rtm_position_1st_timestamp_cert]
      ENCRYPTION BY PASSWORD = N'choose_your_own_pwd'
      WITH SUBJECT = N'Certificate for FN_POSITION_1st_TIMESTAMP Permissions';
    10. 从证书创建登录,如下所示:

      Use [master]
      Go
      CREATE LOGIN attrep_rtm_position_1st_timestamp_login FROM CERTIFICATE
      [attrep_rtm_position_1st_timestamp_cert];
    11. 将登录名添加到 sysadmin 服务器角色,如下所示:

      ALTER SERVER ROLE [sysadmin] ADD MEMBER [attrep_rtm_position_1st_timestamp_login];
    12. 通过证书将签名添加到 [master].[attrep].[rtm_position_1st_timestamp],如下所示:

      Use [master]
      GO
      ADD SIGNATURE
      TO [master].[attrep].[rtm_position_1st_timestamp]
      BY CERTIFICATE [attrep_rtm_position_1st_timestamp_cert]
      WITH PASSWORD = 'choose_your_own_pwd';
      信息注释

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

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

    信息注释

    应该在每个副本上使用相同的 SID 创建登录。

    • 主数据库(在每个副本上):
      • 在 sys.fn_dblog
      • 视图上选择任何定义
      • 查看服务器状态(应授予登录名)。
      • 在 sp_repldone 上执行
      • 在 sp_replincrementlsn 上执行
      • 在 sp_addpublication 上执行
      • 在 sp_addarticle 上执行
      • 在 sp_articlefilter 上执行
      • 在 [attrep].[split_partition_list] 上选择
      • 在 [attrep].[rtm_dump_dblog] 上执行
      • 在 [attrep].[rtm_position_1st_timestamp] 上执行
    • MSDB DB(在每个副本上):
      • 在 msdb.dbo.backupset 上选择
      • 在 msdb.dbo.backupmediafamily 上选择
      • 在 msdb.dbo.backupfile 上选择
    • 对于源数据库(由于数据库是同步的,因此只能在主副本上进行同步):
      • db_owner ROLE

本页面有帮助吗?

如果您发现此页面或其内容有任何问题 – 打字错误、遗漏步骤或技术错误 – 请告诉我们如何改进!