noj*_*lag 43 sql-server sql-server-2012 sql-server-agent availability-groups
我正在寻找处理 SQL Server 2012 可用性组中计划的 SQL Server 代理作业的最佳实践。也许我错过了一些东西,但是在目前的状态下,我觉得 SQL Server 代理并没有真正与 SQL2012 的这个强大功能集成在一起。
如何让计划的 SQL 代理作业知道节点切换?例如,我有一个在主节点上运行的作业,它每小时加载数据。现在,如果主要服务器出现故障,我如何激活现在成为主要服务器的辅助服务器上的作业?
如果我总是在辅助上安排作业,它会失败,因为辅助是只读的。
Tho*_*ger 44
在您的 SQL Server 代理作业中,使用一些条件逻辑来测试当前实例是否正在为您在可用性组中寻找的特定角色提供服务:
if (select
ars.role_desc
from sys.dm_hadr_availability_replica_states ars
inner join sys.availability_groups ag
on ars.group_id = ag.group_id
where ag.name = 'YourAvailabilityGroupName'
and ars.is_local = 1) = 'PRIMARY'
begin
-- this server is the primary replica, do something here
end
else
begin
-- this server is not the primary replica, (optional) do something here
end
Run Code Online (Sandbox Code Playgroud)
所有这一切都是拉取本地副本的当前角色,如果它在PRIMARY角色中,那么如果它是主副本,您可以做任何您的工作需要做的事情。该ELSE块是可选的,但如果您的本地副本不是主要副本,它会处理可能的逻辑。
当然,'YourAvailabilityGroupName'将上述查询更改为您的实际可用性组名称。
不要将可用性组与故障转移群集实例混淆。实例是给定可用性组的主要副本还是次要副本不会影响服务器级对象,例如 SQL Server 代理作业等。
Tru*_*ubs 15
我没有在每个作业的基础上执行此操作(在决定继续之前检查每个作业的服务器状态),而是创建了一个在两台服务器上运行的作业来检查服务器处于什么状态。
这种方法提供了很多东西
这个过程在每台服务器上每 15 分钟执行一次。(附加评论以告知人们为什么该工作被禁用的额外好处)
/*
This proc goes through all SQL Server agent jobs and finds any that refer to a database taking part in the availability Group
It will then enable/disable the job dependant on whether the server is the primary replica or not
Primary Replica = enable job
It will also add a comment to the job indicating the job was updated by this proc
*/
CREATE PROCEDURE dbo.sp_HADRAgentJobFailover (@AGname varchar(200) = 'AG01' )
AS
DECLARE @SQL NVARCHAR(MAX)
;WITH DBinAG AS ( -- This finds all databases in the AG and determines whether Jobs targeting these DB's should be turned on (which is the same for all db's in the AG)
SELECT distinct
runJobs = CASE WHEN role_desc = 'Primary' THEN 1 ELSE 0 END --If this is the primary, then yes we want to run the jobs
,dbname = db.name
,JobDescription = CASE WHEN hars.role_desc = 'Primary' -- Add the reason for the changing the state to the Jobs description
THEN '~~~ [Enabled] using automated process (DBA_tools.dbo.sp_HADRAgentJobFailover) looking for jobs running against Primary Replica AG ~~~ '
ELSE '~~~ [Diabled] using Automated process (DBA_tools.dbo.sp_HADRAgentJobFailover) because the job cant run on READ-ONLY Replica AG~~~ ' END
FROM sys.dm_hadr_availability_replica_states hars
INNER JOIN sys.availability_groups ag ON ag.group_id = hars.group_id
INNER JOIN sys.Databases db ON db.replica_id = hars.replica_id
WHERE is_local = 1
AND ag.Name = @AGname
)
SELECT @SQL = (
SELECT DISTINCT N'exec msdb..sp_update_job @job_name = ''' + j.name + ''', @enabled = ' + CAST(d.runJobs AS VARCHAR)
+ ',@description = '''
+ CASE WHEN j.description = 'No description available.' THEN JobDescription -- if there is no description just add our JobDescription
WHEN PATINDEX('%~~~%~~~',j.description) = 0 THEN j.description + ' ' + JobDescription -- If our JobDescription is NOT there, add it
WHEN PATINDEX('%~~~%~~~',j.description) > 0 THEN SUBSTRING(j.description,1,CHARINDEX('~~~',j.description)-1) + d.JobDescription --Replace our part of the job description with what we are doing.
ELSE d.JobDescription -- Should never reach here...
END
+ ''';'
FROM msdb.dbo.sysjobs j
INNER JOIN msdb.dbo.sysjobsteps s
INNER JOIN DBinAG d ON d.DbName =s.database_name
ON j.job_id = s.job_id
WHERE j.enabled != d.runJobs -- Ensure we only actually update the job, if it needs to change
FOR XML PATH ('')
)
PRINT REPLACE(@SQL,';',CHAR(10))
EXEC sys.sp_executesql @SQL
Run Code Online (Sandbox Code Playgroud)
它不是万无一失的,但对于夜间负载和每小时工作,它可以完成工作。
甚至比按计划运行此过程更好,而是运行它以响应警报 1480(AG 角色更改警报)。
我知道有两个概念可以实现这一点。
先决条件:根据 Thomas Stringer 的回答,我在两台服务器的主数据库中创建了两个函数:
CREATE FUNCTION [dbo].[svf_AgReplicaState](@availability_group_name sysname)
RETURNS bit
AS
BEGIN
if EXISTS(
SELECT ag.name
FROM sys.dm_hadr_availability_replica_states AS ars INNER JOIN
sys.availability_groups AS ag ON ars.group_id = ag.group_id
WHERE (ars.is_local = 1) AND (ars.role_desc = 'PRIMARY') AND (ag.name = @availability_group_name))
RETURN 1
RETURN 0
END
GO
CREATE FUNCTION [dbo].[svf_DbReplicaState](@database_name sysname)
RETURNS bit
AS
BEGIN
IF EXISTS(
SELECT adc.database_name
FROM sys.dm_hadr_availability_replica_states AS ars INNER JOIN
sys.availability_databases_cluster AS adc ON ars.group_id = adc.group_id
WHERE (ars.is_local = 1) AND (ars.role_desc = 'PRIMARY') AND (adc.database_name = @database_name))
RETURN 1
RETURN 0
END
GO
Run Code Online (Sandbox Code Playgroud)
如果作业不在主副本上执行,则终止作业
对于这种情况,两台服务器上的每个作业都需要以下两个代码片段之一作为步骤 1:
按组名检查:
IF master.dbo.svf_AgReplicaState('my_group_name')=0
raiserror ('This is not the primary replica.',2,1)
Run Code Online (Sandbox Code Playgroud)
按数据库名称检查:
IF master.dbo.svf_AgReplicaState('my_db_name')=0
raiserror ('This is not the primary replica.',2,1)
Run Code Online (Sandbox Code Playgroud)
如果您使用第二个,请注意系统数据库 - 根据定义,它们不能成为任何可用性组的一部分,因此它总是会失败。
对于管理员用户来说,这两种方法都是开箱即用的。对于非管理员用户,您必须添加额外的权限,此处建议其中之一:
GRANT VIEW SERVER STATE TO [user];
GRANT VIEW ANY DEFINITION TO [user];
Run Code Online (Sandbox Code Playgroud)
如果您在第一步中将失败操作设置为退出作业报告成功,您将不会得到充满丑陋红十字标志的作业日志,因为主要作业它们会变成黄色警告标志。
根据我们的经验,这并不理想。我们一开始采用了这种方法,但很快就无法找到实际有问题的作业,因为所有辅助副本作业都用警告消息使作业日志混乱。
然后我们要做的是:
代理工作
如果您采用此概念,您实际上需要为要执行的每个任务创建两个作业。第一个是“代理作业”,用于检查它是否正在主副本上执行。如果是这样,它会启动“工人作业”,如果不是,它只是优雅地结束,而不会用警告或错误消息使日志混乱。
虽然我个人不喜欢在每台服务器上每个任务有两个作业的想法,但我认为它绝对更易于维护,并且您不必将步骤的失败操作设置为Quit job Reporting success,这有点尴尬的。
对于作业,我们采用了命名方案。代理作业只是被调用{put jobname here}。工人作业称为{put jobname here} worker。这使得从代理自动启动工作任务成为可能。为此,我向两个主数据库添加了以下过程:
CREATE procedure [dbo].[procStartWorkerJob](@jobId uniqueidentifier, @availabilityGroup sysname, @postfix sysname = ' worker') as
declare @name sysname
if dbo.svf_AgReplicaState(@availabilityGroup)=0
print 'This is not the primary replica.'
else begin
SELECT @name = name FROM msdb.dbo.sysjobs where job_id = @jobId
set @name = @name + @postfix
if exists(select name from msdb.dbo.sysjobs where name = @name)
exec msdb.dbo.sp_start_job @name
else begin
set @name = 'Job '''+@name+''' not found.'
raiserror (@name ,2,1)
end
end
GO
Run Code Online (Sandbox Code Playgroud)
这利用了svf_AgReplicaState上面显示的函数,您可以轻松地将其更改为使用数据库名称进行检查,而不是调用其他函数。
在代理作业的唯一步骤中,您可以这样称呼它:
exec procStartWorkerJob $(ESCAPE_NONE(JOBID)), '{my_group_name}'
Run Code Online (Sandbox Code Playgroud)
这利用此处和此处所示的令牌来获取当前作业的 ID。然后该过程从 msdb 获取当前作业名称,附加 worker到它并使用sp_start_job.
虽然这仍然不理想,但它使作业日志比以前的选项更整洁、更易于维护。此外,您始终可以让 sysadmin 用户运行代理作业,因此无需添加任何额外权限。
| 归档时间: |
|
| 查看次数: |
42415 次 |
| 最近记录: |