Организация кластера (постановка)

Материал из GedeminWiki
Перейти к: навигация, поиск

При увеличении размера базы данных производительность сервера вполне прогнозируемо снижается. Использование многоядерных и/или многопроцессорных серверных архитектур мало помогает в решении данной проблемы, так как основным узким местом СУБД Firebird является обмен данными с жестким диском. Сервер классической архитектуры позволяет загрузить несколько ядер/процессоров одновременно и в общем случае дает лучший отклик при большом количестве пользователей, но имеет существенный недостаток — маленький размер буфера записей. Так, на сервере с 4 Гб оперативной памяти, к которому одновременно подключается 60-70 пользователей, максимальный размер буфера не должен превышать 20 Мб, что ничтожно мало при работе с 8-10 гигабайтной базой данных. Суперсервер позволяет использовать кэш размером в 1 Гб и более, но выполняется только на одном процессоре. К тому же, падение серверного процесса обрывает все активные подключения. Фактически, у наших клиентов с базами в 5 Гб и более и количеством одновременных коннектов в 30-40 и выше, низкая скорость работы системы является одной из основных претензий.

Решение проблемы производительности может быть только комплексным и должно вовлекать:

  1. Использование современного, производительного аппаратного обеспечения для сервера, сетей и рабочих станций;
  2. Оптимизация структур данных и SQL запросов;
  3. Архивирование данных за пределами активного периода (закрытие периода) и удаление их из рабочей базы;
  4. Распределение нагрузки по нескольким физическим серверам. Использование распределенных баз данных.

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

Рассмотрим последний способ более подробно.

Содержание

Организация кластера

Итак, в сети с одним сервером клиент отсылает на него в среднем L запросов, где:

L = M + S

Здесь, M — среднее количество запросов на изменение информации (INSERT, UPDATE, DELETE, EXECUTE PROCEDURE), а S — среднее количество запросов на извлечение информации (SELECT, EXECUTE PROCEDURE).

Соответственно, сервер принимает и обрабатывает следующее количество запросов:

X = L * K, 

где K — число активных клиентов.

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

L` = M * N + S,

а i-й сервер кластера обрабатывает в среднем:

X` = M + S / N

запросов.

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

Оценим преимущества и недостатки предложенной схемы кластеризации.

Недостатки:

  1. Повышение загрузки сети из-за увеличения количества запросов на изменение данных в N раз.
  2. Увеличение временных затрат в клиентском приложении на изменение информации. Количество запросов на изменение увеличится в N раз, но время выполнения одного запроса будет меньше, чем в сети с одним сервером, так как каждый из серверов кластера будет менее загружен. Т.е. можно сказать, что время обновления информации в клиентском приложении подчиняется неравенству: T < T` < T * N, где T — время выполнения запроса в сети с одним сервером, а T` — в кластере.
  3. Увеличение расходов на администрирование N серверов.

Преимущества:

  1. Увеличение быстродействия при выполнении запросов на извлечение информации за счет распределения их по серверам кластера.
  2. Уменьшение риска потери данных вследствие выхода из строя серверного железа. Можно увеличить период архивирования базы, так как архивная копия теперь понадобится только для восстановления информации из-за логических сбоев в программе, некомпетентных или злоумышленных действий пользователей.
  3. Увеличение общей надежности системы. При выходе из строя одного из серверов кластера — остальные примут на себя его нагрузку.
  4. Уменьшение стоимости серверного железа. Четыре однопроцессорных сервера в конфигурации: 4 Гб ОЗУ, один SATA 2 винчестер. Будут стоить дешевле, чем один четырех процессорный сервер с 16 Гб памяти и RAID массивом из четырех дисков.
  5. На практике, в конфигурации сети с одним сервером его часто приходится перезагружать либо из-за исчерпания процессом доступной памяти, либо из-за «подвисания» на длительных запросах. И первое, и второе, как правило, порождается длительными запросами на извлечение данных. При этом если отключен режим принудительной записи, то возможно повреждение файла базы данных. В кластере, нагрузка по SELECT запросам на один сервер снижается. Следовательно, перезагрузок можно избежать, а режим принудительной записи деактивировать, что приведет к ускорению работы. Конечно, если каждый сервер в кластере снабжен надежным источником бесперебойного питания.

Требования к реализации

Реализация кластера должна быть абсолютно прозрачной и не затрагивать существующий прикладной программный код. Свойства gdcBaseManager.Database и IBLogin.Database будут содержать подключение к главной базе данных. На этой же базе будет открыта глобальная транзакция gdcBaseManager.ReadTransaction.

Список активных подключений к серверам кластера будет находиться в объекте IBLogin. Параллельное выполнение модифицирующих запросов на всех базах кластера будет вестись незаметно для программиста, на уровне компонента TIBSQL по следующему алгоритму:

  1. При вызове метода ExecQuery выполняется запрос.
  2. Если выполнение прошло успешно и это модифицирующий запрос и у подключенной транзакции в списке несколько баз данных, то запрос выполняется в цикле для каждой из баз. Если в процессе выполнения на одной из баз произошла ошибка, смотрим на ее тип:
    1. Потеряно соединение к базе. Исключаем эту базу из списка и во всех оставшихся базах помечаем ее как неактивную. В дальнейшем, потребуется вмешательство системного администратора для копирования на деактивированный сервер актуальной базы данных и активирование его. Продолжаем выполнение запроса по списку баз.
    2. Иная ошибка. Для последующих баз запрос не выполняется. Если успели выполнить запрос на более чем одной базе, то откатываем транзакцию по RollbackRetaining.

Модифицирующим считается запрос на вставку, изменение или удаление данных, запрос на выполнение хранимой процедуры, а также SELECT запрос, в котором присутствует обращение к хранимой процедуре, и который после своего выполнения имеет свойство RowsAffected > 0. Обязательное условие модифицирующего запроса — выполнение на транзакции, которая может менять данные, т.е. не имеет параметра read.

Кэширование подготовленных запросов следует адаптировать к работе с несколькими базами одновременно.

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

Автоматическое заполнение списка баз данных для компонента TIBTransaction будет происходить в процессе ее старта, если данный список пуст, базой по умолчанию (DefaultDatabase) для транзакции указана текущая база данных и в параметрах транзакции не указан атрибут «только для чтения». В дальнейшем, следует запретить изменение списка баз данных транзакции извне, если он был сформирован автоматически.

При создании новой учетной записи сервера Firebird на одном из серверов кластера, ее следует продублировать на остальных активных серверах. Соответствующие изменения должны быть внесены в код бизнес-класса TgdcUser.

Хранение списка серверов кластера

Список серверов хранится в базе данных в таблице gd_cluster_server, которая имеет следующую структуру:

Имя поля Тип данных Описание
id dintkey Первичный ключ.
name dname Полное имя базы данных в формате server:full_database_path или server:alias_name.
usergroups INTEGER NOT NULL Битовая маска групп пользователей, которые будут использовать этот сервер. Таким образом можно назначать конкретные сервера, конкретным группам пользователей.
disabled ddisabled Флаг неактивного сервера. Сервер помечается как неактивный, если к нему не удалось подключиться в момент запуска программы или в процессе работы программы оборвалось соединение с этим сервером. Перевести сервер в активное состояние может только системный администратор.

Подключение к кластеру

Существующий алгоритм подключения Гедымина к базе данных содержит следующие шаги:

  1. Считать имя сервера из реестра или командной строки.
  2. Подключиться под учетной записью STARTUSER.
    1. Если произошла ошибка при подключении, то предложить пользователю выбрать другую базу из списка, или войти в программу без подключения, или завершить выполнение программы.
  3. Запросить имя пользователя и пароль. Передать их в процедуру GD_P_SEC_LOGINUSER.
    1. Если пароль или имя пользователя введены неверно, то вернуться к шагу 3.
  4. Получить рабочую учетную запись сервера и ее пароль.
  5. Переподключиться к базе данных под ролью Administrator, используя полученную учетную запись и пароль.

Изменим процедуру GD_P_SEC_LOGINUSER. Добавим два исходящих строковых параметра: ClusterServers и PrefClusterServers. Первый будет содержать список активных серверов кластера (через запятую), а второй — список предпочтительных для подключения серверов.

После проверки имени пользователя, его пароля и прочих параметров хранимая процедура GD_P_SEC_LOGINUSER будет извлекать из таблицы gd_cluster_server список серверов следующим запросом:

SELECT 
  name, BIN_AND(usergroups, InGroup) 
FROM 
  gd_cluster_server 
WHERE 
  disabled = 0 

где InGroup — битовая маска групп, в которые входит пользователь.

Новый алгоритм будет выглядеть следующим образом:

  1. Считать имя сервера из реестра или командной строки.
  2. Подключиться под учетной записью STARTUSER.
    1. Если произошла ошибка при подключении, то предложить пользователю выбрать другую базу из списка, или войти в программу без подключения, или завершить выполнение программы.
  3. Запросить имя пользователя и пароль. Передать их в процедуру GD_P_SEC_LOGINUSER.
    1. Если пароль или имя пользователя введены неверно, то вернуться к шагу 3.
  4. Получить рабочую учетную запись сервера и ее пароль.
  5. Получить список активных серверов кластера и список предпочтительных серверов.
  6. Определить имя главного сервера для подключения:
    1. Если список предпочтительных серверов не пуст, то выбираем из него случайным образом;
    2. Если список предпочтительных серверов пуст, то выбираем из списка активных серверов случайным образом;
    3. Если список активных серверов пуст — в качестве главной базы принимаем текущую.
  7. Подключаемся (переподключаемся) к базе под ролью Administrator, используя полученную учетную запись и ее пароль.
    1. В том случае, если имя сервера изначально было взято из реестра и подключение прошло успешно, и имя сервера отличается от прописанного в реестре, то записываем его в реестр;
    2. Если произошла ошибка при подключении, то ждем некоторое время (2-3 минуты), после чего повторяем попытку, если повторное подключение не завершилось успехом, то помечаем базу как неактивную и возвращаемся к шагу 6.
  8. Устанавливаем подключения ко всем серверам в списке активных серверов кластера.
    1. Если при подключении произошла ошибка, то:
      1. Если пользователь не Admnistrator, то выдаем сообщение о том, что один из серверов кластера не доступен. Завершаем выполнение программы.
      2. Если ползователь Administrator, то ждем некоторое время (2-3 минуты), после чего повторяем попытку, если повторное подключение не завершилось успехом, то помечаем базу как неактивную.
    2. Проверяем, чтобы у всех баз был равный идентификатор.
  9. Если в процессе подключения выявлен хотя бы один неактивный сервер, то обновляем информацию о неактивных серверах во всех подключенных базах данных. В дальнейшем к этим серверам подключаться не будем. Потребуется вмешательство системного администратора для копирования на деактивированный сервер актуальной базы данных и активирование его.

Подготовка кластера к работе

Подготовка кластера к работе осуществляется следующим образом:

  1. На каждый из серверов кластера устанавливается сервер Firebird.
  2. На одном из серверов разворачивается база данных. Создаются учетные записи пользователей сервера Firebird.
  3. Заполняется таблица gd_cluster_server.
  4. База данных и база с учетными записями пользователей копируются на остальные серверы кластера.

Перевод неактивной базы в активное состояние

  1. Завершается работа всех клиентских приложений.
  2. За основу берется один из активных серверов. На нем редактируется таблица gd_cluster_server. Все сервера помечаются как активные.
  3. База данных и, при необходимости, база с учетными записями пользователей копируются на остальные серверы кластера.
Персональные инструменты
Пространства имён

Варианты
Действия
Навигация
Инструменты