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

В части 3 я рассказал о том, как создаются предупреждения в ситуациях аварийных переключений или в случаях, когда происходят изменения в потоках данных. Такие предупреждения весьма полезны, но они не дают полного представления о работоспособности группы доступности. Представьте себе такую ситуацию. У вас имеется «простая» группа доступности, состоящая из двух узлов. Отмечу, кстати, что это весьма неудачный вариант. Вы, конечно, можете иметь группы доступности, состоящие всего из двух активных узлов SQL Server, но при этом наверняка сочтете целесообразным дополнить кластер третьим сервером — для обеспечения кворума (а также для прекращения споров о том, должен ли Server1 исходить из того, что он является главным, когда обнаружит проблемы «связи» с Server2, который также выявит аналогичные проблемы в области коммуникации и, возможно, определит себя как главный сервер).

В подобной простой двухузловой конфигурации если Server1 выходит из строя, администратор получает предупреждение об изменении потока данных и о переключении, как и в случае с предупреждениями, определенными в части 3. Подобным же образом, если работоспособность Server1 сохраняется и эта система не испытывает каких-либо проблем, а Server2 выходит из строя, администратор, как предполагается, получит информацию об изменении в потоке данных и о синхронизации. Однако если аварийным отказом завершится работа третьего узла (то есть «узла-свидетеля», на котором не обязательно должна выполняться система SQL Server, но который помогает в определении кворума), вы попадаете в потенциально опасную ситуацию, где набор обстоятельств может вызвать конфликт между Server1 и Server2. И если у вас нет нейтрального сервера, способного урегулировать его, это грозит серьезными неприятностями.

Организация дополнительных проверок работоспособности

Допускать риск того, что узлы одного из кластеров сети выйдут из строя, не дав знать об этом, не стоит. Лучше организовать регулярные проверки работоспособности по расписанию — осведомиться у SQL Server, в каком состоянии члены базового кластера группы доступности, и подготовить отчет либо предупреждение на случай, если что-то пойдет не так.

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

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

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

Данная хранимая процедура базируется на определяемой пользователем функции dbo.fn_hadr_database_is_primary, которую я приводил в части 5. Так что, если вы решите использовать опубликованную выше комбинацию сценария и хранимой процедуры, обязательно скопируйте и пользовательскую функцию.

Если вы хотите применить упомянутую хранимую процедуру для организации регулярных проверок состояния работоспособности кластера и группы доступности, можете создать простое задание агента SQL Server (с таким именем, как Regular AG Health Checkup) и использовать следующий текст в качестве команды на выполнение один раз в течение 1–5 минут. Однократное выполнение этого сценария занимает практически 0 секунд, так что его можно безо всякого риска запускать каждую минуту.

EXEC master.dbo.dba_CheckOnAndReport
   AGStatus
   @GroupName = N'Name Of AG
   To Watch Here',
   @ProfileName = N’General’,
   @OperatorName = N’Alerts’;

Здесь @GroupName — это имя группы доступности, за которой вы хотите наблюдать, скажем, ‘SSV’, ‘Production’, ‘Widgets’, ‘MyFirstAG’ или какое-либо иное.

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

Резервное копирование баз данных групп доступности

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

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

Резервные копии

Как я уже говорил, одно из преимуществ групп доступности AlwaysOn состоит в том, что они позволяют администраторам баз данных решать вопросы обеспечения высокой доступности и восстановления после аварийных сбоев с помощью единого набора инструментов и интерфейсов. Благодаря этому обстоятельству, а также широким возможностям развертывания данные группы являются весьма мощным средством обеспечения как высокой доступности данных, так и их восстановления в случае катастрофических отказов.

Но это не означает, что необходимость в резервном копировании отпадает. Попросту говоря, без резервного копирования никак не обойтись.

Как ни парадоксально, с развертыванием в сети групп доступности AlwaysOn выполнение резервного копирования несколько осложняется. Правда, эти сложности не так серьезны, как проблемы, с которыми вы столкнетесь, добиваясь того, чтобы пакетные задания выполнялись в соответствии с вашим замыслом. Но как бы то ни было, вам придется преодолеть несколько препятствий.

Фрагментация. К сожалению, термин «фрагментация» в каком-то смысле перегружен. При его упоминании технические эксперты обычно вспоминают о фрагментации дисков. Но в кругах специалистов по SQL Server этот термин, как правило, ассоциируется с проблемой фрагментации индексов. Впрочем, в обоих случаях основная идея состоит в том, что массив необходимых вам данных теряет свою непрерывность, или близость. А это как раз та ситуация, которой все мы хотим избежать при работе с резервными файлами. К примеру, когда (именно «когда», а не «если») у вас возникает необходимость в чрезвычайной ситуации выполнить восстановление базы данных группы доступности, вам так или иначе приходится начинать с «разрыва» группы доступности, что приводит к некоторому осложнению и замедлению процесса. В такой ситуации, разумеется, вас меньше всего вдохновляет перспектива заниматься лихорадочным поиском резервных файлов на нескольких серверах. Только представьте такую картину: последняя полная резервная копия и 15 последующих копий T-Log размещаются на сервере SERVER1, далее 7 копий T-Log — на SERVER2, а последующие 18 копий T-Log — опять-таки на SERVER2. К счастью, предотвращение подобных ситуаций — задача, в сущности, тривиальная, но ее решение потребует от вас заблаговременного планирования.

Выполнение. Подобным же образом, чтобы определить, на каком из хостов будут выполняться задания по резервному копированию, не нужно быть семи пядей во лбу, но, если вы хотите, чтобы эта задача решалась корректно, уделите время дополнительному планированию. Кстати, здесь уместно отметить, что, хотя перекладывание нагрузки по резервному копированию на систему, содержащую одну из вспомогательных реплик, даст немалый выигрыш в производительности, это решение вам дорого обойдется: оно потребует приобретения набора новых или специализированных лицензий Enterprise Edition с целью обеспечения соответствия установленным требованиям в отношении лицензирования.

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

Листинг. Детализированные проверки
USE master;
GO

IF OBJECT_ID('dbo.dba_CheckOnAndReportAGStatus','P') IS NOT NULL
DROP PROC dbo.dba_CheckOnAndReportAGStatus;
GO

CREATE PROC dbo.dba_CheckOnAndReportAGStatus
@GroupName sysname, -- имя группы
   доступности, за которой
   осуществляется наблюдение
@ProfileName sysname, -- имя профиля, используемого
   для отправки сообщений
   по электронной почте
@OperatorName sysname -- имя оператора, которого
   необходимо известить
   при возникновении проблем
AS
SET NOCOUNT ON;

DECLARE @errorMessage nvarchar(max);
DECLARE @subject nvarchar(300);
DECLARE @crlf char(2) = CHAR(13) + CHAR(10);

-- 0) Удостоверьтесь в наличии активного основного сервера
(PRIMARY)
-- (и если таковой сервер имеется, прекратите последующие
проверки):
DECLARE @primaryReplica sysname;
SELECT @primaryReplica = agstates.primary_replica
FROM sys.availability_groups AS ag
LEFT OUTER JOIN
  sys.dm_hadr_availability_group_states as agstates ON
ag.group_id = agstates.group_id
WHERE
ag.name = @GroupName;

IF ISNULL(@primaryReplica,'') = '' BEGIN
 
  SET @subject = N'[SQL SERVER HIGH AVAILABILITY PROBLEM]
  - No Active PRIMARY';

  SET @errorMessage = N'There is currently no Replica defined
  as an ONLINE PRIMARY for the following Availability Group: ';
  SET @errorMessage += @crlf + @GroupName;

  EXEC msdb..sp_notify_operator
   @profile_name = @ProfileName,
      @name = @OperatorName,
      @subject = @subject,
      @body = @errorMessage;

   END;
ELSE BEGIN
  -- иначе у нас ИМЕЕТСЯ работоспособный/работающий
  основной сервер. Если же данный код НЕ выполняется на
  -- основном сервере, значит, мы закончили
  (то есть short-circuit/escape/return);
 
  -- берем базу данных и проверяем ее:
  DECLARE @dbName sysname;
  SET @dbName = (
   SELECT TOP 1 database_name
   FROM sys.availability_databases_cluster
   WHERE group_id = (SELECT group_id
    FROM sys.availability_groups
    WHERE name = @GroupName));

  IF(NOT dbo.fn_hadr_database_is_primary(@dbName) = 1)
  BEGIN  
   -- тогда bail (???) - у нас нет необходимости проводить
   дополнительные проверки или тесты.
   RETURN 0; -- передать возвращаемое значение
   NORMAL/successful (то есть получение ожидаемого
   результата).
  END;

END;

-- 1) Проверяем состояние кластера/кворума:
DECLARE @status varchar(50);
SELECT @status = (SELECT TOP 1 quorum_state_desc FROM
sys.dm_hadr_cluster);
IF @status IS NULL OR UPPER(@status) != 'NORMAL_QUORUM'
BEGIN
 
  SET @subject = N'[SQL SERVER HIGH AVAILABILITY PROBLEM]
  - Non Healthy Cluster Status';

  SET @errorMessage = N'WSFC Cluster Status Problem.';
  SET @errorMessage += @crlf + N'Quorum not detected or
  not normal. [SELECT * FROM sys.dm_hadr_cluster for more
  information.]';

  EXEC msdb..sp_notify_operator
   @profile_name = @ProfileName,
      @name = @OperatorName,
      @subject = @subject,
      @body = @errorMessage;
END;

-- 2) Проверяем членов кластера:
DECLARE @nonUp int;
SELECT @nonUP = COUNT(*) FROM sys.dm_hadr_cluster_members
WHERE member_state != 1; -- UP
IF @nonUp > 0 BEGIN
  SET @subject = N'[SQL SERVER HIGH AVAILABILITY PROBLEM]
  - Non-Operational Members';

  DECLARE @members nvarchar(200) = N'';
  SELECT @members += member_name + N',' FROM
  sys.dm_hadr_cluster_members WHERE member_state != 1;

  SET @errorMessage = N'The Following Availability
  Group Hosts/Servers are currently Down: ';
  SET @errorMessage += @crlf + LEFT(@members,
  LEN(@members) - 1);

  EXEC msdb..sp_notify_operator
   @profile_name = @ProfileName,
      @name = @OperatorName,
      @subject = @subject,
      @body = @errorMessage;
END;

-- 3) Проверяем состояние синхронизации:
DECLARE @nonHealthy int;
SELECT @nonHealthy = COUNT(*) FROM
sys.dm_hadr_availability_group_states WHERE
synchronization_health != 2;
IF @nonHealthy > 0 BEGIN
  SET @subject = N'[SQL SERVER HIGH AVAILABILITY PROBLEM]
  - Non-Synchronized Status';

  SET @members = N'';
  SELECT @members += ags.name + ' : '
  + hags.synchronization_health_desc + ','
  FROM sys.dm_hadr_availability_group_states hags
  INNER JOIN sys.availability_groups ags ON ags.group_id =
  hags.group_id
  WHERE hags.synchronization_health != 2;

  SET @errorMessage = N'The following Availability Groups
  have the following (non-healthy/non-synchronized)
  Synchronization States: ';
  SET @errorMessage += @crlf + LEFT(@members,
  LEN(@members) - 1);

  EXEC msdb..sp_notify_operator
   @profile_name = @ProfileName,
      @name = @OperatorName,
      @subject = @subject,
      @body = @errorMessage;

END;

RETURN 0;
GO