SQL Server Agent İşler ve Kullanılabilirlik Grupları


37

SQL Server 2012 kullanılabilirlik gruplarında zamanlanmış SQL Server Agent işleriyle ilgilenmek için en iyi uygulamayı arıyorum. Belki bir şeyleri özledim, ancak şu anki durumda SQL Server Agent'ın bu harika SQL2012 özelliği ile gerçekten entegre olmadığını hissediyorum.

Zamanlanmış bir SQL aracısı işini düğüm düğmesinden nasıl haberdar edebilirim? Örneğin, her saat başı veri yükleyen birincil düğümde çalışan bir işim var. Şimdi, eğer birincil azalırsa, şimdi birincil olan işi ikincilde nasıl aktif hale getirebilirim?

İşi her zaman ikincil olarak zamanlarsam başarısız olur; çünkü ikincil salt okunurdur.


Yanıtlar:


40

SQL Server Agent işinizde, mevcut örneğin sizin uygunluk grubunuzda aradığınız belirli bir rolü sağlayıp sağlamadığını test etmek için bazı koşullu mantıklara sahip olun:

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

Tüm bunlar yerel kopyanın mevcut rolünü çekmek ve eğer PRIMARYrolün içindeyse, birincil kopya ise, işinizin yapması gereken her şeyi yapabilirsiniz. ELSEBlok isteğe bağlıdır, ancak yerel yineleme birincil değilse mümkün mantığı işlemek için var.

Tabii ki, 'YourAvailabilityGroupName'yukarıdaki sorguyu asıl uygunluk grubunuzun adına göre değiştirin.

Kullanılabilirlik gruplarını yerine çalışma kümesi örnekleri ile karıştırmayın. Örneğin, belirli bir kullanılabilirlik grubunun birincil veya ikincil kopyası olup olmadığı, SQL Server Agent işleri ve benzeri gibi sunucu düzeyinde nesneleri etkilemez.


14

Bunu iş başına yapmak yerine (devam etmeye karar vermeden önce sunucunun durumu için her işi kontrol etmek yerine), sunucunun hangi durumda olduğunu görmek için her iki sunucuda da çalışan bir iş yarattım.

  • Birincil ise, AG'de bir veritabanını hedefleyen bir adımı olan herhangi bir işi etkinleştirin.
  • Sunucu ikincil ise, AG'de bir veritabanını hedefleyen herhangi bir işi devre dışı bırakın.

Bu yaklaşım bir çok şey sağlar

  • AG’de veri tabanı olmayan sunucularda (ya da Db’lerin AG’lerin giriş / çıkışlarının bir karışımı olan) çalışır
  • herkes yeni bir iş yaratabilir ve db'nin bir AG'de olup olmadığı konusunda endişelenmek zorunda kalmayabilir (işi diğer sunucuya eklemeyi hatırlamak zorunda olsalar da)
  • Her işin işe yaramaz bir e-posta göndermesine izin verir (tüm işlerinizde e-postalar doğru mu?)
  • Bir işin geçmişini görüntülerken, aslında gerçekten bir şey çalıştırmamış olan (aslında birincil olan) bir şey yapıp yapmadığını (aslında birincil olanı) görmek istersiniz (ikincide)

komut dosyası aşağıdaki alandaki veritabanını kontrol eder bu veritabanı bir Uygunluk Grubundaysa, komut dosyası bir miktar işlem yapacak

Bu işlem her sunucuda 15 dakikada bir yürütülür. (insanları işin neden devre dışı bırakıldığını bildiren bir yorum ekleme bonusu vardır)

/*
    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

Aptal değil, ama bir gecede yükler ve saatlik işler için bu işi halleder.

Bu prosedürün bir programda yürütülmesinden daha iyidir, bunun yerine Alert 1480'e (AG rol değişikliği uyarısı) yanıt olarak çalıştırın.


9

Bunu başarmak için iki kavramın farkındayım.

Önkoşul: Thomas Stringer'in cevabına dayanarak, iki sunucumuzun ana db'sinde iki fonksiyon oluşturdum:

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. Birincil kopyada çalıştırılmadıysa işi sonlandırın

    Bu durumda, her iki sunucudaki her iş, Adım 1 olarak aşağıdaki iki kod parçasından birine ihtiyaç duyar:

    Grup adına göre kontrol edin:

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

    Veri tabanı adına göre kontrol et:

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

    Bu ikincisini kullanırsanız, sistem veritabanlarına dikkat edin - tanım gereği herhangi bir kullanılabilirlik grubunun parçası olamazlar, bu nedenle bunlar için her zaman başarısız olacaktır.

    Bunların ikisi de yönetici kullanıcılar için kutudan çıkar. Yönetici olmayan kullanıcılar için, bunlardan biri burada önerilen ek izinler eklemeniz gerekir :

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

    Bu ilk adımda başarısızlık eylemini İş raporlama başarısından çık olarak ayarladıysanız, iş günlüğünü çirkin kırmızı çarpı işaretleriyle doldurmazsınız, asıl iş yerine sarı uyarı işaretlerine dönüşürler.

    Tecrübelerimize göre bu ideal değil. İlk başta bu yaklaşımı benimsedik, ancak asıl sorunu olan işleri bulma konusunda hızlı bir şekilde izini kaybettik, çünkü tüm ikincil çoğaltma işleri iş günlüğünü uyarı mesajlarıyla karıştırdı.

    O zaman ne için gittik:

  2. Proxy işleri

    Bu kavramı benimserseniz, gerçekten yapmak istediğiniz görev başına iki iş oluşturmanız gerekir. Birincisi, birincil kopyada çalıştırılıp çalıştırılmadığını kontrol eden "proxy işi". Eğer öyleyse, "işçi işini" başlatır, değilse, günlüğü uyarı veya hata mesajlarıyla karıştırmadan zarafetle sona erer.

    Şahsen her sunucuda görev başına iki iş yapma fikrinden hoşlanmıyorum, bunun kesinlikle daha sürdürülebilir olduğunu düşünüyorum ve adımın başarısızlık eylemini, biraz da olsa iş raporlama başarısından çıkmaya ayarlamak zorunda değilsiniz. garip.

    İşler için bir adlandırma şeması kabul ettik. Proxy işi sadece denir {put jobname here}. İşçi işi denir {put jobname here} worker. Bu, işçi işini proxy'den başlatmayı otomatikleştirmeyi mümkün kılar. Bunu yapmak için, ana dbs'nin ikisine de aşağıdaki prosedürü ekledim:

    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

    Bu, svf_AgReplicaStateyukarıda gösterilen işlevi kullanır; bunun yerine, diğer işlevi çağırmak yerine, veritabanı adını kullanarak kontrol etmek için kolayca değiştirebilirsiniz.

    Proxy işinin tek adımından, buna şöyle diyorsunuz:

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

    Bu , mevcut işin kimliğini almak için burada ve burada gösterildiği gibi Simgeleri kullanır . Daha sonra prosedür, geçerli iş adını msdb'den alır, ekler  workerve çalışan işçiyi başlatır sp_start_job.

    Bu hala ideal olmamakla birlikte, iş kayıtlarını bir önceki seçenekten daha düzenli ve sürdürülebilir tutar. Ayrıca, her zaman proxy işinin bir sysadmin kullanıcısıyla çalışmasını sağlayabilirsiniz, bu nedenle herhangi bir ekstra izin eklemek gerekmez.


3

Veri yükleme işlemi basit bir sorgu veya prosedür çağrısıysa, her iki düğümde de iş oluşturabilir ve veri yükleme işlemini yürütmeden önce veritabanının Updateability özelliğini temel alan birincil düğümü olup olmadığını belirlemeye izin verebilirsiniz:

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

-- Data Load code goes under here

END

1

Birincil Çoğaltma olup olmadığını kontrol eden yeni bir İş Adımı oluşturmak her zaman daha iyidir, ardından İkincil Bir Çoğaltma ise işi durdurmaya devam etmek için iş yürütmeye devam etmek için her şey yolundadır. İşi başarısızlığa uğratma, aksi halde gereksiz bildirimler göndermeye devam eder. Bunun yerine, işi durdurmak için iptal edin ve bu işler İkincil Çoğaltmada yürütüldüğünde hiçbir bildirim gönderilmez.

Belirli bir iş için ilk adımı eklemek için komut dosyası aşağıdadır.

Komut dosyasını yürütmek için not:

  • 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' 'Job_ID ile değiştir
  • 'YYYYYYYYYYYYYYYYYYYYYYYYYYYY' yerini Job_Name ile değiştirin
  • Birden çok Kullanılabilirlik Grubu varsa, AG adını, @AGNameToCheck_IfMoreThanSingleAG değişkeninde, hangi AG'nin kopya durumu için kontrol edileceği olarak ayarlayın.

  • Ayrıca, bu betiğin kullanılabilirlik grupları bulunmayan sunucularda bile iyi çalışması gerektiğini unutmayın. Yalnızca 2012 ve sonraki SQL Server sürümleri için yürütülür.

            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

Başka bir yol, her bir işte ilk önce çalışması gereken adımı aşağıdaki kodla eklemektir:

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

Bu adımı, bir sonraki başarı adımına devam etmek ve bir başarısızlık durumunda iş raporlama başarısından çıkmak için ayarlayın.

Mevcut bir adıma fazladan bir mantık eklemek yerine fazladan bir adım eklemeyi daha temiz buluyorum.


0

Başka bir daha yeni seçenek, master.sys.fn_hadr_is_primary_replica ('DbName') kullanıyor. Veritabanının bakımını yapmak için SQL Agent kullanırken (yıllardır kullandığım bir imleçle birleştiğimde) ve ayrıca bir ETL veya başka bir veritabanına özel bir görev yürütürken bu süper yardımı buldum. Bunun yararı, ihtiyacınız olan şeyse, tüm Kullanılabilirlik Grubuna bakmak yerine veritabanını seçmesidir. Ayrıca, bir komutun birincil "üzerinde" olan bir veritabanına karşı çalıştırılmasını çok daha olanaksız kılar, ancak iş yürütme sırasında otomatik bir yük devretme olduğunu ve şimdi ikincil bir çoğaltmada olduğunu varsayalım. Birincil kopyaya bakan yukarıdaki yöntemler tek bir bakış alır ve güncelleme yapmaz. Unutmayın, bu çok benzer sonuçlar elde etmenin ve ihtiyacınız olursa daha ayrıntılı kontrol sağlamanın farklı bir yoludur. Ayrıca, bu soruyu sorulduğunda bu yöntemin tartışılmamasının nedeni, Microsoft'un bu işlevi SQL 2014 yayınlanıncaya kadar serbest bırakmamasıydı. Aşağıda bu fonksiyonun nasıl kullanılabileceğine dair bazı örnekler verilmiştir:

   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

Bunu kullanıcı veritabanı bakımı için kullanmak istiyorsanız, kullandığım şey:

/*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

Umarım bu yararlı bir ipucu!


0

Bunu kullanıyorum:

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

end
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.