SQL Server 에이전트 작업 및 가용성 그룹


37

SQL Server 2012 가용성 그룹에서 예약 된 SQL Server 에이전트 작업을 처리하는 모범 사례를 찾고 있습니다. 어쩌면 뭔가를 놓쳤을 수도 있지만 현재 상태에서는 SQL Server 에이전트 가이 위대한 SQL2012 기능과 실제로 통합되지 않은 것 같습니다.

예약 된 SQL 에이전트 작업이 노드 스위치를 인식하도록하려면 어떻게해야합니까? 예를 들어 매 시간마다 데이터를로드하는 기본 노드에서 작업을 실행하고 있습니다. 이제 1 차가 중단되면 2 차에서 작업이 활성화되는 작업을 어떻게 활성화 할 수 있습니까?

보조에서 항상 작업을 예약하면 보조는 읽기 전용이므로 실패합니다.


답변:


40

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

이 모든 작업은 로컬 복제본의 현재 역할을 가져 오는 것입니다. PRIMARY역할에있는 경우 기본 복제 본인 경우 작업에서 수행해야하는 모든 작업을 수행 할 수 있습니다. ELSE블록은 선택 사항이지만 해당 지역의 복제가 차없는 경우는 가능한 로직을 처리 할 수 있습니다.

물론 'YourAvailabilityGroupName'위 쿼리에서 실제 가용성 그룹 이름으로 변경하십시오.

가용성 그룹을 장애 조치 클러스터 인스턴스와 혼동하지 마십시오. 인스턴스가 지정된 가용성 그룹의 기본 복제본인지 보조 복제본인지는 SQL Server 에이전트 작업 등과 같은 서버 수준 개체에 영향을 미치지 않습니다.


14

작업 단위로이 작업을 수행하는 대신 (계속하기로 결정하기 전에 서버 상태에 대한 모든 작업 확인) 두 서버에서 실행중인 작업을 만들어 서버의 상태를 확인했습니다.

  • 기본 인 경우 AG에서 데이터베이스를 대상으로하는 단계가있는 작업을 사용하십시오.
  • 서버가 보조 서버 인 경우 AG에서 데이터베이스를 대상으로하는 작업을 비활성화하십시오.

이 방법은 여러 가지를 제공합니다

  • AG에 데이터베이스가없는 서버 (또는 AG의 입 / 출력 혼합 DB)에서 작동합니다.
  • 누구나 새 작업을 생성 할 수 있으며 DB가 AG에 있는지에 대해 걱정할 필요가 없습니다 (작업을 다른 서버에 추가해야한다는 것을 기억해야 함).
  • 각 작업에 유용한 오류 이메일이있을 수 있습니다 (모든 작업에 실패 이메일이 있습니까?)
  • 작업 기록을 볼 때 실제로 작업을 수행하지 않은 긴 성공 목록 (보조에서)을 보지 않고 실제로 작업이 실제로 실행되고 무언가를 수행했는지 (이것이 기본) 여부를 확인할 수 있습니다.

스크립트는 아래 필드에서 데이터베이스를 확인합니다 이 데이터베이스가 가용성 그룹에 있으면 스크립트가 조치를 수행합니다.

이 proc은 각 서버에서 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

완벽한 증거는 아니지만 야간로드 및 시간별 작업의 경우 작업이 완료됩니다.

이 절차를 일정대로 실행하는 것보다 경고 1480 (AG 역할 변경 경고)에 대한 응답으로 실행하는 것이 좋습니다.


9

나는 이것을 달성하기위한 두 가지 개념을 알고 있습니다.

전제 조건 : Thomas Stringer의 답변을 바탕으로 두 서버의 master db에 두 개의 함수를 작성했습니다.

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


  1. 기본 복제본에서 작업이 실행되지 않으면 작업을 종료하십시오.

    이 경우 두 서버의 모든 작업에는 1 단계와 같이 다음 두 코드 스 니펫 중 하나가 필요합니다.

    그룹 이름으로 확인 :

    IF master.dbo.svf_AgReplicaState('my_group_name')=0
      raiserror ('This is not the primary replica.',2,1)

    데이터베이스 이름으로 확인하십시오.

    IF master.dbo.svf_AgReplicaState('my_db_name')=0
      raiserror ('This is not the primary replica.',2,1)

    이 두 번째 데이터베이스를 사용하는 경우 시스템 데이터베이스를주의하십시오. 정의에 따라 가용성 그룹의 일부가 될 수 없으므로 항상 데이터베이스 데이터베이스에 실패합니다.

    이 두 가지 모두 관리자를 위해 즉시 사용 가능합니다. 관리자가 아닌 사용자의 경우 추가 권한을 추가해야하며 그 중 하나는 다음과 같이 제안 됩니다 .

    GRANT VIEW SERVER STATE TO [user];
    GRANT VIEW ANY DEFINITION TO [user];

    이 첫 번째 단계 에서 실패 조치를 작업보고 성공 종료로 설정하면 주 작업에 대한 못생긴 빨간색 십자 표시로 작업 로그가 표시되지 않으며 대신 주 경고가 노란색 경고 표시로 바뀝니다.

    우리의 경험에서 볼 때 이것은 이상적이지 않습니다. 처음에는이 접근 방식을 채택했지만 모든 보조 복제본 작업이 작업 메시지를 경고 메시지로 복잡하게 만들었 기 때문에 실제로 문제가있는 작업을 찾는 것과 관련하여 신속하게 추적을 잃었습니다.

    우리가 간 것은 :

  2. 프록시 작업

    이 개념을 채택하면 실제로 수행하려는 작업마다 두 개의 작업을 작성해야합니다. 첫 번째는 기본 프록시에서 실행 중인지 확인하는 "프록시 작업"입니다. 그렇다면 "작업자 작업"을 시작합니다. 그렇지 않은 경우 경고 또는 오류 메시지로 로그를 어지럽히 지 않고 정상적으로 종료됩니다.

    나는 개인적으로 모든 서버에 작업 당 두 개의 작업을 데의 생각처럼하지 않지만, 나는 그것이의 definetly 더 유지 보수가 생각, 당신은 할 수있는 단계의 실패 작용 설정할 필요가 없습니다 작업을보고 성공을 종료 조금이다, 어색한.

    작업을 위해 이름 지정 체계를 채택했습니다. 프록시 작업을 방금 호출했습니다 {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

    이것은 svf_AgReplicaState위에 표시된 함수를 사용하므로 다른 함수를 호출하는 대신 데이터베이스 이름을 사용하여 확인하도록 쉽게 변경할 수 있습니다.

    프록시 작업의 유일한 단계에서 다음과 같이 호출합니다.

    exec procStartWorkerJob $(ESCAPE_NONE(JOBID)), '{my_group_name}'

    같이이 토큰을 이용하여 여기여기에 현재 작업의 ID를 얻을 수 있습니다. 그런 다음 프로시 저는 msdb에서 현재 작업 이름을 가져와 추가  worker하고를 사용하여 작업자 작업을 시작합니다 sp_start_job.

    여전히 이상적이지는 않지만 작업 로그를 이전 옵션보다 더 깔끔하고 유지 관리 할 수 ​​있습니다. 또한 항상 sysadmin 사용자로 프록시 작업을 실행할 수 있으므로 추가 권한을 추가 할 필요가 없습니다.


3

데이터로드 프로세스가 단순한 쿼리 또는 프로 시저 호출 인 경우 두 노드에서 작업을 작성하고 데이터로드 프로세스를 실행하기 전에 데이터베이스의 업데이트 가능성 특성을 기반으로 기본 노드인지 판별 할 수 있습니다.

IF (SELECT CONVERT(sysname,DatabasePropertyEx(DB_NAME(),'Updateability'))) != 'READ_ONLY'
BEGIN

-- Data Load code goes under here

END

1

기본 복제본인지 확인하는 새 작업 단계를 작성하는 것이 좋습니다. 보조 복제 본인 경우 작업 실행을 계속하고 작업을 중지하는 것이 좋습니다. 그렇지 않으면 작업이 실패하지 않아도 불필요한 알림이 계속 전송됩니다. 대신 작업을 중지하여 작업이 취소되고이 작업이 보조 복제본에서 실행될 때마다 알림이 전송되지 않습니다.

다음은 특정 작업에 대한 첫 단계를 추가하는 스크립트입니다.

스크립트를 실행하십시오.

  • 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'를 Job_ID로 바꾸십시오.
  • 'YYYYYYYYYYYYYYYYYYYYYYYYYYYY'를 Job_Name으로 바꾸십시오.
  • 가용성 그룹이 여러 개인 경우 @AGNameToCheck_IfMoreThanSingleAG 변수에 AG 이름을 설정하여 복제본 상태를 확인할 AG를 지정하십시오.

  • 또한이 스크립트는 가용성 그룹이없는 서버에서도 제대로 작동합니다. SQL Server 버전 2012 이상에서만 실행됩니다.

            USE [msdb]
            GO
            EXEC msdb.dbo.sp_add_jobstep @job_id=N'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', @step_name=N'CheckForSecondaryReplica', 
                    @step_id=1, 
                    @cmdexec_success_code=0, 
                    @on_success_action=3, 
                    @on_fail_action=2, 
                    @retry_attempts=0, 
                    @retry_interval=0, 
                    @os_run_priority=0, @subsystem=N'TSQL', 
                    @command=N'
            DECLARE @AGNameToCheck_IfMoreThanSingleAG VARCHAR(100)
            SET @AGNameToCheck_IfMoreThanSingleAG = ''AGName_IfMoreThanOneAG'' -- If there are Multiple AGs, then a single server can have Primary of one AG and Secondary of other. So Job creator has to define as to which AG needs to verified before the job is automatically run on Primary.
    
            DECLARE @NumberofAGs INT
            SELECT @NumberofAGs = COUNT(group_id) FROM sys.availability_groups ags
    
    
            IF(@NumberofAGs < 2)
                IF EXISTS(Select * FROM sys.dm_hadr_availability_replica_states hars WHERE role_desc = ''Secondary'' AND hars.is_local = 1)                 
                                    EXEC msdb.dbo.sp_stop_job N''YYYYYYYYYYYYYYYYYYYYYYYYYY'' ;
                                    --RAISERROR(''This is a Secondary Replica'',16,1)
    
            IF(@NumberofAGs >= 2)
                IF EXISTS(SELECT 1 FROM sys.availability_groups WHERE name = @AGNameToCheck_IfMoreThanSingleAG)
                BEGIN
                            IF EXISTS(Select * from  sys.availability_groups ag
                                            JOIN sys.dm_hadr_availability_replica_states hars
                                                        ON ag.group_id = hars.group_id
                                                        Where role_desc = ''Secondary''
                                                        AND hars.is_local = 1
                                                        AND ag.name = @AGNameToCheck_IfMoreThanSingleAG)
                            BEGIN
                                    EXEC msdb.dbo.sp_stop_job N''YYYYYYYYYYYYYYYYYYYYYYYYYY'' ;
                                    --RAISERROR(''This is a Secondary Replica'',16,1)
                            END
                END
                ELSE
                            BEGIN
                                    RAISERROR(''The Defined AG in the Variable is not a part of this Server. Please Check!!!!!!!!!!!'',16,1)
                            END', 
                    @database_name=N'master', 
                    @flags=0
            GO

0

다른 방법은 다음 코드를 사용하여 각 작업에 단계를 삽입하는 것입니다 (먼저 실행해야 함).

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
    AND ars.is_local = 1) <> 'PRIMARY'
BEGIN
   --We're on the secondary node, throw an error
   THROW 50001, 'Unable to execute job on secondary node',1
END

성공의 다음 단계를 계속하고 실패시 작업보고 성공을 종료하려면이 단계를 설정하십시오.

기존 단계에 논리를 추가하는 대신 추가 단계를 추가하는 것이 더 깨끗합니다.


0

다른 최신 옵션은 master.sys.fn_hadr_is_primary_replica ( 'DbName')를 사용하는 것입니다. SQL 에이전트를 사용하여 데이터베이스 유지 관리 (수년 동안 사용한 커서와 결합)를 수행 할 때와 ETL 또는 기타 데이터베이스 특정 작업을 실행할 때 매우 유용합니다. 이점은 필요한 경우 전체 가용성 그룹을 보는 대신 데이터베이스를 단일화한다는 것입니다. 또한 기본에 "있는"데이터베이스에 대해 명령을 실행하는 것이 훨씬 불가능하지만 작업 실행 중에 자동 장애 조치가 발생하여 이제 보조 복제본에 있다고 가정 해 봅시다. 기본 복제본을 확인하는 위의 방법은 한 번만보고 업데이트하지 않습니다. 이 방법은 매우 유사한 결과를 얻고 필요한 경우보다 세밀한 제어를 제공하는 다른 방법 일뿐입니다. 또한이 질문에 대해이 방법을 설명하지 않은 이유는 SQL 2014가 출시 될 때까지 Microsoft가이 기능을 릴리스하지 않았기 때문입니다. 다음은이 기능을 사용하는 방법에 대한 샘플입니다.

   IF master.dbo.fn_hadr_database_is_primary_replica('Admin') = 1
    BEGIN 
        -- do whatever you were going to do in the Primary:
        PRINT 'Doing stuff in the Primary Replica';
    END
ELSE 
    BEGIN 
        -- we're not in the Primary - exit gracefully:
        PRINT 'This is not the primary replica - exiting with success';
    END

사용자 데이터베이스 유지 관리에 이것을 사용하려면 이것이 내가 사용하는 것입니다.

/*Below evaluates all user databases in the instance and gives stubs to do work; must change to get anything other than print statements*/
declare @dbname varchar(1000)
declare @sql nvarchar(4000)

declare AllUserDatabases cursor for
    select [name] from master.sys.databases
    where database_id > 4 --this excludes all sysdbs; if all but tempdb is desired, change to <> 2
    and [state] = 0

open AllUserDatabases
fetch AllUserDatabases into @dbname

while (@@FETCH_STATUS = 0)
    begin
    --PRINT @dbname
        set @sql = '
            IF master.sys.fn_hadr_is_primary_replica(''' + @dbname + ''') = 1
                BEGIN 
                    -- do whatever you are going to do in the Primary:
                    PRINT ''Doing stuff in the Primary Replica''
                END
            ELSE 
                BEGIN 
                    -- not in the Primary - exit gracefully:
                    PRINT ''This is not the primary replica - exiting with success''
                END             
        '
        exec sp_executesql @sql
        fetch AllUserDatabases into @dbname
    end
close AllUserDatabases
deallocate AllUserDatabases

이것이 유용한 팁이기를 바랍니다!


0

나는 이것을 사용한다 :

if (select primary_replica from sys.dm_hadr_availability_group_states) = @@SERVERNAME begin
... paste your t-sql here ...

end
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.