Привет, меня зовут Владимир Сердюк. Я основатель компании SOFTPOINT и этой статьей хочу открыть цикл, посвященный распределенным кластерам СУБД с возможностью равномерного распределения нагрузки по всем его серверам.
Идеи создания распределенного вычислительного кластера СУБД (далее РВК) посещали меня достаточно давно. Если упрощенно описать, то программное обеспечение РВК позволяет объединить множество серверов в один суперсервер (кластер), осуществляющий равномерную балансировку всех запросов между отдельными серверами. При этом для приложения, которое работает на РВК все будет выглядеть как будто оно работает с одним сервером и одной базой данных (далее БД), это будут не разрозненные базы данных на распределенных серверах, а как будто одна виртуальная. Все сетевые протоколы, репликационные обмены, прокси-перенаправления будут скрыты внутри РВК. При этом будут эффективно и равномерно использоваться все ресурсы распределенных серверов, в частности, оперативная память и процессорное время.
Например, в облачном ЦОДе можно взять один физический суперсервер и нарезать его на множество виртуальных серверов СУБД. Но обратная процедура до настоящего момента была невозможна, т.е. нельзя взять много физических серверов и объединить их в один виртуальный суперсервер СУБД. В определенном смысле РВК это технология позволяющая объединять физические сервера в один виртуальный суперсервер СУБД.
Позволю еще одно сравнение, РВК – это все равно, что когерентная NUMA-технология, только для объединения SQL серверов. Но в РВК, в отличии от NUMA, за синхронизацию (когерентность) данных и частично оперативной памяти отвечает программное обеспечение, а не контроллер.
Для наглядности приведу схему привычного всем подключения клиентского приложения к серверу СУБД и сразу же схему с РВК. Обе схемы упрощенные, просто для понимания.
Идея кластера – децентрализованная модель. На рисунке выше proxy-сервер всего один, но в общем случае их может быть несколько.
Результатом этого решения станет возможность на порядок повысить масштабируемость СУБД относительно типичного односерверного решения с самым мощным сервером на текущий момент. Такого решения на данный момент не существует, ну или, по крайней мере, в моем большом и профессиональном сообществе никто о подобном не знает.
После пяти лет исследований я детально проработал логическую архитектуру и протоколы взаимодействия и с помощью небольшой группы разработки создал рабочий прототип, который проходит нагрузочные тесты на популярной ИТ-системе 1С8.х под управлением СУБД PostgreSQL. СУБД может быть и MS SQL, и Oracle. Принципиально выбор СУБД не влияет на те идеи, что я приведу.
Это статьей я открываю целый цикл статей, посвященных РВК, в которых постепенно буду раскрывать ту или иную проблематику и предлагать по ним решения. К такой схеме я пришел после выступления на одной из IT-конференций, где тему эту признали достаточно сложной для понимания. Первая статья будет вводная, я пройдусь вкратце по верхам, сделав акцент на неочевидные утверждения, и расскажу, что будет в следующих публикациях.
Задача построения РВК оказалась значительно более ресурсоемкая, чем я планировал изначально. Поэтому целью цикла статей будет в том числе поиск стратегического и технологического партнерства для развития и оптимизации РВК. Также интересны клиенты для первых внедрений этого продукта, который на данный момент не имеет аналогов.
На тему партнерства пишите на адрес rvkpartner@softpoint.ru с комментарием «Партнерство по РВК».
На тему интереса внедрения продукта пишите на rvksales@softpoint.ru с комментарием «Внедрение РВК».
Оглавление
Для каких ИТ систем эффективен РВК
Стратегическая область применения
Почему нельзя для РВК использовать транзакционную репликацию
Зачем нужен централизованный протокол в РВК
Механизмы обеспечения целостности и синхронности распределенных данных на уровне транзакций в РВК
Проблема распределенных деадлоков в РВК
Специфика администрирования РВК
Технологии параллельных вычислений как средство, повышающее эффективность использования РВК
Для каких ИТ систем эффективен РВК
Идея РВК заключается в создании специальной программной оболочки, которая будет все запросы на запись выполнять одновременно и синхронно на всех серверах, а запросы на чтение будет выполнять на конкретной ноде (сервере) с привязкой к пользователю. Другими словами, пользователи будут равномерно распределены по серверам кластера: запросы на чтение будут выполнятся локально на сервере, к которому привязан пользователь, а запросы на изменение будут синхронно выполнятся одновременно на всех серверах (за счет этого не будет нарушений в логике).
Таким образом, при условии, что запросы на чтение значительно превосходят по нагрузке запросы на запись, мы получаем приблизительно равномерное распределение нагрузки по серверам (нодам) РВК.
Давайте сначала порассуждаем, а справедливо ли утверждение, что нагрузка запросов на чтение значительно превосходит нагрузку запросов на запись? Для ответа на этот вопрос необходимо немного обратится к истории создания SQL языка. Что являлось целью и что в итоге получилось.
Изначально SQL планировался как язык, которым смогут пользоваться без навыков программирования и математики.
Вот выдержка из википедии:
Кодд использовал символическую запись с математическими обозначениями операций, но Чемберлин и Бойс захотели спроектировать язык так, чтобы им мог воспользоваться любой пользователь, даже не имеющий навыков программирования и знаний математики[5].
На текущий момент можно утверждать, что навыки программирования для SQL все-таки нужны, но определенно минимальные. Большинство программистов изучали основы оптимизации запросов по верхам и того же Дэна Тоу «Настройка sql для профессионалов» в глаза не видели. Много логики по оптимизации запросов скрыто внутри СУБД. Раньше, например, в MS SQL было ограничение в 256 объединений таблиц, сейчас в современных ИТ системах не редкость когда в запросе тысячи объединений. Широко и иногда слишком бездумно используется dynamic SQL, когда запрос формируется динамически. Поверьте, не существует математически точной модели построения оптимального плана выполнения сложного запроса. Эта задача чем-то похожа на задачу коммивояжёра и считается, что она не имеет точного математического решения.
Вывод такой. SQL запросы эволюционно доказали свою эффективность и практически вся отчетность формируется на SQL запросах. Чего не скажешь про бизнес-логику и транзакционную логику. Любой из SQL языков оказался не очень удобным с точки зрения программирования сложной транзакционной логики. Он не поддерживает объектно-ориентированное программирование, имеет весьма неудобные логические конструкции. Поэтому можно сказать, что программирование разделилось на две составляющие. Написание разнообразных отчетов-запросов SQL, получение данных на клиента или сервер приложения и реализация остальной логики на прикладном языке приложения (не важно двухзвенка или трехзвенка). С точки зрения нагрузки на СУБД это выглядит как тяжелая SQL-конструкция на чтение и потом много маленьких на изменение.
Давайте теперь рассмотрим вопрос распределения нагрузки запросов Чтение-Запись на сервере СУБД по времени. Для начала нужно определится что значит нагрузка и как ее можно измерять? Под нагрузкой будем понимать (в порядке приоритета описания): CPU (процессорная нагрузка), используемая оперативная память, нагрузка на дисковую подсистему. Основным ресурсом с точки зрения нагрузки будет CPU. Возьмем абстрактную OLTP систему и все SQL вызовы из множества параллельных потоков разобьём на две категории Чтение и Запись. Далее на основании средств мониторинга производительности, отобразим на графике интегральное значение, например CPU. Если усреднить значение хотя бы на 30 секунд, мы увидим, что значение графика «Чтение» в десятки, а то и сотни раз выше, чем значение графика «Запись».
Это обусловлено тем, что в единицу времени большее количество пользователей может запустить отчеты или макротранзакции, использующие тяжелые SQL конструкции, на чтение. Да, конечно, возможны задачи, когда система регулярно подгружает данные из очередей репликации, из внешних систем, запускаются регламентные процедуры закрытия периодов, запускаются процедуры бэкапирования. Но на основании нашей многолетней статистики (SOFTPOINT уже 20+ лет занимается решением задач производительности и масштабируемости баз данных) для подавляющего количества ИТ-систем нагрузка SQL-конструкций на Чтение превышает нагрузку на Запись в десятки раз. Разумеется, могут быть исключения, например, биллинговые системы, в которых факт изменений фиксируется без какой-либо сложной логики и отчётности, но это легко проверить специализированным ПО и понять, насколько РВК будет эффективен для ИТ системы.
Стратегическая область применения
РВК на данный момент будет полезен, а, возможно, и жизненно необходим для крупных компаний с большими информационными потоками и мощной аналитической составляющей. Например, для крупнейших банков. С помощью относительно небольших серверов можно составить РВК, который по мощности будет значительно опережать все существующие суперЭВМ. Разумеется, не будет одних плюсов. Минусом будет усложняющаяся часть администрирования распределённой системы и фиксированное замедление транзакций. Да, к сожалению, сетевые протоколы и логические схемы, которые использует РВК приводят к замедлению выполнения транзакций. На текущий момент целевым параметром является замедление транзакции не более 15% по времени. Но еще раз повторю, что при этом система станет гораздо более масштабируемой, все пиковые нагрузки будут проходить без проблем и в среднем то же время транзакции будет меньше чем в случае использования одного сервера. Поэтому если система не испытывает проблем с пиковыми нагрузками и стратегически этого не предвидится, РВК не будет эффективен. В дальнейшем РВК после автоматизации административных процессов и оптимизации станет возможно также эффективен для средних компаний, потому как можно будет собирать мощный кластер даже из ПК с ssd (быстрыми, ненадежными и дешевыми) дисками. Его распределенная структура позволит без проблем отключать вышедший из строя ПК и на лету подключать новый. Система РВК контроля транзакций не даст некорректно записать данные. Также нельзя игнорировать геополитику, к примеру в случае отсутствия доступа к мощным серверам, РВК позволит собрать мощный кластер на базе серверов отечественных производителей.
Почему нельзя для РВК использовать транзакционную репликацию
Данный раздел требует подробного описания, и я его опишу отдельной статьей. Здесь укажу лишь тезисно о проблемах.
Многие разработчики приложений, используя СУБД, даже не задумываются о том какие конфликты обращения к данным внутри движка решает система. Например, нельзя настроить транзакционную репликацию, добиться синхронности данных на множестве серверов и сказать, что это кластер СУБД. Данное решение не будет решать конфликт одновременного обращения к записи типа «Писатель-Писатель». Подобные коллизии непременно приведут к нарушению логики поведения системы. Также существующие протоколы транзакционной репликации обладают большими издержками, подобная система будет очень сильно проигрывать односерверному варианту.
Итого транзакционная репликация не подходит для РВК потому, что:
1. Слишком большие издержки типовых протоколов репликации синхронной транзакцией.
Типовые протоколы распределенной транзакции имеют слишком много дополнительных, в первую очередь временнЫх, сетевых издержек. На один сетевой вызов получается дополнительно до трех вызовов. В подобном виде происходит серьезнейшая деградация простейших атомарных операций.
2. Не решается конфликт типа Писатель-Писатель
Конфликт возникает при одновременном изменении одних и тех же данных в разных транзакциях. По факту прошедшего изменения система «помнит» только абсолютное последнее изменение (или историю). Смысл SQL конструкции для последовательного применения теряется. Подобные конфликты в репликации иногда вовсе не имеют решений.
В отдельной статье я приведу пример различных типов репликации для PostgreSQL и MSSQL, и объясню почему они архитектурно не могут решать проблему распределения транзакционной нагрузки. А также почему она не решается архитектурно на уровне железа.
Проблема Писатель-Писатель принципиально не решается без proxy-сервиса на логическом уровне анализа SQL-трафика приложения.
Механизмы (протоколы) обмена
Полное архитектурное описание РВК будет приведено в отдельной статье. А пока ограничимся кратким, чтобы обозначить проблематику.
Все запросы к СУБД в РВК проходят через прокси-сервис. К примеру, на системах 1С он может быть установлен на сервер приложения. Прокси сервис понимает какой тип запроса Чтение или Запись. И если Чтение, то отправляет его на привязанный к пользователю(сессии) сервер. Если тип запроса Изменение, то отправляет его на все сервера асинхронно. При этом к следующему запросу он не переходит, пока не дождется положительного ответа от всех серверов. Если произошла ошибка, то она транслируется в клиентское приложение и откатывается транзакция на всех серверах. Если все сервера подтвердили успешное выполнение SQL конструкции, то только после этого прокси обрабатывает следующий клиентский SQL запрос.
Именно при такой логике не возникает логических противоречий. Как видно, при такой схеме возникают дополнительные сетевые и логические издержки, хотя при должной оптимизации они минимальны (стремимся к не более 15% временной задержки транзакций).
Описанный выше алгоритм является основным протоколом, и мы его назовем зеркально- параллельным. К сожалению, этот протокол логически не для всех ИТ-систем может реализовать зеркально репликацию данных.
В некоторых случаях данные могут гарантированно отличаться в силу специфики системы, для этого реализован еще один протокол – «централизованный асинхронный», который гарантированно решает синхронную передачу информации. О нём в следующем разделе.
Зачем нужен централизованный протокол в РВК
К сожалению, в некоторых случаях, отправляя одну и ту же конструкцию на разные сервера, мы получаем гарантированно разные результаты. Например, в том случае, когда при вставке новой записи в таблицу первичный ключ формируется на основании GUID на серверной части. В этом случае на основании одного только определения мы гарантированно получим различные результаты на разных серверах. Как вариант, можно обучить экспертную систему прокси-сервиса понимать, что это поле формируется на сервере, формировать его явно на прокси и подставлять в текст запроса.
А что, если нельзя этого делать по какой-либо причине? Для решения подобных проблем вводится еще один протокол и сервер. Назовем его УсловноЦентральный. Далее будет понятно, что фактически центральным сервером он не является.
Алгоритм протокола заключается в следующем. Служба прокси-сервиса понимает, что SQL конструкция на изменение, скорее всего, приведет к различным результатам на разных серверах. Поэтому она сразу перенаправляет запрос на УсловноЦентральный сервер. Затем после его выполнения с помощью репликационных триггеров получает изменения, к которым этот запрос привел, и отправляет асинхронно все эти изменения на оставшиеся сервера. И далее переходит к выполнению следующей команды.
По аналогии с зеркально-параллельным протоколом, в случае если хотя бы на одном из серверов произошла ошибка, то она перенаправляется на клиент, и транзакция откатывается.
В этом протоколе вообще исключены какие-либо коллизии, данные всегда будут гарантированно синхронны, распределенных деадлоков также практически не будет. Но есть важный минус – в силу своей специфики протокол накладывает самые большие издержки по времени выполнения. Поэтому он будет использоваться только в исключительных случаях, иначе ни о каких целевых параметрах задержки не более 15 % и говорить не придётся.
Механизмы обеспечения целостности и синхронности распределенных данных на уровне транзакций в РВК
Как мы уже обсудили в прошлом разделе, существуют логические (например, NEWGUID) операции SQL на изменение, которые, выполняясь одновременно на различных серверах, гарантированно примут различные значения. Хорошо, давайте мы исключим всевозможные случайные функции и флуктуации. Предположим, у нас есть четкие арифметические процедуры, например,
UPDATE ТаблицаИтогов
SET Итог = Итог+Дельта
WHERE ИдТовара = Y.
Разумеется, выполненная такая конструкция в однопоточном варианте приведет к одному и тому же результату и данные будут синхронны, ведь законы математики никто еще не отменял. Но, если подобные конструкции выполнять в многопоточном режиме варьируя величину Дельта, то у нас запросто за счет нарушения хронологии выполнения запросов может возникнуть запутанность потоков. Которая приведет либо к дедлокам, либо к нарушению синхронности данных. Фактически может получится что результаты транзакций на разных серверах могут отличаться. Да, это будет, конечно, редко, и определенными действиями можно уменьшить эту вероятность, но полностью исключить ее нельзя, как, впрочем, без существенной просадки производительности и полностью решить. Таких алгоритмов в принципе не существует, как и не существует полностью синхронного времени для нескольких серверов, или сетевых запросов, гарантированно выполняющихся определенное время.
Поэтому в РВК есть служба управления распределенными транзакциями, и, в частности, обязательна проверка на хэш сумму транзакций. Почему хэш сумма? Потому что можно быстро по всем серверам сверить состав данных изменений. В случае если все совпадает, то транзакция подтверждается, если нет – откатывается с соответствующей ошибкой.
Подробности будут в отдельной статье. Там, кстати, в математике получаются интересные аналогии с квантовой механикой, в частности, с транзакционно-петлевой теорией (есть и такая маргинальная теория).
Проблема распределенных деадлоков в РВК
Эта проблема является одной из ключевых в РВК и с точки зрения рисков внедрения РВК является самой опасной. Связанно это с тем, что возникновение распределенных деадлоков в РВК является следствием спутанности потоков в силу изменения хронологии выполнения SQL запросов на различных серверах. Эта ситуация возникает из-за неравномерной нагрузки на серверы и сетевые интерфейсы. При этом, в отличии от локальных деадлоков, для возникновения которого нужно хотя бы два объекта блокировки, в распределенном может быть только один объект.
Для уменьшения распределенных деадлоков необходимо решить несколько технологических задач, одна из них – выделение различных физических сетевых интерфейсов под запись и чтение. Ведь если смотреть отношение CPU операций типа Чтения к Записи, то будет соотношение одного порядка, то для сетевого трафика тоже соотношение будет начинаться от двух порядков, более чем в сотни раз. Поэтому разделив на физически разные каналы сетевых коммуникаций эти операции (Чтение-Запись) мы сможем гарантировать определённое время доставки SQL запросов типа Запись на все сервера.
Также, чем меньше блокировок в системе, тем меньше вероятность распределенных деадлоков. В версионниках такие проблемы возникают при конкурентном изменении одной и той же записи. Удивительно, но с помощью РВК в виде дополнительного бонуса можно на уровне настроек расшивать такие узкие места если они существуют в системе.
Если же распределенные деадлоки все же изредка возникают, специальный сервис РВК, который мониторит все блокирующие процессы на всех серверах и разрешает их путем отката одной из транзакций.
Подробнее будет в отдельной статье.
Специфика администрирования РВК
Администрирование распределенной системы, конечно же, сложнее чем локальной, особенно с требованиями работы 24*7. А все потенциальные пользователи РВК как раз и являются счастливыми обладателями ИТ-систем с режимом работы 24/7.
Возникают сразу же проблемы восстановления распределенной БД, горячего подключения нового сервера к РВК. Быстрая сверка данных в распределенных БД также необходима, несмотря на присутствие механизмов сверки транзакций. Возникают задачи мониторинга производительности, а в частности агрегации данных счетчиков по связанным транзакциям и в целом по серверам кластера. Возникают некоторые проблемы безопасности настройки прокси сервиса. Полный перечень проблем и предложение по их решению будет в отдельной статье.
Технологии параллельных вычислений как средство, повышающее эффективность использования РВК
Для масштабируемой ИТ системы крайне важен высокий параллелизм процессов внутри базы данных. Для формирования параллельно отчетности как правило данный вопрос не стоит. Для транзакционной нагрузки в силу исторических рудиментов неоптимальной архитектуры возможны блокировки на уровне изменения одних и тех же записей (конфликт писатель-писатель). Если ИТ систему можно менять, есть открытый код, то можно оптимизировать систему. А если система закрытая, что делать в этом случае? В случае использования РВК появляются возможности на уровне администрирования обходить подобные ограничения. Ну или по крайней мере расширить возможности. В частности, с помощью настроек давать возможность изменять одну и ту же запись, не дожидаясь фиксации транзакции, если возможно грязное чтение, конечно. При этом, в случае отката транзакции данные об изменениях в хронологической последовательности также откатываются.
Подобная ситуация точно подходит, например, для таблиц с агрегацией итогов. У меня уже есть решения для данной проблемы, и я считаю, что независимо от использования РВК, необходимо расширить административные настройки СУБД, как Postgres, так и MSSQL (на ORACLE не исследовал вопрос).
Подробности – в отдельной статье.
Также необходимо раскрыть тематику грязного чтения в РВК и возможные с учетом этого небольшие доработки – внедрение виртуальных блокировок (компания SOFTPOINT запатентовала их как гибкие блокировки).
План следующих публикаций по теме РВК
Статья 2. Результаты нагрузочного тестирования РВК.
Статья 3. Почему для РВК нельзя использовать транзакционную репликацию?
Статья 4. Краткое архитектурное описание РВК
Статья 5. Зачем нужен централизованный протокол в РВК?
Статья 6. Механизмы обеспечения целостности и синхронности распределенных данных на уровне транзакций в РВК.
Статья 7. Проблема распределенных взаимоблокировок в РВК.
Статья 8. Специфика администрирования РВК
Статья 9. Технологии параллельных вычислений как средство, повышающее эффективность использования РВК
Статья 10. Пример интеграции РВК с 1С 8.х
Автор: remoteadmiral