Еще до того как приступить к работе над серией статей о группах доступности AlwaysOn и заданиях SQL Server, я решил, что это будет цикл материалов, затрагивающих целый ряд тем. На первых порах предполагалось составить серию из трех основных частей.

  • Введение. Статьи, в которых я намеревался представить читателям «предметную область», определить термины и разъяснить, почему такие вопросы, как синхронизация заданий и обеспечение перехода на другой ресурс, следует считать достойными внимания.
  • Задания агентов SQL Server. Статьи, посвященные отдельным проблемам и ограничениям, а также вариантам «обходных маневров», применяемых при работе с «пакетными» заданиями, которые потенциально могут размещаться на нескольких серверах.
  • Резервные копии. Статьи, в которых я хотел высказать общие соображения относительно того, как гарантировать работу резервных копий в соответствии с вашим замыслом и избежать при этом различного рода сюрпризов.

Затем я планировал опубликовать заключение и поставить на этом точку. Но в процессе работы я пришел к выводу, что завершить серию следует четвертым разделом:

  • Дополнительные соображения. В завершающих статьях серии я решил представить несколько замечаний общего свойства, а также подробно рассказать о ряде стратегий и потенциальных приемах работы с заданиями SQL Server в ситуациях, когда используются группы доступности AlwaysOn.

Кроме того, я хотел бы поделиться некоторыми соображениями о главных серверах.

Ключевая идея данной серии статей состоит в том, что вы должны стараться синхронизировать задания агента SQL Server на ряде хостов группы доступности AlwaysOn, чтобы вне зависимости от того, идет ли речь о резервных копиях или о пакетных заданиях, вы всегда могли быть уверены, что задания выполняются: во-первых, в тот момент, когда это необходимо, и, во-вторых, на определенном хосте (или в отношении такого хоста) в соответствии с вашим замыслом. Чтобы помочь вам реализовать данную потребность, я описал ряд приемов синхронизации настроек заданий, деталей исполнения и других параметров на ряде серверов — все это с помощью сценариев или как бы «вручную».

Примечательно, что в системе SQL Server, в сущности, реализован фантастический механизм для удовлетворения потребностей в «синхронизации» без предварительной настройки, то есть пользователю нет нужды составлять особый код. И работа с данным механизмом осуществляется посредством настройки Multi-Server Administration или с использованием идеи, согласно которой вы можете взять один агент Single SQL Server Agent и наделить его статусом главного сервера Master Server (MSX), тогда последний может «направлять» или контролировать выполнение заданий и других деталей агента SQL Server на управляемых серверах, именуемых целевыми Target Servers (TSX).

Если вы познакомились со всеми предыдущими статьями серии и вам до сих пор не доводилось слышать о механизме Multi Server Administration (а может быть, вы слышали, но просто забыли о нем, как и большинство пользователей), для вас, возможно, стала откровением идея управления заданиями агентов SQL Server с помощью серверов MSX/TSX.

Да, их действительно можно использовать именно для этой цели. Но, как вы, вероятно, предположили, здесь имеется ряд ограничений.

На первый взгляд управление заданиями агентов SQL Server с помощью главного сервера осуществляется довольно просто. Вам нужно только выполнить следующие действия.

  • Настройте серверы MSX/TSX (хосты группы доступности должны быть настроены как целевые серверы, причем здесь вы можете указать, что каждое из заданий агента SQL Server, которое потребуется запустить, нужно выполнять на всех целевых серверах в одно и то же время и с абсолютно идентичной логикой/кодом.
  • Настройте или модифицируйте ваши «мультисерверные» задания таким образом, чтобы они включали логические конструкции для выявления предпочтительных реплик для резервных копий (когда будете работать с ними); руководствуйтесь при этом данными ранее рекомендациями или логическими конструкциями для выявления основной реплики при работе с пакетными заданиями; в этом случае ориентируйтесь на рекомендации из соответствующей статьи.
  • Предоставьте событиям развиваться своим чередом — без непроизводительных затрат и проблем, связанных с синхронизацией, которые были описаны в предыдущих статьях.

Разумеется, мы рассматриваем ситуацию, что называется, в самом общем виде. При этом я ни в коем случае не сбрасываю со счетов комбинацию MSX\TSX, поскольку это, без сомнения, наилучший механизм управления заданиями агентов SQL Server при работе с хостами или базами данных групп доступности AlwaysOn.

Впрочем, имеется ряд обстоятельств, которые вам следует держать в поле зрения, если вы решите идти по этому пути. Все это довольно очевидные ловушки, однако мне хотелось бы о них упомянуть.

  • Не размещайте главный сервер Master Server на одном из хостов своей группы доступности, ибо это поставит под угрозу отказоустойчивость ваших заданий/резервных копий. Сформулирую свою мысль иначе. Идея групп доступности состоит в том, чтобы объяснить следующий факт: отдельный экземпляр SQL Server (то есть хост группы доступности) может дать сбой и обязательно выйдет из строя. Поэтому размещение на одном из хостов эквивалента «главного контроллера» агента SQL Server, координирующего выполнение всех заданий, служит гарантией того, что в конечном счете вы на каком-то этапе потеряете главный сервер, отчего пострадают задания, средства предупреждения и т. д.
  • Если выполнение заданий и резервных копий является для вас важной задачей (как это бывает в большинстве случаев), имеет смысл преобразовать в кластер ваш сервер MSX или главный сервер. Иными словами, если вы установите новый «контроллер» (то есть главный сервер) с целью координации выполнения заданий на нескольких отказоустойчивых системах SQL Server, размещающих базы данных группы доступности, то получите единственную точку отказа при выполнении потенциально жизненно важных операций: получение резервных копий и выполнение заданий. Тогда вам нужно будет превратить в отказоустойчивый ваш сервер MSX, что сделать довольно легко: нужно, чтобы ваш агент SQL Server, выступающий в роли главного сервера, был развернут в составе экземпляра отказоустойчивого кластера AlwaysOn — так, чтобы он тоже стал в достаточной степени отказоустойчивым.
  • Синхронизация. Системы MSX/TSX могут функционировать безукоризненно, но иногда они теряют согласованность.
  • Пакеты SSIS. Предполагается, что системы MSX/TSX могут работать с пакетами SSIS (в виде планов обслуживания SQL Server) без предварительной настройки. Однако я подозреваю, что здесь вы со временем столкнетесь с проблемами — как, впрочем, и при работе с другими заданиями и пакетами SSIS. К сожалению, я не могу подкрепить это соображение количественными аргументами или дать какие-либо ссылки, так что я, возможно, неправ, но у меня остались смутные воспоминания о том, что несколько лет назад мне самому приходилось сталкиваться с некоторыми трудностями в этой сфере, и я решил отнести работу с пакетами SSIS к разряду потенциальных проблем.

В остальном же, если вы располагаете серверами (я имею в виду как аппаратную часть, так и лицензии SQL Server), необходимыми для обеспечения функционирования Multi-Server Administration, это будет для вас одним из наиболее подходящих вариантов организации управления заданиями агентов SQL Server, а также выполнения таких заданий при использовании групп доступности AlwaysOn.

Варианты развертывания более «продвинутых» систем и связанные с ними проблемы

В предыдущих статьях я оперировал главным образом «простыми» группами доступности, то есть решениями, состоящими из двух реплик (основная и нечитаемая вспомогательная). А это в свою очередь объясняет, почему тот код, который я использовал в тех статьях, где основное внимание уделялось либо выявлению различий в заданиях (синхронизации), размещенных на различных узлах, либо решению, на каком сервере выполнять задачу, определялся прежде всего понятием партнерского связанного сервера. Проблема, разумеется, в том, что хотя многие группы доступности будут создаваться в целях обеспечения «простой» отказоустойчивости или избыточности, я уверен, что многие группы доступности будут значительно более сложными или, скажем так, более «продвинутыми». По этой причине я хотел бы коснуться ряда проблем, с которыми вам придется столкнуться при запуске группы доступности, а также при запуске заданий агентов SQL Server в топологиях, включающих в себя более двух реплик.

Задания в сетях с развернутыми и разбросанными по ряду расположений группами доступности

В ситуациях, когда в группах доступности имеется более двух узлов — неважно, по какой причине они появляются, — отдельными вопросами, рассмотренными нами ранее, не обойтись, поскольку здесь возникает ряд других соображений, которые следует принимать во внимание. Вот на что вам стоило бы обратить внимание (порядок пунктов не принципиален).

  • Резервные копии и вспомогательные реплики (когда их число превышает единицу). Это самый простой пункт — о нем мы уже упоминали. Нужно только указать предпочтения, на какой реплике вы хотели бы выполнять резервные копии, и дело сделано.
  • Случаи восстановления данных. Если вы создали решения, обеспечивающие удаленную доступность, или «растянутые» системы, в которых некий центр обработки данных располагает двумя адаптерами FCI, и затем «подсоединили» свой кластер к кластеру во вспомогательном центре обработки данных (с помощью «репликации» средствами AlwaysOn), значит, вы сделали это с целью создания отказоустойчивого решения. В большинстве случаев вы тем самым попытаетесь зеркально отобразить детали настроек в одном центре обработки данных, в максимальной степени совпадающие с соответствующими показателями другого центра обработки данных. И все же, к сожалению, руководство зачастую пытается «прикрыть» один из таких центров обработки данных (и соответственно аппаратные средства) в рамках борьбы за экономию. Чаще всего эти меры не срабатывают, так что смело протестуйте против них всякий раз, когда это возможно, если только в соглашениях об уровне обслуживания нет четко выраженных положений, гарантирующих вашу защиту в случае неблагоприятной реакции, которая непременно последует. По этой причине, если вы окажетесь в таком незавидном положении, просто поймите, что в вашем случае рабочие нагрузки не должны быть идентичными на разных участках — так, чтобы некоторые задания можно было отключать или лишать доступа к отдельным элементам «инфраструктуры» и конечным точкам, которые, возможно, были бы доступны в другом центре обработки данных. Вам придется соответствующим образом осуществлять планирование (и принимать все сказанное в расчет при определении того, каким образом упомянутые обстоятельства будут влиять на стратегию, используемую вами для сохранения согласованности заданий и/или сохранения возможности их активации/деактивации по мере необходимости).
  • Лицензирование. Надеюсь, что следующее соображение будет понятно без всяких разъяснений, хотя я всегда изумляюсь, насколько часто организации упускают его из виду. Итак, помните, что если вы используете программу поддержки Software Assurance, вы, в сущности, заключаете сделку из разряда «купил один продукт — второй (равный или меньший по стоимости) получаешь бесплатно». По условиям этой сделки Microsoft продает вам лицензию на SQL Server Enterprise Edition, которая обеспечит вам функционирование «основного» сервера, а также вспомогательного сервера (на аналогичной или менее мощной аппаратной платформе), который может использоваться исключительно для обеспечения аварийных переключений. Если в дополнение к этому вам требуется получать отчеты с данного сервера или ваши разработчики хотят запускать тестовые базы данных либо решать аналогичные задачи, такой лицензии вам будет недостаточно. Вместо нее потребуется получить производственную лицензию. Соответственно, когда в организациях начинают говорить о выполнении масштабных сценариев (то есть о формировании вспомогательных реплик с правами только для чтения), важно иметь в виду, что при всех достоинствах упомянутых вариантов за них приходится платить — приобретать дополнительную лицензию. Хотя нужно сказать, что в случае приобретения с гарантией Software Assurance эта лицензия, в свою очередь, тоже приобретается по схеме «купил один продукт — второй получаешь бесплатно». Речь идет об SQL Server 2014 и более новых версиях — до их появления соглашение SA не требовалась.
  • Синхронизация и активация заданий. В ранее опубликованных статьях серии примеры кода и демонстрационные решения были привязаны в основном к «двухузловым группам доступности» и в значительной степени основывались на идее использования абстракций в форме двух серверов, которые в равной степени обращались друг к другу как к ‘PARTNER’ (через интерфейсы связанных серверов), чтобы облегчить операции взаимопроверки и сопоставления деталей, касающихся активных заданий, деталей заданий (например, является ли определение той или иной задачи на сервере A точной копией определения на сервере B) и т. д. Проблема, разумеется, состоит в том, что если вы употребляете ServerC или ServerD, эта конструкция не работает, и вы оказываетесь перед необходимостью искать другое средство обеспечения согласованности или идентичности определений заданий на серверах. Впрочем, работа с аварийными переключениями или определение того, какой сервер является активным и должен взять на себя выполнение заданий, осуществляется так же, как и раньше, и код, описанный ранее, по-прежнему функционирует в соответствии с ожиданиями. Так вот, имея в своем распоряжении предназначенные только для чтения вспомогательные серверы (для которых вы приобрели дополнительные платные лицензии, чтобы иметь возможность снять с основного сервера задачу обработки данных), вы, возможно, захотите рассмотреть варианты, позволяющие нацеливать некоторые (разумеется, предназначенные только для чтения) агенты SQL Server (для организации отчетов или для других целей) на один из вспомогательных серверов «только для чтения». И если у вас появится такое желание, выяснится, что вам требуется иной способ выявления «предпочтительного» сервера, после того как произойдет аварийное переключение и все декорации поменяются. Одно из очевидных решений в этом случае может состоять в том, чтобы вывести выполнение задания из той логики, которая определяет предпочтительную реплику при работе с резервными копиями. Во многих случаях, если вы решили перевести операции по обработке резервных копий на вспомогательный сервер, будет вполне логично выполнять здесь и предназначенные только для чтения задачи агентов SQL Server. Иначе когда в дело вступят более двух серверов, вполне вероятно, что вам придется создать механизм проверки всех серверов по имени (вместо использования слова PARTNER как псевдонима связанных серверов), и тогда вам также придется решать вопрос об определении, где именно будет выполняться этот код.

Дополнительные способы аварийных переключений заданий

Во всех статьях данной серии я исходил из довольно пессимистичного представления о том, как специалисты большинства организаций управляют заданиями агентов SQL Server. Иначе говоря, почти все фрагменты кода и приведенные мною примеры базируются на допущениях о том, как осуществляется управление изменениями, вносимыми в задания.

  • Управление изменениями. Основы­ваясь на прошлом опыте, я пришел к следующему выводу. Если возникает необходимость внести в задание агента SQL Server какое-то изменение, оказывается, что во многих организациях не разработана соответствующая политика. Когда некое задание необходимо изменить, такая политика со стопроцентной гарантией должна обеспечивать внесение изменений на любом сервере (или на всех серверах), где размещается текущая группа доступности. Учитывая это обстоятельство, я подготовил ряд статей, демонстрирующих, как выполняются проверки синхронизации, позволяющие извещать администраторов обо всех несоответствиях, которые могут возникнуть после того, как кто-нибудь непреднамеренно изменяет ту или иную часть задания и забывает зафиксировать это изменение на всех остальных серверах.
  • Активация и деактивация заданий. Подобным же образом я исходил из того, что если в течение нескольких дней не будет необходимости выполнять то или иное пакетное задание, специалисты большинства организаций предпочтут не удалять это задание, а просто деактивировать его. Например, если имеется некое задание по обработке данных, которое нужно будет отключить, скажем, на три дня, я полагал, что администраторы предпочтут найти это задание и деактивировать его, вместо того чтобы удалить, а потом воссоздать через несколько дней. Дело не только в том, что процедуры активации и деактивации, на мой взгляд, выполнять проще, но и в том, что когда вы ограничиваетесь этими процедурами, сохраняется история выполненных операций. Проблема, разумеется, состоит в том, что если вы, к примеру, просто деактивируете задание на сервере A, то, когда вам придется переключаться на сервер B (в качестве основного), вы получите задание, которое должно быть «с логической точки зрения» отключено, но, скорее всего, будет активировано. Все это может привести к катастрофическим последствиям. Поэтому я описал прием, позволяющий решить данную проблему с помощью «таблицы состояний».

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

Еще один способ работы с аварийными переключениями и активацией заданий

Несколько месяцев назад Том Мейер прислал мне электронное письмо, в котором изложил — в качестве примера более простого, организованного подхода к управлению заданиями — другой взгляд на проблему управления заданиями на серверах, где размещаются группы доступности. Он следил за публикацией данной серии статей, и ему захотелось поделиться со мной другим видением управления заданиями. А у меня, в свою очередь, возникло желание поделиться этими соображениями со всеми читателями.

Логика Тома и его подход к решению проблемы организованы намного более четко, чем мои. И ему удается с их помощью добиваться успеха главным образом потому, что Мейер убежден: регулярные «проверки синхронизации» не нужны, и ему не приходится следить за тем, какие задания активированы, а какие деактивированы. По этой причине Том выработал в конечном итоге гораздо более простой подход к управлению заданиями. Вот как он определяет данный процесс.

  • Создайте категорию или категории заданий для всех заданий, которые хотите синхронизировать.
  • Поместите в них все задания. Если вы вручную обновите категорию на ряде заданий посредством обновления таблицы системных заданий, вам придется осуществить повторный запуск агента, иначе в случае попытки повторно открыть это задание агент выдаст сообщение об ошибке.
  • Хранимая процедура проходит через все эти задания с использованием следующей логики:

— если есть основная реплика и она деактивирована, активируйте ее;

— если есть основная реплика и она активирована, не предпринимайте никаких действий;

— если есть дополнительная реплика и она активирована, деактивируйте ее;

— если есть дополнительная реплика и она деактивирована, не предпринимайте никаких действий.

  • Создайте задание агента, запускающее хранимую процедуру для определенных категорий.
  • Создайте оповещение для сообщения 1480.
  • Определите ответ на это оповещение с целью выполнения задания.

Взяв этот подход на вооружение, Том устраняет необходимость проведения по истечении определенного количества минут «проверок синхронизации» и вполне может обходиться без таблицы состояний. Кроме того, ему не требуются связанные серверы (для осуществления проверок синхронизации), что позволяет дополнительно упростить задачу. В листинге приведен пример кода, используемого Томом. В целом, это простое и изящное решение, абсолютно обоснованное в случаях, когда вам не приходится беспокоиться по поводу проблем, описанных мною в начале статьи (то есть связанных с управлением изменениями, а также с активацией и деактивацией заданий). Подобным же образом, если перед вами не стоит проблема управления изменениями, но вы хотите сохранить возможность активации или деактивации заданий, вы сможете с легкостью адаптировать подход Тома к своей ситуации и включить в код таблицу состояний или, возможно, создать новую категорию заданий под названием, скажем, AGSuchAndSuch-Disabled, с помощью которой будете решать сходные задачи без составления таблицы состояний.

Словом, способов решения проблемы управления заданиями имеется множество. Ответ на вопрос о том, какой из них больше всего подойдет для вас, зависит от вашей рабочей среды, принятых в организации установок и от ваших потребностей.

Листинг. Управление заданиями
CREATE procedure [dbo].[uspDBAHADRAgentJobFailover] (@agname varchar(200))
as
begin
       declare @is_primary_replicate bit
       declare @job_name sysname
       declare @job_enabled bit

       select @is_primary_replicate=DBAWork.dbo.fn_hadr_group_is_primary(@agname)

       declare job_cursor cursor for
       select s.name from msdb.dbo.sysjobs s
       inner join msdb.dbo.syscategories c on s.category_id = c.category_id
       where c.name = @agname
       order by name

       open job_cursor
       fetch next from job_cursor into @job_name
       while @@fetch_status = 0
       begin
              select @job_enabled=enabled from msdb.dbo.sysjobs where name = @job_name
              if @is_primary_replicate = 1
              begin
                     if @job_enabled = 1
                           print @job_name+’ enabled on primary. do nothing’
                     else
                     begin
                           print @job_name+’ disabled on primary. enable it !’
                           exec msdb.dbo.sp_update_job @job_name = @job_name,@enabled = 1
                     end
              end
              else if (@is_primary_replicate = 0)
              begin
                     if @job_enabled = 1
                     begin
                           print @job_name+’ enabled on secondary. disable it !’
                           exec msdb.dbo.sp_update_job @job_name = @job_name,@enabled = 0
                     end
                     else
                           print @job_name+’ disabled on secondary. do nothing’
              end
              fetch next from job_cursor into @job_name
       end
       close job_cursor
       deallocate job_cursor
end

GO

Click and drag to move