Компьютеризация, информатизация и другие –ции всё сильнее вторгаются в нашу жизнь. Необходимость принимать оперативные решения и быстро обрабатывать большие объёмы информации требует автоматизации всё новых областей человеческой деятельности. Многие из этих задач являются критически важными и требуют различных степеней надёжности функционирования. Отказ – даже частичный – таких систем может привести к проблемам на различных уровнях – вплоть до глобальной катастрофы. Ранее подобные системы строились по схеме, которая позволяла просчитать все возможные варианты развития событий и для каждого из них содержала план действий, приводящий к ликвидации проблемы.
С ростом объёма информации и числа компонентов информационных и управляющих систем возможность просчитать все варианты становилась всё призрачнее и призрачнее. Потребовались новые понятия и требования, которые позволяли бы в подобных системах манипулировать большими подсистемами как отдельными компонентами с определёнными параметрами по надёжности. В этом случае опять удавалось просчитать все возможные варианты и построить планы устранения критических ситуаций для всех возможных случаев.
Такими компонентами могу выступать отдельные программные, аппаратные или аппаратно-программные комплексы. При этом на уровне анализа возможного поведения всей системы в целом, эти комплексы рассматриваются как единое целое – без учёта их внутренней структуры и поведения. Комплексы на уровне подобного анализа обладают кроме основных своих характеристик, связанных с выполнением возложенных на них функций, ещё и некоторыми специфичными параметрами – такими как время восстановления после сбоя, время наработки на отказ, среднее время внепланового останова, вероятность возникновения сбоя и т.п. Эти параметры различны для разных комплексов и систем и отражают требования по надёжности для всей системы в целом.
В качестве одного из таких компонентов может рассматриваться СУБД. Практически любая система требует ведения базы данных в том или ином виде. Большинство из них используют ту или иную СУБД для хранения и обработки своих данных. В результате, для критически важных приложений возникает необходимость определения параметров СУБД по надёжности и наложения на них дополнительных требований на них.
Но прежде, чем пытаться рассматривать необходимые параметры и требования, нужно определиться с целями и задачами, которые их накладывают. Если говорить обобщённо, то основной целью является снижение времени простоя СУБД до нуля. При этом под временем простоя может пониматься и серьёзная деградация производительности СУБД при условии, что она (деградация производительности) превышает определённый порог, специфичный для каждой информационной системы.
Рассмотрим основные факторы, которые приводят к простою СУБД.
Во-первых, отказ оборудования. Здесь следует учитывать, что отказ может быть полный или частичный. Если с полным отказом всё более или менее ясно, то о частичном следует сказать особо. При частичном отказе может возникнуть или частичный отказ СУБД или серьёзное (возможно, неприемлемое для системы в целом) снижение производительности СУБД. Следует также учитывать, что в эти отказы может входить не только отказ оборудования, на котором работает собственно сервер баз данных, но и отказ телекоммуникационного оборудования, связывающего СУБД с клиентскими (или вспомогательными) задачами.
Во-вторых, отказ собственно кода СУБД, вызванный (в конечном итоге) ошибками программирования. При этом практически не важна причина, вызвавшая этот сбой – предпринятая атака на сервер с использованием недокументированных возможностей, посылка пользователем специфических данных или запроса, общая перегрузка.
В-третьих, нехватка ресурсов сервера – или сервера БД или комплекса, на котором он функционирует. При этом опять же не важно, чем вызвана эти нехватка – неправильным проектированием, расчётами, целенаправленной атакой или другими причинами.
В-четвёртых, отказ операционной системы или окружения. Причины этого отказа могут быть теми же, что и причины отказа кода СУБД, а могут быть связаны с ошибками проектирования системы в целом.
В-пятых, необходимость проведения планового обновления, модернизации или обслуживания оборудования, операционной системы, вспомогательного программного обеспечения или собственно кода СУБД.
В-шестых, серьёзная авария, связанная с локальной аварией на территории, где расположено оборудование (например, пожар) или с более или менее крупной катастрофой, например, землетрясением, - вызвавшими физическую невозможность выполнять необходимые функции в течение длительного времени (или навсегда). Системы, которые позволяют продолжать в таких условиях обработку информации без заметных последствий для пользователя, обычно называют «катастрофоустойчивыми системами».
Ниже будут подробнее рассмотрены эти ситуации и средства борьбы с ними. А пока, если исходить из того, что простои будут иметь место, то следует рассмотреть вопрос о том, как свести их время к минимуму. И можно ли вообще обеспечить пользователям непрерывную работу их приложений в случаях простоя СУБД по тем или иным причинам.
Каким же образом время простоя может быть сведено к минимуму? Сразу приходит в голову идея о необходимости 100% резервирования оборудования, данных и исполняемых (и исполняющихся) программ. В этом случае две системы, выполняющиеся абсолютно параллельно, могут (в случае сбоя одной из них) практически одновременно переключить управление с отказавшего сервера на другой. При этом, разумеется, требуется специальное ПО или оборудование, которое будет заниматься определением подобного сбоя и перенастраивать информационные потоки по-новому, или клиентские компоненты должны «знать» о наличии двух серверов и поддерживать одновременно 2 соединения под видом одного.
Однако в этой ситуации есть несколько подводных камней.
Первый – основная и дублирующая системы не будут работать абсолютно одинаково. У них могут немного отличаться условия – например, могут отличаться электронные компоненты вычислительной системы, в результате чего может накапливаться ошибка по времени или при параллельном исполнении квантование на уровне операционной системы (или ядра СУБД) будет происходить по-разному. Это, конечно, совсем небольшое различие в каждый конкретный момент времени, но системы уже не будут абсолютно идентичны, и это расхождение может привести к расхождению в результатах после «подмены» одной системы другой.
Второй – в случае, если произошёл программный сбой, связанный с ошибкой программирования, то при такой схеме, скорее всего он произойдёт и на втором узле. Поэтому оба узла откажут одновременно.
Третий – при введении в строй восстановленного узла будет выполняться длительная и очень неприятная процедура полного восстановления среды сервера по основному на тот момент. При этом, если они не будут иметь высокоскоростного канала связи, то этот процесс может быть практически бесконечным.
Таким образом, данное решение может частично снизить актуальность проблемы отказа оборудования, но совсем не решит проблем программного отказа.
Вторым вариантом организации отказоустойчивой (и даже катастрофоустойчивой) СУБД будет применение тех же понятий к операционной системе. То есть если использовать для функционирования СУБД операционную систему (и аппаратную среду), которая гарантирует отказоустойчивость (в определённой мере), то в случае, если сама СУБД обеспечивает отказоустойчивость на своём уровне (т.е. не содержит программных ошибок), то получившаяся система будет отказоустойчивой. В случае аппаратных или программных сбоев уже операционная система гарантирует возможность автоматической замены отказавших компонент и перезапуска (с восстановлением) отказавших программных компонент, включая и саму себя.
В этом случае операционная система возьмёт на себя ответственность за адекватное состояние базы данных после возможного переключения нагрузки в случае серьёзных отказов или катастроф. В этом направлении развиваются программно-аппаратные комплексы компании IBM.
Ещё один вариант организации отказоустойчивой системы СУБД будет организация схемы с разделяемыми данными. Этот вариант характеризуется наличием отдельной компоненты – отказоустойчивого хранилища информации. Аппаратная реализация такого хранилища может быть произвольной, но для СУБД она выступает как гарантированно непрерывно функционирующая единица, к которой подключены одна или несколько ЭВМ. В этом случае резервный (один или несколько) сервер находится всё время в ждущем режиме, но имеет доступ ко всем данным основного (одного или нескольких) сервера. В случае обнаружения сбоя на основном сервере, он инициирует запуск на исполнение копии СУБД. При этом запущенная СУБД должна получить информацию о том, что она подхватывает работу «упавшего» основного сервера, соответствующим образом «подхватить» его БД и обработать её. Далее могут быть два сценария развития событий. При одном из них с БД (или другим способом) должна передаваться информация о текущих соединениях и состоянии этих соединений, и в этом случае резервный сервер может полностью восстановить обработку запросов с точки падения основного. При другом – откатываются все незавершённые транзакции (и, возможно, закрываются каналы связи с СУБД от пользователей). Затем запросы переназначаются на новый сервер. В этом случае пользовательское приложение должно будет или перезапустить транзакции, или заново открыть соединение и тоже перезапустить транзакции.
Частным случаем такой схемы может являться использование резервного сервера вместе с основным для обработки запросов. Т.е. получается кластерное решение, при котором СУБД выполняется на нескольких узлах кластера и, в случае отказа одного из узлов, перераспределяет потоки запросов на работоспособные узлы кластера.
Четвёртым вариантом реализации отказоустойчивой системы является решение подобное инкрементному backup. В этом случае в некий момент времени на резервном узле получается копия БД и затем, посредством специальных механизмов, все последующие изменения этой БД. Причём эта операция упрощается тем, что эта информация присутствует практически всегда в СУБД и используется в подобных механизмах «доката» транзакций или инкрементного backup. Такая реализация, с одной стороны, не требует особенных затрат, а с другой она не лишена своих недостатков.
Недостатками такой системы могут являться следующие: во-первых, дублируется ЭВМ, которая фактически простаивает (с точки зрения обработки запросов СУБД) при работоспособном состоянии второго сервера. Во-вторых, при инициализации резервного сервера (например, при вводе его в строй после отказа), требуется перекачка на него всей базы данных. И на протяжении всего этого времени система будет функционировать без резервирования. В-третьих, для гарантии целостности информации на резервном узле в любой момент времени требуется, чтобы фактически выполнялся двухфазный commit для пользовательских запросов и для информации, передаваемой на резервный сервер. То есть в случае, если пользователь получает ответ на commit, то эта информация гарантированно должна попасть на резервный сервер. В противном случае несколько последних завершённых транзакций откатятся (или потеряются) при переключении на резервный сервер.
На основе подобной технологии можно реализовать схему с отсутствующей второй ЭВМ. В этом случае роль резервного сервера должно выполнять устройство долговременной памяти (например, магнитная лента) в паре с устройством FLASH-памяти, на которое сбрасываются данные о проводимых в БД изменениях.
При такой реализации на ленту архивируется первоначальное состояние БД и фрагменты журналов БД, содержащие изменения для неё. При функционировании СУБД, фрагменты её журналов должны обязательно помещаться (одновременно с выдачей commit пользователю) в энергонезависимую оперативную память - благо, это достаточно быстрая операция. Параллельно должна выполняться задача, которая сбрасывает данные с flash на ленту.
В случае сбоя такой системы, после замены оборудования (если это необходимо), данные будут восстановлены с ленты, затем, поверх, будут дописаны данные из журналов транзакций, которые не успели туда записаться (из flash), и будет запущена процедура восстановления СУБД.
Эта система позволяет избавиться от необходимости постоянно держать вторую машину и от проблем, связанных с необходимостью гарантировать доставку данных (модификаций БД) на удалённый узел. Эти данные будут размещаться локально и в памяти, что позволит обрабатывать пиковые нагрузки на СУБД без дополнительной синхронизации с СУБД на удалённом сервере.
Однако, имея достоинства, подобная схема имеет и свои недостатки. Основным, пожалуй, следует признать необходимость, в случае сбоя, как минимум, восстанавливать базу данных с ленты и, затем, докатывать по журналу (который может быть достаточно большим) все необходимые модификации. При этом, разумеется, потеряются все соединения с СУБД, и клиентским приложениям придётся их заново инициировать.
Существует и ещё одна не вполне очевидная проблема – большое время восстановления в случае, если журналов накопится достаточно много. Для решения этой проблемы можно применять параллельно две операции архивирования со сдвигом по времени, – периодически меняя точки начала архивирования.
В 1994 у одного из подразделений ВНИИРА, занимающегося разработкой систем управления полётами для аэропортов, возникла необходимость в использовании СУБД для реализации современной системы управления полётами – взамен морально и технически устаревших систем. Спецификой их задачи являлось то, что она разрабатывалась в операционной системе реального времени OS9000 и задачам требовалась надёжность близкая к 100% с предсказуемым поведением во всех ситуациях – за исключением катастроф (что, в общем-то, логично – в случае катастрофы самолётам просто некуда будет садиться).
Для решения своей проблемы специалисты ВНИИРА обратились к нашей компании. Это положило основу нашему долговременному сотрудничеству и реализации подсистемы «горячего резервирования» СУБД ЛИНТЕР
Специально для этой задачи нами была разработана диагностическая программа-контроллер, которая позволяет управлять программно-аппаратным комплексом из двух СУБД, функционирующих на различных ЭВМ, соединённых между собой и с клиентами двумя независимыми каналами связи. Остальные компоненты СУБД ЛИНТЕР были модифицированы для того, чтобы обеспечивать необходимые параметры по скорости и надёжности. Кроме того, была реализована специальная поддержка некоторых режимов управления таким двухмашинным комплексом.
В результате в 1999 году в аэропорту Хабаровска после двух лет опытной эксплуатации была запущена первая система управления полётами на основе СУБД ЛИНТЕР в режиме «горячего резервирования».
При решении этой задачи мы исходили из нескольких предпосылок. Первой был выбор операционной системы – это операционная система реального времени, которая имеет предсказуемые характеристики по любым параметрам. В частности, наших заказчиков интересовали такие параметры, как надёжность файловой системы, гарантированное время отклика на событие и время перезагрузки ОС.
К СУБД, функционирующей в системе, были предъявлены следующие требования: время перезапуска после сбоя или после переключения – не более 15-20 секунд, возможность прозрачного доступа для клиентов по двум физически независимым сетям, время определения «смерти» СУБД (или машины) – 2 сек, обязательная идентичность всех баз данных (не допускались ситуации, при которых части транзакций или даже некоторые последние транзакции целиком отсутствовали бы на резервном узле после переключения). Кроме того, не допускались блокировки задачи на операциях с БД – все запросы к БД должны были выполняться асинхронно.
Задача осложнялась тем, что эта операционная система не могла использовать внешние общие устройства хранения информации по нескольким причинам – от отсутствия соответствующих драйверов для этой ОС до ограничения на стоимость аппаратных средств.
Кластерная архитектура тоже не поддерживалась целевой операционной системой.
В результате оставалось два пути – или попытаться реализовать полное синхронное выполнение двумя базами данных всех запросов (и подтверждать результаты, выданные одной базой, результатами другой), или реализовать систему на основе передачи модификаций базы данных (репликации журналов).
Первоначально мы шли параллельно по двум направлениям. Однако на первом из них мы натолкнулись на достаточно серьёзные проблемы при реализации синхронного исполнения активных компонентов СУБД – триггеров и хранимых процедур. Кроме того, обнаружились проблемы при первоначальной синхронизации и синхронизации после сбоя – синхронизация выполнялась, но БД уже не были полностью идентичны, что в общем случае приводило к конфликтам на клиентской части – результаты выполнения запросов разными серверами различались. В итоге мы получили рабочий вариант, но он не удовлетворял по скорости стартовой синхронизации и давал достаточно большое количество конфликтов на клиенте.
Второе направление оказалось более перспективным. В результате была получена готовая система, которая была внедрена сначала в опытную, а, по её успешному завершению, в промышленную. Унификация механизмов поддержки «горячего резерва» позволила перенести эту систему на платформу UNIX, и к настоящему времени эта система уже встроена в несколько продуктов, которые поставляются на объекты, требующие повышенной надёжности и отказоустойчивости. В частности, данная система уде второй год используется в международном аэропорту Минска.
Рассмотрим особенности реализации системы горячего резервирования СУБД. Существует 4 звена на пути от клиента к БД. Клиентская задача, клиентский сетевой драйвер, серверный сетевой драйвер и собственно ядро СУБД. Отдельно от этой цепочки стоит контроллер системы горячего резерва.
Функциональное назначение контроллера состоит в том, что он общается с другим таким же контроллером на второй машине и управляет всеми процессами ЛИНТЕР на сервере. Он может перезапускать отказавшие процессы, обнаруживать ситуации «зацикливания» компонентов СУБД и перезапускать их принудительно, выполнять команды администратора – например, команду на переключение серверов, на останов одного из серверов и т.п. Этот же контроллер разрешает конфликты между серверами – например, в случае если система была полностью остановлена, а затем запускается, то он определяет, какой сервер содержит более свежую информацию, и переключает его в режим основного, а второй - в режим резервного.
Администратор системы может принудительно вывести на профилактику один из серверов. При этом на сервере может быть обновлено программное обеспечение, заменено оборудование или проведены другие работы. После подключения его обратно, контроллер основного сервера обнаружит появление нового сервера и установит его в режим резервного.
Клиентская задача не имеет информации о том, какой из серверов в данный момент активен – к каком ей необходимо устанавливать соединение и т.п. Она просто обращается к БД. В случае возникновения сбоя (или программного переключения серверов) все запросы пользователей (в том числе и асинхронные) завершаются с определённым кодом завершения. Затем на протяжении времени переключения задача получает код, извещающий её о том, что идёт переключение. После переключения следующий запрос открывает соединение и появляется возможность повторить транзакцию, при выполнении которой возникла ошибка. Время подобного переключения составляет примерно 5-30 секунд в зависимости от операции.
Сетевой драйвер клиента тоже не знает о том, что существуют 2 сервера, которые работают в горячем резерве. И не знает о нескольких физических сетях доступа к удалённым серверам. Он имеет только список сетевых адресов одного «виртуального» сервера, который прозрачно будет обслуживать все запросы пользователей. Часть из этих адресов может относиться к одному физическому серверу и разным линиям связи с ним, а часть - ко второму.
Драйвер сервера вообще не знает ничего о том, что у него особенный режим исполнения – он работает в обычном режиме, передавая запросы от клиентов серверам. Однако он обладает группой дополнительных свойств, связанных с анализом «живости» ядра и соединений, посредством которых с ним связываются клиенты. Драйвера клиента и сервера отслеживают функционирование сети в обе стороны клиент отслеживает сервер, а сервер – клиент). Параллельно отслеживается ситуация «зацикливания» или «падение» ядра СУБД. В случае обнаружения одного из таких состояний соединение разрывается и соответствующая информация доставляется контроллеру комплекса, который должен проанализировать состояние других компонент и принять решение или о перезапуске ядра СУБД, или о подаче сигнала администратору об отказе одной из линий связи, или о переключении нагрузки на резервный сервер.
Ядро СУБД кроме выполнения своих основных функций выполняет самодиагностику и операции «доката» по журналу транзакций модификаций из другой БД. Соответственно, оно контролирует функционирование родительского процесса-контроллера. В случае сбоя последнего – его обязанность немедленно завершиться, остановив все текущие на данный момент транзакции. Это будет означать, что резервный узел должен взять на себя основную нагрузку.
На резервной машине ядро СУБД постоянно функционирует в режиме «доката» транзакций, получаемых от основного сервера.
Получившаяся система имеет ряд недостатков. Основным из них является как обычно время первичной синхронизации – для проведения такой синхронизации необходимо перекачать на резервную машину всю БД. Это достаточно большой промежуток времени, в который система работает без резервного сервера, и сбой в этот момент может привести к останову всей системы.
Как второй недостаток может рассматриваться необходимость синхронизировать передачу данных журналов на диск основного сервера и резервному серверу. Это несколько замедляет работу модифицирующих транзакций.
Развитие данной системы мы видим в реализации её на основе разделяемых дисковых массивов. Это позволит решить проблему, связанную с передачей БД на резервный сервер. Однако это не значит, что текущая схема будет забыта – с её помощью получается очень дешёвое и достаточно простое средство поддержки режима высокой готовности СУБД ЛИНТЕР.