Завдання агента SQL Server і групи доступності


37

Я шукаю найкращу практику роботи з запланованими роботами агента SQL Server у групах доступності SQL Server 2012. Можливо, я щось пропустив, проте в поточному стані я відчуваю, що агент SQL Server насправді не інтегрований з цією чудовою функцією SQL2012.

Як я можу зробити заплановану роботу агента SQL відомо про перемикання вузлів? Наприклад, у мене є робота, що працює на первинному вузлі, який завантажує дані щогодини. Тепер, якщо первинна знижується, як я можу активувати роботу на другорядній, яка тепер стає первинною?

Якщо я планую роботу завжди на вторинній, вона виходить з ладу, тому що вторинна є лише для читання.


Відповіді:


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

Замість того, щоб робити це за принципом роботи (перевіряючи кожне завдання на стан сервера, перш ніж вирішити продовжити), я створив роботу, що працює на обох серверах, щоб перевірити, у якому стані знаходиться сервер.

  • Якщо його основне, тоді ввімкніть будь-яку роботу, яка має крок націлення на базу даних в АГ.
  • Якщо сервер є вторинним, відключіть будь-яке завдання, орієнтоване на базу даних в АГ.

Такий підхід передбачає низку речей

  • він працює на серверах, де в АГ немає баз даних (або суміш Db введення / виходу з АГ)
  • будь-хто може створити нову роботу і не турбуватися про те, чи є db в АГ (хоча вони повинні пам’ятати, щоб додати завдання на інший сервер)
  • Дозволяє кожній роботі надсилати електронну пошту про помилку, яка залишається корисною (усі ваші завдання мають електронну пошту про помилку?)
  • Переглядаючи історію роботи, ви насправді дізнаєтесь, чи справді робота виконувалась і щось робила (це є основним), а не бачити довгий список успіху, який насправді нічого не запускав (на вторинному рівні)

скрипт перевіряє базу даних у полі нижче якщо ця база даних є у групі доступності, сценарій вживатиме певних дій

Ця процедура виконується кожні 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

Це не дурний доказ, але для навантажень за ніч і погодинних робіт він виконує роботу.

Навіть краще, ніж запускати цю процедуру за розкладом, замість цього запустити її у відповідь на Alert 1480 (попередження про зміну ролі АГ).


9

Мені відомо дві концепції для цього.

Необхідна умова: На підставі відповіді Томаса Стрінгера я створив дві функції в головному 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. Проксі-завдання

    Якщо ви приймете цю концепцію, вам фактично потрібно буде створити два завдання за завдання, яке ви хочете виконати. Перший - це "завдання проксі", яке перевіряє, чи виконується воно в основній репліку. Якщо так, то він запускає "робочу роботу", якщо ні, він просто витончено закінчується, не захаращуючи журнал попередженнями або повідомленнями про помилки.

    Хоча мені особисто не подобається ідея мати два завдання на кожне завдання на кожному сервері, я вважаю, що це, безумовно, більш рентабельне, і вам не доведеться встановлювати дію відмови, щоб Вийти з успіху у звіті про роботу , що небагато незграбний

    Для робочих місць ми прийняли схему називання. Завдання проксі просто називається {put jobname here}. Робота працівника називається {put jobname here} worker. Це дає можливість автоматизувати роботу робочого з проксі. Для цього я додав таку процедуру до обох головних dbs:

    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}'

    При цьому використовуються маркери, як показано тут і тут, щоб отримати ідентифікатор поточної роботи. Потім процедура отримує поточну назву завдання від 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

Завжди краще створити новий крок роботи, який перевіряє, чи це первинна репліка, тоді все добре, щоб продовжити виконання завдання ще, якщо це вторинна репліка, то зупиніть роботу. Не відмовляйтесь від роботи, інакше вона продовжуватиме надсилати непотрібні сповіщення. Замість цього зупиніть роботу, щоб вона була скасована, і повідомлення не надсилалися щоразу, коли ці завдання виконуються у вторинній репліку.

Нижче наведено сценарій для додавання першого кроку до конкретної роботи.

Примітка для виконання сценарію:

  • Замініть "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" на Job_ID
  • Замініть "YYYYYYYYYYYYYYYYYYYYYY" на Job_Name
  • Якщо є кілька груп доступності, то встановіть ім'я AG у змінній @AGNameToCheck_IfMoreThanSingleAG щодо того, в якій 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 Agent для обслуговування баз даних (у поєднанні з курсором, яким я користувався протягом багатьох років), а також під час виконання ETL або іншого завдання, пов’язаного з базою даних. Перевага полягає в тому, що він виділяє базу даних, а не дивиться на всю групу доступності ... якщо це те, що вам потрібно. Це також робить набагато неправдоподібнішим, що команда буде виконуватися проти бази даних, яка "була" на первинному, але скажімо, автоматичний збій стався під час виконання завдання, і тепер він знаходиться у вторинній репліку. Вищеописані методи, що розглядають основну репліку, мають один погляд і не оновлюються. Майте на увазі, що це просто інший спосіб досягти дуже подібних результатів і надати більш детальний контроль, якщо вам це потрібно. Крім того, причина цього методу не обговорювалася, коли було задано це питання, тому що Microsoft не випустила цю функцію до моменту виходу SQL 2014. Нижче наведено декілька прикладів використання цієї функції:

   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

Я сподіваюся, що це корисна порада!


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.