All*_*k66 6 sql-server sql-server-2012 availability-groups
我已经为即将推出的新“Intranet”安装并成功配置了我们的 SQL Server 2012 AlwaysOn 2 节点服务器。我已经让 AlwaysOn 运行良好,我们的 Intranet 前端服务器将使用 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)?
请指教,我确定我不是第一个遇到这个问题的人。我在各种 Internet 站点(包括这个站点!)上看过以前的帖子,解决方案要么不正确,要么声明诸如“当然,继续编写脚本!”之类的内容。谢谢,但我需要更多细节。
再次感谢,
-艾伦
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
Run Code Online (Sandbox Code Playgroud)
这里有很多警告。在数据/日志路径在所有副本中一致、未添加错误处理等的情况下,我以非常有限的方式对此进行了测试。如果您决定这样做,您可以从 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
Run Code Online (Sandbox Code Playgroud)
创建存储过程后,您可以通过这种方式调用它并查看消息窗格以查看它是否在运行之前识别了正确的组、数据库和服务器:
EXEC dbo.AddNewDBsToGroup @debug = 1;
Run Code Online (Sandbox Code Playgroud)
当您确信它会做正确的事情时(并且您完全理解“正确的事情”是什么),然后将其更改为:
EXEC dbo.AddNewDBsToGroup @debug = 0;
Run Code Online (Sandbox Code Playgroud)
如果失败,别担心,它会告诉你的。
您不必编写游标 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
Run Code Online (Sandbox Code Playgroud)
由于您现在拥有一个自动化机制,该机制将在创建新数据库时启动,因此您可以使用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
Run Code Online (Sandbox Code Playgroud)
多思考一下这一点,你就可以更有创意地实现自动化——
-- 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'
Run Code Online (Sandbox Code Playgroud)
编辑:下面的脚本将为您提供帮助。显然,您必须理解它并在测试环境中对其进行测试。
/************************************************************************************
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
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
14688 次 |
最近记录: |