web-dev-qa-db-ja.com

SQL Server 2012 AlwaysOn:スクリプトによってデータベースを自動的に追加する必要があります-T-SQLまたはPowerShell

SQL Server 2012 AlwaysOn 2ノードサーバーをインストールし、新しい "イントラネット"用に構成しました。私はAlwaysOnをうまく機能させており、イントラネット用のフロントエンドサーバーはSharePoint 2013を使用します。グリッチは、SharePoint 2013がデータベースをSQL Server 2012バックエンドに自動的に追加するように構成されていますが、AlwaysOnにはありません。これについて読んだり、Microsoft MSDNサポートに連絡したりする際のデフォルトの答えは、「手動で検索、選択、バックアップしてから、AlwaysOnにそれらの新しいデータベースを個別に追加する必要がある」です。

ちょっと待って;これはかなりの作業になる可能性があり、SQL Serverバックエンドサーバーを常にチェックして、作成されたデータベースを確認し、それらをAlwaysOn 7/24に追加する必要があります。新しいデータベースをチェックし、それらの新しいデータベースをフルモードでバックアップし(もちろん、AlwaysOnに追加するため)、それらのデータベースをAlwaysOnにすべて自動的に追加するスクリプトまたはプロセスを探しています。または、これを... 1〜2時間ごとに実行しますか? (ユーザーの介入なし)

これまでに思いついたのは、新しく追加されたデータベースを実際に識別し(まだAlwaysOnにはない)、このスクリプトを共有場所にバックアップするスクリプトです。私の次のタスクは、新しく追加されたデータベースを見つけ、必要なさまざまなプロセスを介してそれらをAlwaysOnに追加することです。これには、ある種のループ動作が含まれると思います。私はT-SQL /スクリプト作成の第一人者ではありません。これを実行するためにアクセスする可能性のあるソリューションまたはスクリプトはありますか? (データベースをAlwaysOnに自動的に追加)?

私にこの問題が発生した最初の人ではないことを確信してください。これまでにさまざまなインターネットサイト(これを含む)で投稿を確認しましたが、解決策が間違っているか、「確認して、先に進めて、スクリプトを作成してください!」のようなメッセージが表示されます。ありがとう。でも、もう少し詳細が必要です。

再度、感謝します、

-アレン

DECLARE @name VARCHAR(50) -- database name  
DECLARE @path VARCHAR(256) -- path for backup files  
DECLARE @fileName VARCHAR(256) -- filename for backup  

-- specify database backup directory
SET @path = '\\atel-web-be2\backups\'  

DECLARE db_cursor CURSOR FOR  
select name from sys.databases
where group_database_id is null and replica_id is null 
    and name not in('master','model','msdb','tempdb')

OPEN db_cursor   
FETCH NEXT FROM db_cursor INTO @name   
WHILE @@FETCH_STATUS = 0   
BEGIN   
    SET @fileName = @path + @name + '.BAK'  
    BACKUP DATABASE @name TO DISK = @fileName   
    FETCH NEXT FROM db_cursor INTO @name   
END 

CLOSE db_cursor   
DEALLOCATE db_cursor
6
AllenValk66

作成された新しいデータベースをチェックし、それを実行するようにスケジュールするためにカーソルtsqlスクリプトを記述する必要はありません。毎分。代わりに、EVENTDATA()関数をサーバーレベルのトリガーと組み合わせて使用​​します。

IF EXISTS (SELECT * FROM sys.server_triggers
    WHERE name = 'ddl_trig_database')
DROP TRIGGER ddl_trig_database
ON ALL SERVER;
GO
CREATE TRIGGER ddl_trig_database 
ON ALL SERVER 
FOR CREATE_DATABASE 
AS 
    PRINT 'Database Created.'
    SELECT EVENTDATA().value('(/EVENT_INSTANCE/TSQLCommand/CommandText)[1]','nvarchar(max)')
GO
DROP TRIGGER ddl_trig_database
ON ALL SERVER;
GO

これで、新しいデータベースが作成されたときに起動する自動化メカニズムが準備できたので、 ALTER AVAILABILITY GROUP および ALTER DATABASE-SET HADR を使用できます。

基本的にあなたは単に含める必要があります:

-- Move each database into the Availability Group
-- Change database name and Group as per your environment.
ALTER DATABASE Test1 SET HADR AVAILABILITY GROUP = TestAG
ALTER DATABASE Test2 SET HADR AVAILABILITY GROUP = TestAG
GO

これをもう少し考えると、より創造的に自動化できます-

-- create a driver table
create table AlwaysON_Candidates (
    DatabaseName sysname
    ,createdate datetime default getdate()
    ,IsAlwaysOnMember bit default 0 -- 0 = Not a part of AG
    ) -- 1 = Is part of AG
go
-- below insert will be governed by the server level trigger
insert into AlwaysON_Candidates (DatabaseName)
values ('Test1')
--- check the values in the driver table
select *
from AlwaysON_Candidates
--- add database to AG
alter database Test1
set HADR AVAILABILITY group = TestAG
-- update the bit in the driver table AlwaysON_Candidates
update AlwaysON_Candidates
set IsAlwaysOnMember = 1
where DatabaseName = 'Test1'

tsqlを使用して設定するためのいくつかの優れたリファレンスが見つかります here および here

EDIT:以下のスクリプトが役立ちます。明らかに、それを理解し、テスト環境でテストする必要があります。

/************************************************************************************
Author  : Kin Shah
Version : 1.0.0 for dba.stackexchange.com

Note:   This script does not have ERROR handling and is not tested.
        Use at your own risk, It will print out the sql statements, but wont execute it
        unless the print statements have been modified to use "exec"

        UNDERSTAND the script and then test it on a TEST environment !!!!!!!!

*************************************************************************************/
-- create table 
set ansi_nulls on
go

set quoted_identifier on
go

create table AlwaysON_Candidates (
    ID int identity(1, 1)
    ,EventType nvarchar(128) null
    ,DatabaseName nvarchar(128) null
    ,LoginName nvarchar(128) null
    ,UserName nvarchar(128) null
    ,AuditDateTime datetime null
    ,IsAlwaysOnMember bit default 0
    )
go

alter table [dbo].[AlwaysON_Candidates] add default((0))
for [IsAlwaysOnMember]
go

-- create server trigger
if exists (
        select *
        from master.sys.server_triggers
        where parent_class_desc = 'SERVER'
            and name = N'ddl_trig_database'
        )
    drop trigger [ddl_trig_database] on all server
go

set ansi_nulls on
go

set quoted_identifier on
go

create trigger [ddl_trig_database] on all server
for CREATE_DATABASE as

insert into NewDatabases
select EVENTDATA().value('(/EVENT_INSTANCE/EventType)[1]', 'nvarchar(128)') as EventType
    ,EVENTDATA().value('(/EVENT_INSTANCE/DatabaseName)[1]', 'nvarchar(128)') as DatabaseName
    ,EVENTDATA().value('(/EVENT_INSTANCE/LoginName)[1]', 'nvarchar(128)') as LoginName
    ,EVENTDATA().value('(/EVENT_INSTANCE/UserName)[1]', 'nvarchar(128)') as UserName
    ,GETDATE() as AuditDateTime
    ,0 as IsAlwaysOnMember
go

set ansi_nulls off
go

set quoted_identifier off
go

ENABLE trigger [ddl_trig_database] on all server
go

--- PREREQUISITE ... CREATE A LINKED SERVER FROM PRIMARY TO SECONDARY SERVER !!!
--- fill in *** CHANGE HERE values 
--- test it on a TEST server
--- Not tested and not responsible for any dataloss. UNDERSTAND it and test it before implementing it.
declare @databasename varchar(max)
declare @sqlbackup varchar(max)
declare @sqlrestore varchar(max)
declare @PrimaryAG varchar(max)
declare @SecondaryAG varchar(max)
declare @backupPath varchar(max)

set @backupPath = '\\servername\sharedfolder\' --- *** CHANGE HERE

declare @group sysname

set @group = N'your_group_name' --- *** CHANGE HERE

declare @remotesql1 varchar(max)
declare @remotesql2 varchar(max)
declare @linkedserverName sysname

set @linkedserverName = 'kin_test_AG_LS' --- *** CHANGE HERE

select @databasename = min(DatabaseName)
from AlwaysON_Candidates
where IsAlwaysOnMember = 0

while @databasename is not null
begin
    -- ** BACKUP HAPPENS HERE **
    select @sqlbackup = N'BACKUP DATABASE ' + QUOTENAME(@databasename) + ' TO DISK = ''' + @backupPath + @databasename + '_forAG.BAK'' WITH COPY_ONLY, FORMAT, INIT, COMPRESSION;
    BACKUP LOG ' + QUOTENAME(@databasename) + ' TO DISK = ''' + @backupPath + @databasename + '_forAG.TRN'' WITH INIT, COMPRESSION;'
    from AlwaysON_Candidates
    where IsAlwaysOnMember = 0

    print @sqlbackup --- *** CHANGE HERE for EXEC master..sp_executesql @sqltext

    -- ** RESTORE HAPPENS HERE **
    select @sqlrestore = N'RESTORE DATABASE ' + QUOTENAME(@databasename) + ' FROM DISK = ''' + @backupPath + @databasename + '_forAG.BAK'' WITH REPLACE, NORECOVERY;
    RESTORE LOG ''' + @backupPath + @databasename + '_forAG.TRN'' WITH NORECOVERY;'

    print @sqlrestore

    select @remotesql1 = N'EXEC ' + QUOTENAME(@linkedserverName) + '.master..sp_executesql @sqlrestore;'

    print @remotesql1 --- *** CHANGE HERE for EXEC master..sp_executesql @sqltext

    -- join the AG group on primary
    select @PrimaryAG = N'ALTER AVAILABILITY GROUP ' + QUOTENAME(@group) + ' ADD DATABASE ' + QUOTENAME(@databasename) + ';'

    print @PrimaryAG --- *** CHANGE HERE for EXEC master..sp_executesql @sqltext

    -- join the AG group on secondary
    select @SecondaryAG = 'ALTER DATABASE ' + QUOTENAME(@databasename) + ' SET HADR AVAILABILITY GROUP = ' + QUOTENAME(@group) + ' ;'

    print @SecondaryAG

    select @remotesql2 = N'EXEC ' + QUOTENAME(@linkedserverName) + '.master..sp_executesql @sqlrestore;'

    print @remotesql2 --- *** CHANGE HERE for EXEC master..sp_executesql @SecondaryAG

    -- finally update the table 
    update AlwaysON_Candidates
    set IsAlwaysOnMember = 1
    where DatabaseName = @databasename

    -- go to another database if it is added newly
    select @databasename = min(DatabaseName)
    from AlwaysON_Candidates
    where IsAlwaysOnMember = 0
        and DatabaseName > @databasename
end
4
Kin Shah

ここにはたくさんの警告があります。データ/ログのパスがすべてのレプリカで一致している、エラー処理が追加されていないなどのシナリオで、これを非常に限られた方法でテストしました。このストアドプロシージャは、DDLトリガーから呼び出すことができます。方法、キンが示唆したように、または仕事から、またはあなたは何を持っています。

追加コメントをインラインで参照してください。

CREATE PROCEDURE dbo.AddNewDBsToGroup
  @group SYSNAME = N'your_group_name', -- *** SPECIFY YOUR GROUP NAME HERE ***
  @path  SYSNAME = N'\\atel-web-be2\backups\',
  @debug BIT = 1
AS
BEGIN
  SET NOCOUNT ON;

  DECLARE 
    @sql        NVARCHAR(MAX) = N'',
    @remote_sql NVARCHAR(MAX) = N'';

  DECLARE @t TABLE(db SYSNAME);

  INSERT @t SELECT name FROM sys.databases 
  WHERE replica_id IS NULL AND database_id > 4;

  DECLARE @r TABLE(s NVARCHAR(512));

  -- get the *healthy* replicas available for this group
  -- you'll need error handling to handle cases where any
  -- of the replicas is currently *not* healthy. This 
  -- script does not tell you this happened.

  INSERT @r SELECT r.replica_server_name
  FROM sys.availability_groups AS g
  INNER JOIN sys.dm_hadr_availability_group_states AS s
  ON g.group_id = s.group_id
  INNER JOIN sys.availability_replicas AS r
  ON g.group_id = r.group_id
  AND r.replica_server_name <> @@SERVERNAME
  WHERE g.name = @group
  AND s.primary_replica = @@SERVERNAME
  AND s.primary_recovery_health_desc = 'ONLINE'
  AND s.synchronization_health_desc = 'HEALTHY';

  -- add the database to the group on the primary:

  SELECT @sql += N'ALTER AVAILABILITY GROUP ' 
    + QUOTENAME(@group) + ' ADD DATABASE ' + QUOTENAME(db) + ';'
  FROM @t;

  IF @debug = 1
  BEGIN
    PRINT @sql;
  END
  ELSE
  BEGIN
    EXEC master..sp_executesql @sql;
  END

  -- back up the database locally:
  -- this assumes your database names don't have characters illegal for paths

  SET @sql = N'';

  SELECT @sql += N'BACKUP DATABASE ' + QUOTENAME(db) -- ** BACKUP HAPPENS HERE **
    + ' TO DISK = ''' + @path + db + '.BAK'' WITH COPY_ONLY, FORMAT, INIT, COMPRESSION;
    BACKUP LOG ' + QUOTENAME(db) +
    ' TO DISK = ''' + @path + db + '.TRN'' WITH INIT, COMPRESSION;'
  FROM @t;

  IF @debug = 1
  BEGIN
    PRINT @sql;
  END
  ELSE
  BEGIN
    EXEC master..sp_executesql @sql;
  END

  -- restore the database remotely:
  -- this assumes linked servers match replica names, security works, etc.
  -- it also assumes that each replica has the exact sime data/log paths
  -- (in other words, your restore doesn't need WITH MOVE)

  SET @sql = N'';

  SELECT @sql += N'RESTORE DATABASE ' + QUOTENAME(db) -- ** RESTORE HAPPENS HERE **
    + ' FROM DISK = ''' + @path + db + '.BAK'' WITH REPLACE, NORECOVERY;
    RESTORE LOG ''' + @path + db + '.TRN'' WITH NORECOVERY;
    ALTER DATABASE ' + QUOTENAME(db) + ' SET HADR AVAILABILITY GROUP = '
    + QUOTENAME(@group) + ';'
  FROM @t; 

  SET @remote_sql = N'';

  SELECT @remote_sql += N'EXEC ' + QUOTENAME(s) + '.master..sp_executesql @sql;'
    FROM @r;

  IF @debug = 1
  BEGIN
    PRINT @sql;
    PRINT @remote_sql;
  END
  ELSE
  BEGIN
    EXEC sp_executesql @remote_sql, N'@sql NVARCHAR(MAX)', N'SELECT @@SERVERNAME;';
  END
END
GO

ストアドプロシージャを作成したら、この方法で呼び出し、メッセージペインを見て、実行前に適切なグループ、データベース、サーバーが識別されているかどうかを確認できます。

EXEC dbo.AddNewDBsToGroup @debug = 1;

それが正しいことを行うと確信している場合(そして「正しいこと」が何であるかを完全に理解している場合)、それを次のように変更します。

EXEC dbo.AddNewDBsToGroup @debug = 0;

失敗しても心配しないでください。

8
Aaron Bertrand

これを行うのは非常に簡単な練習です。これは、私がいくつかのクールなことをするために私が家に持っているスクリプトの縮小版です。いくつかのメモ:

  1. データベースが作成されたら、AlwaysOn初期化のためにキューに入れます。暗黙のトランザクションが発生するため、CREATE DATABASEからすぐに実行することはできません。それ以外の場合、発生する必要のあるバックアップはSQLによってブロックされます。

  2. 私のコードでは、作成する「可用性グループ」が自動的に検出されます。これは、CREATE DATABASEを発行したときに接続していたグループです(アプリケーションがAlwaysOnに自己入力できるようにします)。サーバー名に直接接続する場合、AG名を見つけることができないため、自動複製は行われません。

  3. 'RPC'および 'RPC OUT'権限と適切な資格情報を持つ他のサーバー(名前はホスト名と同じである必要があります)を指す可用性グループのすべてのサーバーにリンクサーバーが必要です。これは、ほとんどの場合、混合認証を有効にし、「sa」タイプのアカウントを持つことを意味します。

  4. 'master'からHADR_REPLICATE_QUEUEプロシージャを定期的に呼び出します。 5-10分ごとに言ってください。つまり、すぐにデータベースにHAが存在することはありませんが、まもなく発生します。

  5. すべてのサーバーでバックアップが行われるパスは、グループ内のすべてのサーバー(つまり、UNC共有)にアクセスできる必要があります。インストール時に設定されたデフォルトのインスタンスバックアップパスから自動的にスクレイピングします。

ご覧のとおり、私にはできる限り手を加えないように設計されています。 CREATE LOGINも同様の方法でキャッチし、可能な限りすべてのノードの一貫性を維持します。

スクリプトは次のようになります。

/**
 * AlwaysOn Self-Population Script
 * By: Steve Gray / [email protected]
 * Usage: Free, but buy me a beer if you're ever in Brisbane.
 **/
USE [master]
GO
IF EXISTS (SELECT * FROM sys.tables WHERE name='hadr_pending_replicate')
    BEGIN
        DROP TABLE hadr_pending_replicate;
    END;
GO
CREATE TABLE hadr_pending_replicate (database_name VARCHAR(512) PRIMARY KEY NOT NULL, availability_group_name VARCHAR(2048) NOT NULL);
GO
IF EXISTS (SELECT * FROM sys.server_triggers WHERE name='ddl_hadr_autoreplicate')
    BEGIN
        DROP TRIGGER ddl_hadr_autoreplicate ON ALL SERVER;
    END;
GO
CREATE TRIGGER ddl_hadr_autoreplicate ON ALL SERVER
    FOR CREATE_DATABASE
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @DatabaseName NVARCHAR(2048)

    -- Find the availability group, if the CREATE DATABASE is occuring on an AG listener.
    DECLARE @AddToGroupName         VARCHAR(512);
    SELECT TOP 1
        @AddToGroupName         = [AG].[name]
    FROM
        sys.availability_groups AS [AG]
            INNER JOIN sys.availability_group_listeners AS [LS] ON [LS].[group_id] = [AG].[group_id]
                INNER JOIN sys.availability_group_listener_ip_addresses AS [IP] ON [IP].[listener_id] = [LS].[listener_id]
                    INNER JOIN sys.dm_exec_connections AS [CN] ON [CN].[local_net_address] = [IP].[ip_address] AND [CN].[local_tcp_port] = [LS].[port]
    WHERE
        [CN].[session_id] = @@SPID;

    SET @DatabaseName = (SELECT EVENTDATA().value('(/EVENT_INSTANCE/DatabaseName)[1]', 'nvarchar(128)'));

    -- We have to use a queue since initial backups cant happen during the CREATE DATABASE trigger firing.
    IF @AddToGroupName IS NOT NULL
        BEGIN
            PRINT 'Database is queueing for HADR replication';
            DELETE FROM hadr_pending_replicate WHERE database_name = @DatabaseName;
            INSERT INTO hadr_pending_replicate SELECT @DatabaseName, @AddToGroupName;
        END
END;
GO
IF EXISTS (SELECT * FROM sys.procedures WHERE name='hadr_process_replicate')
    BEGIN
        DROP PROCEDURE hadr_process_replicate;
    END;
GO
/**
 * This script automatically performs a few tasks when a database is created via a connection
 * to a SQL Server availability group listener:
 *
 *     1) Switch the database from 'SIMPLE' to 'FULL' recovery.
 *     2) Perform a full backup to the default backup path for the server.
 *     3) Connect to other servers in the availability group and stage a WITH NO RECOVERY restore.
 *     4) Add the database to the availability group and initialize AlwaysOn.
 *
 * For this to work, you must have linked servers on all your nodes with the same name as the Windows
 * Host name (i.e. SERVERNAME). If in doubt, look at replica_server_name from sys.availability_replicas.
 * Linked servers must have RPC and RPC OUT options set to true. Script assumes that the backup destination 
 * is accessible to every other server too. 
 **/
CREATE PROCEDURE hadr_process_replicate
    @AddToGroupName VARCHAR(2048),
    @DatabaseName VARCHAR(2048)
AS
BEGIN
    DECLARE @AvailabilityGroupID    UNIQUEIDENTIFIER;
    DECLARE @BackupDestination      VARCHAR(2048);
    DECLARE @BackupSuffix           VARCHAR(2048) = '_Initial.bak';

    SET @AvailabilityGroupID = (SELECT group_id FROM sys.availability_groups WHERE name=@AddToGroupName);

    -- Switch the database to FULL recovery if it was created without it.
    IF (SELECT recovery_model FROM sys.databases WHERE name=@DatabaseName) <> 1
        BEGIN
            PRINT 'Changing recovery model to FULL';
            DECLARE @ModeChange NVARCHAR(512) = 'ALTER DATABASE [' + @DatabaseName + ']  SET RECOVERY FULL WITH NO_WAIT';
            EXEC sp_executesql @ModeChange;
        END;
    ELSE
        BEGIN
            PRINT 'Database is already in FULL recovery mode.'
        END;


    -- Read the default backup path from the SQL Server configuration here. This path needs to be accessible to all the servers in
    -- the availability group.
    EXEC master.dbo.xp_instance_regread  N'HKEY_LOCAL_MACHINE',N'Software\Microsoft\MSSQLServer\MSSQLServer',N'BackupDirectory', @BackupDestination OUTPUT, 'no_output';
    DECLARE @TargetFile VARCHAR(2048) = @BackupDestination + '\' + @DatabaseName + @BackupSuffix;

    -- Perform initial backup of the database - Will overwrite any existing file.
    PRINT 'Backing up initial database to ' + @TargetFile;
    DECLARE @BackupCommand NVARCHAR(2048) = 'BACKUP DATABASE [' + @DatabaseName + '] TO  DISK = N''' + @TargetFile + ''' WITH INIT, NOFORMAT, NAME = N''Initial backup for HADR seeding'', SKIP, NOREWIND, NOUNLOAD,  STATS = 100';
    PRINT '    Command: ' + @BackupCommand
    EXEC (@BackupCommand)

    PRINT 'Joining database to availability group'
    DECLARE @JoinToAG NVARCHAR(2048) = 'ALTER AVAILABILITY GROUP [' + @AddToGroupName + '] ADD DATABASE [' + @DatabaseName + ']';
    EXEC sp_executesql @JoinToAG;

    -- Loop through all availability replicas
    DECLARE @Replicas TABLE (ReplicaName VARCHAR(512))
    INSERT INTO @Replicas   -- Have to use a table, since T-SQL wasnt giving me 
                            -- all replicas when I did this straight via the cursor.... (Bug?)
        SELECT replica_server_name 
            FROM 
                sys.availability_replicas 
            WHERE group_id=CAST(@AvailabilityGroupID AS VARCHAR(512));

    DECLARE cur_Replicas CURSOR FOR SELECT ReplicaName FROM @Replicas INNER JOIN sys.servers [SV] ON [SV].[name] = [ReplicaName] AND [SV].[is_linked] = 1 ORDER BY [ReplicaName];
    OPEN cur_Replicas;
    DECLARE @CurrentReplica VARCHAR(255)
    FETCH NEXT FROM cur_Replicas INTO @CurrentReplica;
    WHILE @@FETCH_STATUS >= 0
        BEGIN
            PRINT 'Restoring initial backup to ' + @CurrentReplica;
            DECLARE @RestoreCommand VARCHAR(2048) = 'RESTORE DATABASE [' + @DatabaseName + '] FROM  DISK = N''' + @TargetFile + ''' WITH  NORECOVERY,  NOUNLOAD,  REPLACE,  STATS = 5;'
            -- The 'inception' moment.
            DECLARE @DoubleDynamicRestore NVARCHAR(2048) = 'EXEC (''' +  REPLACE(@RestoreCommand, '''', '''''') + ''') AT [' + @CurrentReplica + ']';
            PRINT @DoubleDynamicRestore
            EXEC sp_executesql @DoubleDynamicRestore;

            PRINT 'Bringing replica online'     
            DECLARE @DynamicAddHADR NVARCHAR(2048) = 'EXEC (''ALTER DATABASE [' + @DatabaseName + '] SET HADR AVAILABILITY GROUP = [' + @AddToGroupName +'];'') AT [' + @CurrentReplica +']';
            EXEC sp_executesql @DynamicAddHADR

            FETCH NEXT FROM cur_Replicas INTO @CurrentReplica;
        END;
    CLOSE cur_Replicas;
    DEALLOCATE cur_Replicas;
END;
GO
IF EXISTS (SELECT * FROM sys.procedures WHERE name='hadr_replicate_queue')
    BEGIN
        DROP PROCEDURE hadr_replicate_queue;
    END;
GO
/** 
 * Process all pending HADR replicates.
 **/
CREATE PROCEDURE hadr_replicate_queue
AS
BEGIN
    DECLARE cur_ReplicationTask CURSOR FOR
        SELECT database_name, availability_group_name FROM hadr_pending_replicate WITH(HOLDLOCK)
    OPEN cur_ReplicationTask;

    DECLARE @DB VARCHAR(512), @AG VARCHAR(2048)
    FETCH NEXT FROM cur_ReplicationTask INTO @DB, @AG
    WHILE @@FETCH_STATUS = 0
        BEGIN
            EXEC hadr_process_replicate @AddToGroupName = @AG, @DatabaseName = @DB
            FETCH NEXT FROM cur_ReplicationTask INTO @DB, @AG
        END;

    DELETE FROM hadr_pending_replicate;

    CLOSE cur_ReplicationTask;
    DEALLOCATE cur_ReplicationTask
END;
GO

プロセスの中核は次のとおりです。

  1. データベースを完全復旧に切り替えます。
  2. 既知の場所にバックアップします。
  3. それを可用性グループ定義に追加します。
  4. それをすべてのサーバーに復元し、グループに「参加」させます。

質問があればコメントしてください。

1
Steve Gray

私は、MSDNで作業してスクリプトを変更することで、答えを見つけました。

xp_cmdshell 'del /F /Q /S \\atel-vm-test\backup\*.*'

--drop table #dbs
--deallocate adddbs
IF OBJECT_ID('dbo.#dbs', 'U') IS NOT NULL
  DROP TABLE dbo.#dbs

--IF CURSOR_STATUS('global','adddbs')>=-1
--BEGIN
-- DEALLOCATE adddbs
--END

IF (SELECT CURSOR_STATUS('global','adddbs')) >=0 
BEGIN
  DEALLOCATE adddbs
END

create table #dbs(a int primary key identity, dbname varchar(100))

declare @nextdb varchar(100)
declare @restorestring varchar(200)

--Populate temp table
insert into #dbs(dbname) 
--select name from sys.databases where create_date between getdate()-1 and getdate()

select name from sys.databases
where group_database_id is null and replica_id is null 
    and name not in('master','model','msdb','tempdb')

--Create a cursor to 
declare adddbs cursor for
select dbname from #dbs

open adddbs

FETCH NEXT FROM adddbs 
INTO @nextdb

WHILE @@FETCH_STATUS = 0
BEGIN
  EXEC ('BACKUP DATABASE ' + @nextdb + ' TO  DISK = ' + '''\\atel-vm-test\backup\' + @nextdb + 'initialize.bak''')

  EXEC ('ALTER AVAILABILITY GROUP [atel-testAG] ADD DATABASE ' + @nextdb)


  EXEC ('BACKUP DATABASE ' + @nextdb + ' TO  DISK = ' + '''\\atel-vm-test\backup\' + @nextdb + '.bak''')

  EXEC ('BACKUP LOG ' + @nextdb + ' TO  DISK = ' + '''\\atel-vm-test\backup\' + @nextdb + '_log.bak''')

  set @restorestring='sqlcmd -S atel-vm-test2 -E -Q"RESTORE DATABASE ' + @nextdb + ' FROM  DISK = ' + '''\\atel-vm-test\backup\' + @nextdb + '.bak''' + ' WITH  NORECOVERY,  NOUNLOAD,  STATS = 5"'
  exec xp_cmdshell @restorestring
  print 'sqlcmd -S [atel-vm-test2] -E -Q"RESTORE DATABASE ' + 'agd2' + ' FROM  DISK = ' + '''\\atel-vm-test\backup\' + 'agd2' + '.bak''' + ' WITH  NORECOVERY,  NOUNLOAD,  STATS = 5"'
  set @restorestring='sqlcmd -S atel-vm-test2 -E -Q"RESTORE LOG ' + @nextdb + ' FROM  DISK = ' + '''\\atel-vm-test\backup\' + @nextdb + '_log.bak''' + ' WITH  NORECOVERY,  NOUNLOAD,  STATS = 5"'
  exec xp_cmdshell @restorestring

  set @restorestring='sqlcmd -S atel-vm-test2 -E -Q"ALTER DATABASE ' + @nextdb + ' SET HADR AVAILABILITY GROUP = [atel-testag]"'
  exec xp_cmdshell @restorestring

  FETCH NEXT FROM adddbs 
  INTO @nextdb
END

CLOSE adddbs
DEALLOCATE adddbs

--end

次のステップ:

  • atel-testAGを独自の可用性グループ名に置き換えます。
  • \\atel-vm-test\backupをサーバーの共有バックアップ場所に置き換えます。
  • \\atel-vm-test2をセカンダリサーバーの名前に置き換えます。

このジョブは、プライマリサーバーから実行されるようにスケジュールされています。 (変数「プライマリサーバー」から実行するように設定していません。今のところ、atel-vm-testというプライマリサーバーで実行しています。

SQLサーバーで実行するためのxp_cmdshell関数権限があることを確認してください。

エラーなしで実行できるようになり、1時間ごとに実行されるようにスケジュールされたSQLジョブになりました。期待どおりに動作します。援助をありがとう。

0
AllenValk66

以下は、20分ごとにSQL Serverエージェントジョブとして実行する必要がある、完成した現在動作しているスクリプトです。そうなる:

  • プライマリサーバーとセカンダリサーバーの名前を自動的に決定します。
  • 可用性グループ名を自動的に決定します。
  • 追加するデータベースをシンプルモードからフルモードに変換します。 (データベースがAlwaysOnに正常に追加されるために必要です)。

単一のSQLインスタンスを持つSQLサーバーまたは2番目のSQLインスタンスで作業します。 2番目のSQLインスタンスがこのスクリプトを使用している場合は、バックアップパスの2番目の名前を共有する必要があります。

このスクリプトがSSMSコンソールで手動で実行されている場合は、最初に "DROP TABLE dbo。#dbs"コマンドを実行する必要があります(このスクリプトが以前に実行されていた場合)。何らかの理由で、このコマンドはSSMSでこのスクリプトを手動で実行しても読み取られませんが、SQL Serverエージェントジョブとして実行すると機能します。 (????)

\ backupsの場所は、このスクリプトでハードコーディングされています。必要に応じてこれを変更してください。また、SQLサーバーのSSMSコンソールでコマンド機能「xp_cmdshell」を有効にする必要があります。調べるためにそれをグーグル;それは簡単なコマンド呼び出しです。これは、1つのプライマリと1つのセカンダリを備えた2-SQLサーバー構成で実行するように設計されています。

xp_cmdshell 'del /F /Q /S \\atel-web\be1\backups\*.*'

exec xp_cmdshell 'del /F /Q /S \\atel-web\be2\backups\*.*'

USE master

DECLARE @secondaryservername nvarchar(50)

DECLARE @primaryservername nvarchar(50)

DECLARE @availabilitygroup nvarchar(50) 

SET @secondaryservername = (select replica_server_name AS Servername from sys.dm_hadr_availability_replica_states  
 , sys.dm_hadr_availability_replica_cluster_states
where role_desc = 'SECONDARY' AND sys.dm_hadr_availability_replica_states.replica_id = 
sys.dm_hadr_availability_replica_cluster_states.replica_id)

SET @primaryservername = (select replica_server_name AS Servername from sys.dm_hadr_availability_replica_states  
 , sys.dm_hadr_availability_replica_cluster_states
where role_desc = 'PRIMARY' AND sys.dm_hadr_availability_replica_states.replica_id = 
sys.dm_hadr_availability_replica_cluster_states.replica_id)


SET @availabilitygroup = (SELECT name FROM [sys].[availability_groups])


IF OBJECT_ID('dbo.#dbs', 'U') IS NOT NULL
  DROP TABLE dbo.#dbs

IF (SELECT CURSOR_STATUS('global','adddbs')) >=0 
BEGIN
DEALLOCATE adddbs
END

create table #dbs(a int primary key identity, dbname varchar(100))
declare @nextdb varchar(100)
declare @restorestring varchar(400)

--Populate temp table
insert into #dbs(dbname) 

select name from sys.databases
where group_database_id is null and replica_id is null 
    and name not in('master','model','msdb','tempdb')

--Create a cursor to 
declare adddbs cursor for
select dbname from #dbs

open adddbs

FETCH NEXT FROM adddbs 
INTO @nextdb

WHILE @@FETCH_STATUS = 0
BEGIN
EXEC ('ALTER DATABASE' + '[' + @nextdb + ']' + 'set RECOVERY FULL')
EXEC ('BACKUP DATABASE ' + '[' + @nextdb + ']' + ' TO  DISK = ' + '''\\' +@primaryservername+'\backups\' + '[' + @nextdb + ']' + 'initialize.bak''')

EXEC ('ALTER AVAILABILITY GROUP ['+ @availabilitygroup +'] ADD DATABASE ' + '[' + @nextdb + ']')


EXEC ('BACKUP DATABASE ' + '[' + @nextdb + ']' + ' TO  DISK = ' + '''\\'@primaryservername+'\backups\' + '[' + @nextdb + ']' + '.bak''')

EXEC ('BACKUP LOG ' + '[' + @nextdb + ']' + ' TO  DISK = ' + '''\\'@primaryservername+'\backups\' + '[' + @nextdb + ']' + '_log.bak''')

set @restorestring='sqlcmd -S ' +@secondaryservername+' -E -Q"RESTORE DATABASE ' + '[' + @nextdb + ']' + ' FROM  DISK = ' + '''\\' +@primaryservername +'\backups\' + '[' + @nextdb + ']' + '.bak''' + ' WITH  NORECOVERY,  NOUNLOAD,  STATS = 5"'
exec xp_cmdshell @restorestring

set @restorestring='sqlcmd -S ' +@secondaryservername+' -E -Q"RESTORE LOG ' + '[' + @nextdb + ']' + ' FROM  DISK = ' + '''\\' +@primaryservername+'\backups\' + '[' + @nextdb + ']' + '_log.bak''' + ' WITH  NORECOVERY,  NOUNLOAD,  STATS = 5"'
exec xp_cmdshell @restorestring

set @restorestring='sqlcmd -S ' +@secondaryservername+' -E -Q"ALTER DATABASE ' + '[' + @nextdb + ']' + ' SET HADR AVAILABILITY GROUP = [' + @availabilitygroup +']"'
exec xp_cmdshell @restorestring

FETCH NEXT FROM adddbs 
    INTO @nextdb
END

CLOSE adddbs
DEALLOCATE adddbs
0
AllenValk66