Các nhóm công việc và các nhóm khả dụng của SQL Server


37

Tôi đang tìm kiếm cách thực hành tốt nhất để xử lý các công việc Đại lý SQL Server được lên lịch trong các nhóm sẵn có của SQL Server 2012. Có thể tôi đã bỏ lỡ điều gì đó, tuy nhiên ở trạng thái hiện tại tôi cảm thấy rằng SQL Server Agent không thực sự được tích hợp với tính năng SQL2012 tuyệt vời này.

Làm thế nào tôi có thể làm cho một công việc đại lý SQL theo lịch trình nhận biết về một chuyển đổi nút? Ví dụ, tôi có một công việc đang chạy trên nút chính tải dữ liệu mỗi giờ. Bây giờ nếu chính bị hỏng, làm thế nào tôi có thể kích hoạt công việc trên thứ cấp mà bây giờ trở thành chính?

Nếu tôi lên lịch công việc luôn ở cấp hai thì thất bại vì sau đó thứ cấp chỉ đọc.


Câu trả lời:


40

Trong công việc Tác nhân Máy chủ SQL của bạn, có một số logic có điều kiện để kiểm tra xem phiên bản hiện tại có đang phục vụ vai trò cụ thể mà bạn đang tìm kiếm trong nhóm khả dụng của bạn không:

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ất cả những điều này là kéo vai trò hiện tại của bản sao cục bộ và nếu nó ở PRIMARYvai trò đó, bạn có thể làm bất cứ điều gì mà công việc của bạn cần làm nếu đó là bản sao chính. Các ELSEkhối là không bắt buộc, nhưng nó có thể để xử lý logic nếu bản sao địa phương của bạn không phải là chính.

Tất nhiên, thay đổi 'YourAvailabilityGroupName'truy vấn trên thành tên nhóm khả dụng thực tế của bạn.

Đừng nhầm lẫn các nhóm khả dụng với các trường hợp cụm chuyển đổi dự phòng. Cho dù cá thể là bản sao chính hay phụ cho một nhóm khả dụng nhất định sẽ không ảnh hưởng đến các đối tượng cấp máy chủ, như các công việc của Đại lý máy chủ SQL, v.v.


14

Thay vì thực hiện việc này trên cơ sở từng công việc (kiểm tra mọi công việc về trạng thái của máy chủ trước khi quyết định tiếp tục), tôi đã tạo một công việc chạy trên cả hai máy chủ để kiểm tra xem máy chủ đang ở trạng thái nào.

  • Nếu là chính, thì kích hoạt bất kỳ công việc nào có bước nhắm mục tiêu cơ sở dữ liệu trong AG.
  • Nếu máy chủ là thứ yếu, hãy vô hiệu hóa bất kỳ công việc nào nhắm mục tiêu cơ sở dữ liệu trong AG.

Cách tiếp cận này cung cấp một số điều

  • nó hoạt động trên các máy chủ không có cơ sở dữ liệu trong AG (hoặc kết hợp Db's in / out AGs)
  • bất cứ ai cũng có thể tạo một công việc mới và không phải lo lắng về việc db có ở trong AG hay không (mặc dù họ phải nhớ thêm công việc vào máy chủ khác)
  • Cho phép mỗi công việc có một email thất bại vẫn hữu ích (tất cả các công việc của bạn đều có email thất bại phải không?)
  • Khi xem lịch sử của một công việc, bạn thực sự phải xem liệu công việc đó có thực sự chạy và đã làm một cái gì đó (đây là chính), thay vì nhìn thấy một danh sách dài thành công mà thực sự không chạy bất cứ thứ gì (trên thứ cấp)

kịch bản kiểm tra cơ sở dữ liệu trong trường bên dưới nếu cơ sở dữ liệu này nằm trong Nhóm sẵn có, tập lệnh sẽ thực hiện một số hành động

Proc này được thực hiện cứ sau 15 phút trên mỗi máy chủ. (có thêm phần thưởng khi thêm một bình luận để thông báo cho mọi người lý do tại sao công việc bị vô hiệu hóa)

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

Nó không phải là bằng chứng ngu ngốc, nhưng đối với tải qua đêm và công việc hàng giờ, nó hoàn thành công việc.

Thậm chí tốt hơn so với việc thủ tục này chạy theo lịch trình, thay vào đó hãy chạy nó để đáp ứng với Thông báo 1480 (cảnh báo thay đổi vai trò của AG).


9

Tôi nhận thức được hai khái niệm để thực hiện điều này.

Điều kiện tiên quyết: Dựa trên câu trả lời của Thomas Stringer, tôi đã tạo hai hàm trong db chính của hai máy chủ của chúng tôi:

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. Tạo một công việc chấm dứt nếu nó không được thực hiện trên bản sao chính

    Trong trường hợp này, mọi công việc trên cả hai máy chủ đều cần một trong hai đoạn mã sau đây như Bước 1:

    Kiểm tra theo tên nhóm:

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

    Kiểm tra theo tên cơ sở dữ liệu:

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

    Nếu bạn sử dụng cái thứ hai này, hãy cẩn thận với cơ sở dữ liệu hệ thống - theo định nghĩa, chúng không thể là một phần của bất kỳ nhóm khả dụng nào, vì vậy nó sẽ luôn thất bại đối với những cơ sở dữ liệu đó.

    Cả hai đều hoạt động tốt cho người dùng quản trị. Đối với người dùng không phải là quản trị viên, bạn phải thêm quyền, một trong số họ được đề xuất ở đây :

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

    Nếu bạn đặt hành động thất bại thành Thoát báo cáo thành công trong bước đầu tiên này, bạn sẽ không nhận được nhật ký công việc đầy dấu hiệu chữ thập đỏ xấu xí, thay vào đó, công việc chính họ sẽ chuyển thành dấu hiệu cảnh báo màu vàng.

    Từ kinh nghiệm của chúng tôi, điều này không lý tưởng. Lúc đầu, chúng tôi đã áp dụng phương pháp này, nhưng nhanh chóng mất dấu vết về việc tìm kiếm các công việc thực sự có vấn đề, bởi vì tất cả các công việc sao chép thứ cấp làm lộn xộn nhật ký công việc với các thông điệp cảnh báo.

    Những gì chúng tôi sau đó đã đi là:

  2. Công việc proxy

    Nếu bạn áp dụng khái niệm này, bạn thực sự sẽ cần tạo hai công việc cho mỗi nhiệm vụ bạn muốn thực hiện. Cái đầu tiên là "công việc proxy" kiểm tra xem nó có được thực thi trên bản sao chính không. Nếu vậy, nó bắt đầu "công việc công nhân", nếu không, nó chỉ kết thúc một cách duyên dáng mà không làm lộn xộn nhật ký với các thông báo cảnh báo hoặc lỗi.

    Mặc dù cá nhân tôi không thích ý tưởng có hai công việc cho mỗi nhiệm vụ trên mỗi máy chủ, tôi nghĩ rằng nó chắc chắn sẽ dễ bảo trì hơn và bạn không phải đặt hành động thất bại của bước để Thoát khỏi thành công báo cáo công việc , đó là một chút lúng túng.

    Đối với các công việc, chúng tôi áp dụng một kế hoạch đặt tên. Công việc proxy chỉ được gọi {put jobname here}. Công việc công nhân được gọi {put jobname here} worker. Điều này cho phép tự động hóa bắt đầu công việc worker từ proxy. Để làm như vậy, tôi đã thêm thủ tục sau vào cả hai dbs chính:

    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
    

    Điều này sử dụng svf_AgReplicaStatechức năng hiển thị ở trên, bạn có thể dễ dàng thay đổi điều đó để kiểm tra bằng cách sử dụng tên cơ sở dữ liệu thay vì gọi hàm khác.

    Từ bước duy nhất của công việc proxy, bạn gọi nó như thế này:

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

    Điều này sử dụng các Token như được hiển thị ở đâyở đây để lấy id của công việc hiện tại. Quy trình sau đó lấy tên công việc hiện tại từ msdb, nối  workervào nó và bắt đầu công việc worker bằng cách sử dụng sp_start_job.

    Mặc dù điều này vẫn không lý tưởng, nhưng nó giữ cho nhật ký công việc gọn gàng và dễ bảo trì hơn so với tùy chọn trước đó. Ngoài ra, bạn luôn có thể chạy công việc proxy với người dùng sysadmin, vì vậy việc thêm bất kỳ quyền bổ sung nào là không cần thiết.


3

Nếu quá trình tải dữ liệu là một truy vấn đơn giản hoặc gọi thủ tục thì bạn có thể tạo công việc trên cả hai nút và để nó xác định xem đó có phải là nút chính dựa trên thuộc tính Khả năng cập nhật của cơ sở dữ liệu hay không, trước khi thực hiện quy trình tải dữ liệu:

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

-- Data Load code goes under here

END

1

Luôn luôn tốt hơn để tạo Bước công việc mới để kiểm tra xem đó có phải là Bản sao chính hay không, mọi thứ đều ổn để tiếp tục thực hiện công việc khác nếu đó là Bản sao phụ sau đó Dừng công việc. Đừng thất bại công việc khác nó sẽ tiếp tục gửi thông báo không cần thiết. Thay vào đó hãy dừng công việc để nó bị hủy và không có thông báo nào được gửi đi bất cứ khi nào các công việc này được thực thi trên Bản sao phụ.

Dưới đây là kịch bản để thêm một bước đầu tiên cho một công việc cụ thể.

Lưu ý để thực thi tập lệnh:

  • Thay thế 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' bằng Job_ID
  • Thay thế 'YYYYYYYYYYYYYYYYYYYYYYYYY' bằng Job_Name
  • Nếu có nhiều Nhóm sẵn có, thì hãy đặt tên AG trong biến @AGNameToCheck_ IfMoreThanSingleAG theo đó AG cần được kiểm tra trạng thái bản sao.

  • Cũng lưu ý rằng tập lệnh này sẽ hoạt động tốt ngay cả trên các máy chủ không có nhóm khả dụng. Sẽ chỉ thực hiện cho các phiên bản SQL Server 2012 trở lên.

            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

Một cách khác là chèn một bước trong mỗi công việc, nên chạy trước, với đoạn mã sau:

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

Đặt bước này để tiếp tục với bước tiếp theo về thành công và bỏ công việc báo cáo thành công khi thất bại.

Tôi thấy nó sạch hơn để thêm một bước bổ sung thay vì thêm logic vào một bước hiện có.


0

Một tùy chọn khác, mới hơn, đang sử dụng master.sys.fn_hadr_is_primary_Vplica ('DbName'). Tôi đã thấy điều này siêu hữu ích khi sử dụng SQL Agent để bảo trì cơ sở dữ liệu (kết hợp với một con trỏ tôi đã sử dụng trong nhiều năm) và cả khi thực hiện một ETL hoặc tác vụ cụ thể của cơ sở dữ liệu khác. Lợi ích là nó chỉ ra cơ sở dữ liệu thay vì nhìn vào toàn bộ Nhóm sẵn có ... nếu đó là những gì bạn cần. Nó cũng làm cho nhiều khả năng hơn là một lệnh sẽ được thực thi đối với cơ sở dữ liệu "đã" trên chính, nhưng giả sử một chuyển đổi dự phòng tự động đã xảy ra trong quá trình thực thi công việc, và bây giờ nó là bản sao thứ cấp. Các phương pháp trên nhìn vào bản sao chính sẽ xem và không cập nhật. Hãy nhớ rằng, đây chỉ là một cách khác nhau để đạt được kết quả rất giống nhau và cung cấp cho kiểm soát chi tiết hơn, nếu bạn cần nó. Ngoài ra, lý do phương pháp này không được thảo luận khi câu hỏi này được hỏi là vì Microsoft đã không phát hành chức năng này cho đến khi SQL 2014 được phát hành. Dưới đây là một số mẫu về cách sử dụng chức năng này:

   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

Nếu bạn muốn sử dụng điều này để bảo trì cơ sở dữ liệu người dùng, đây là những gì tôi sử dụng:

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

Tôi hy vọng đây là một mẹo hữu ích!


0

Tôi sử dụng cái này:

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

end
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.