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

 

К свободе от проблемы Больших Данных

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

Сергей Кузнецов

Кроме того, масштабируемые транзакционные СУБД должны поддерживать «правильные» свойства транзакций, наличие которых облегчает и ускоряет разработку транзакционных приложений, удовлетворяющих запросы пользователей. Однако этому требованию не отвечают популярные сегодня системы NoSQL, которые правильнее было бы называть системами NoACID, поскольку их разработчики отказываются именно от свойств ACID-транзакций (Atomicy — Атомарность, Consistency — Согласованность, Isolation — Изолированность, Durability — Долговечность). Попробуем разобраться в следующем: какая разновидность масштабирования хороша для мира транзакционных систем и какие проблемы при этом возникают; какие имеются пути решения этих проблем и каковы истинные причины появления систем NoACID.

Горизонтальное масштабирование транзакционных систем

Транзакционные системы критически важны в инфраструктуре любого современного предприятия, поскольку направлены на поддержку его оперативной деятельности (управление финансами, человеческими ресурсами, логистика, управление связями с заказчиками, продажи и т. д.). Развитие бизнеса приводит к соответствующему росту объемов оперативных бизнес-процессов — требуется выполнять все больше транзакций в единицу времени, причем время выполнения каждой транзакции должно оставаться в пределах, соответствующих ожиданиям пользователей. При развитии бизнеса возрастают и объемы оперативных данных — транзакционная система должна справляться с этим ростом, сохраняя допустимое время обработки транзакций. Другими словами, транзакционные системы должны обладать свойством масштабируемости.

Имеются два основных подхода к масштабированию программных систем — вертикальное (scale up) и горизонтальное (scale out) масштабирование. Вертикальное предполагает повышение производительности каждого компонента оборудования для достижения требуемой производительности программной системы. При горизонтальном масштабировании желательная производительность программной системы достигается за счет увеличения числа компонентов оборудования. Спорным, но все более распространенным мнением является то, что сегодня правильным решением проблем растущего бизнеса является горизонтальное масштабирование. Обоснование этого мнения в основном экономическое. Темпы жизни общества растут, постоянно возникают новые компании (стартапы), большая часть которых не выдерживает конкуренции и исчезает, более удачливые поглощаются крупными или средними стабильными компаниями, а единицы выживают самостоятельно и обретают хотя бы относительную стабильность. В этих условиях компании почти невозможно рассчитать, какого масштаба инфраструктура ИТ ей понадобится для поддержки развития бизнеса хотя бы в среднесрочной перспективе. При вертикальном масштабировании фактически требуется смена оборудования, это неизбежно означает крупные затраты (больше, чем реально нужно в данный момент, чтобы оттянуть время до следующей модернизации), что почти невозможно для стартапов, затруднительно для средних компаний и неудобно для крупных.

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

Проблемы горизонтально масштабируемых транзакционных систем

В сфере управления данными на аппаратном уровне горизонтальная масштабируемость программных систем и приложений поддерживается массово-параллельными архитектурами без использования общих ресурсов узлами вычислительной системы — кластерами, состоящими из процессорных узлов с собственной памятью. Каждый узел использует только свои ресурсы. В сообществе баз данных такие вычислительные системы и разработанные для них программные системы и приложения принято объединять термином shared nothing, чтобы отличать их от симметричных мультипроцессорных архитектур (в частности, многоядерных), которые называют shared everything, и кластерных архитектур с выделенной общей системой хранения (shared disks).

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

Что же касается транзакционных горизонтально масштабируемых систем, то в таких системах теоретически невозможно избежать появления распределенных транзакций, то есть транзакций, которые при своем выполнении читают и/или обновляют данные, хранящиеся в нескольких разделах базы данных в нескольких узлах вычислительной системы. Как будет видно дальше, именно распределенные транзакции составляют главную проблему горизонтально масштабируемых транзакционных СУБД и основную причину появления систем NoACID.

Но сначала разберемся в том, почему именно аббревиатура ACID стала для одних почти священным заклинанием, а для других ругательством. Понятие транзакции баз данных со свойствами ACID первым предложил Джим Грей в своей статье “The Transaction Concept: Virtues and Limitations”. По его мнению, которое вполне соответствует житейскому смыслу слова «транзакция», транзакция базы данных должна поддерживать свойства атомарности (любая транзакция над базой данных либо должна быть полностью выполнена, либо никакие ее результаты не должны отражаться на состоянии базы данных); согласованности (после завершения любой транзакции база данных должна находиться в состоянии, соответствующем требованиям согласованности, заданным в виде логического условия проектировщиками или администраторами базы данных, причем понятие согласованности принято называть логической согласованностью, или логической целостностью базы данных, в отличие от физической согласованности, или физической целостности базы данных, требующей согласованности физических структур хранения базы данных, например корректности ссылок); изолированности (ни одна транзакция не должна видеть изменения базы данных, выполненные еще не завершившейся транзакцией); долговечности хранения (результаты любой успешно завершившейся транзакции должны быть навсегда сохранены в состоянии базы данных, каким бы образом оно ни восстанавливалось после возможных в будущем сбоев).

Есть много доводов в пользу того, что поддержка ACID-транзакций должна присутствовать в будущих горизонтально маштабируемых транзакционных СУБД. Во-первых, эти свойства соответствуют общечеловеческому смыслу понятия транзакции. Во-вторых, аббревиатура ACID неразрывна. Можно показать, что невозможно пожертвовать каким-либо свойством, не исказив смысла оставшихся. В-третьих, сообщество баз данных за прошедшие десятилетия накопило громадный багаж алгоритмов и методов поддержки ACID-транзакций. Наконец, в-четвертых, при наличии такой поддержки на уровне СУБД разработчикам транзакционных приложений не приходится думать о многих технических проблемах и они могут сосредоточиться на логике приложений.

Так в чем дело? Давайте делать новые горизонтально масштабируемые транзакционные СУБД, поддерживающие нужные свойства транзакций. Но не все так просто. При переходе к архитектуре shared nothing возникают новые проблемы, часть из которых успешно решается, а вот для решения других требуются интенсивные исследования, причем без гарантии получения идеальных результатов. Вернемся к свойствам транзакций и рассмотрим проблемы их поддержки в среде shared nothing.

Атомарность. Если поддерживается изолированность транзакций, то для обеспечения атомарности нужно уметь откатывать (Rollup) транзакции, которые по каким-то причинам (например, при нарушения требования согласованности) не удается зафиксировать. В среде shared nothing откат распределенной транзакции означает выполнение требуемых действий во всех узлах, где от имени этой транзакции были изменены данные. Ничего сложного в этом нет — проблема решается, если мы умеем эффективно фиксировать распределенные транзакции.

Согласованность. Концептуально проверка выполнения требования согласованности для транзакции выполняется при ее фиксации. Если ограничения целостности выполняются, то транзакция фиксируется, если нет — откатывается. В среде shared nothing проверка выполнения требования согласованности для распределенной транзакции означает выполнение требуемых действий во всех узлах, где от имени этой транзакции изменялись данные. Опять же проблема решается, если имеется эффективная поддержка фиксации распределенных транзакций.

Изолированность. В классических централизованных транзакционных СУБД эта проблема решается за счет использования двухфазного протокола синхронизационных блокировок объектов базы данных. В среде shared nothing этот подход работает плохо, поскольку таблицы блокировок разделяются по узлам системы и очень трудно обнаруживать и устранять неизбежные синхронизационные тупики (дедлоки). Однако имеется альтернативный подход на основе временных меток транзакций и версий объектов базы данных. И опять — в самом подходе ничего особенно сложного нет, если мы хорошо умеем фиксировать распределенные транзакции (именно в момент фиксации скрытая версия объекта должна стать видимой для других транзакций).

Долговечность хранения. Чтобы обеспечить это свойство, нужно выполнять некоторые действия именно при фиксации транзакции (например, вытолкнуть во внешнюю память буфера журнала, если в СУБД поддерживается журнализация изменений). Сами действия в любом случае просты, но требуется умение эффективно фиксировать распределенные транзакции.

Получается, что все проблемы поддержки ACID-транзакций в среде shared nothing сводятся к фиксации распределенных транзакций — именно она пугает, и чтобы понять ее сложность, напомним суть наиболее распространенного двухфазного протокола фиксации распределенных транзакций. Пусть в распределенной транзакции участвуют n узлов системы — T1, T2, …, Tn (соответствующие части распределенной транзакции принято называть транзакциями-участниками). В некотором узле Tk (возможно, одном из узлов-участников) выполняется программный компонент, который принято называть координатором распределенной транзакции. Координатор получает от приложения, от имени которого выполняется данная распределенная транзакция, сообщение с требованием ее фиксации. На первой фазе работы протокола координатор рассылает всем участникам сообщение «подготовиться к фиксации». Если они подтверждают свою готовность к фиксации, то на второй фазе координатор рассылает сообщение с требованием их фиксации. Иначе на второй фазе участникам рассылается сообщение с требованием отката.

Итого, при фиксации распределенной транзакции, в которой участвует n узлов, потребуется 3 х n обменов сообщениями между узлами системы (если считать, что координатор выполняется не в отдельном узле). Если учесть, что в транзакционных приложениях транзакции обычно очень короткие и каждая операция, как правило, затрагивает данные только одного раздела, то накладные расходы на фиксацию распределенных транзакций могут значительно превысить стоимость выполнения основной части транзакции. Но хуже другое — при горизонтальном масштабировании транзакционной СУБД, вообще говоря, вероятно увеличение числа участников каждой распределенной транзакции, поскольку операции, которая обращалась к данным одного раздела, после перераспределения данных, вызванного горизонтальным масштабированием, могут потребоваться данные из нескольких разделов. Другими словами, после увеличения масштаба системы некоторые транзакции могут начать выполняться медленнее, чем раньше. Так что распределенные транзакции действительно составляют основную проблему транзакционных систем категории shared nothing.

Попытки решения

Наиболее интересные подходы к построению горизонтально масштабируемых транзакционных систем были разработаны в проекте H-Store Массачусетского технологического института, Брауновского и Йельского университетов и HP Labs. Идейным вдохновителем проекта является Майкл Стоунбрейкер, основавший несколько лет назад компанию VoltDB для коммерциализации H-Store .

H-Store (см. рисунок) — это архитектура СУБД shared nothing с хранением всех разделов базы данных в основной памяти. В системе не поддерживается журнал redo (повторного выполнения транзакций), и для восстановления фрагментов базы данных после сбоев используется синхронная репликация. Код всех транзакций выполняется в узлах СУБД, а клиенты могут лишь запускать транзакции с указанием аргументов. Как показывают эксперименты разработчиков, такая архитектура обеспечивает высокую скорость обработки транзакций, значительно превосходящую ту, которую на аппаратных средствах сравнимой стоимости могут обеспечить традиционные универсальные (поддерживающие и аналитические, и транзакционные базы данных) СУБД, если, конечно, транзакции не являются существенно распределенными.

Общая архитектура H-Store
Общая архитектура H-Store

 

Кстати, при использовании многоядерных процессоров разработчики H-Store трактуют каждое ядро как отдельный узел с собственной основной памятью. Это не очень хорошо, потому что не используется факт реального наличия общей памяти у многоядерного компьютера. И здесь стоило бы обратить внимание на проект DORA , которым руководила Анастасия Айламаки. В этом проекте ядра многоядерного процессора образуют как бы систему shared nothing, но разделение данных в основной памяти производится за счет использования механизма виртуальной памяти. Такой подход выглядит очень перспективным.

Как же решают разработчики H-Store проблему распределенных транзакций? Первый подход основывается на том, что код транзакций в подавляющем большинстве транзакционных приложений статичен — он известен до начала выполнения любой транзакции. Разработаны методы статического преобразования кода транзакций, которые при известном способе разделения транзакционной базы данных обеспечивают улучшение поведения транзакций с позиций затрат на их фиксацию. Эту задачу можно считать очень сложной, и пока ее полное решение отсутствует. Второй подход состоит в том, что при заданной рабочей нагрузке (известны код каждой транзакции и частота их выполнения) находится способ разделения базы данных между заданными узлами, минимизирующий затраты на выполнение распределенных транзакций. Эта задача близка к классической задаче разрезания ориентированного графа на подграфы таким образом, чтобы подграфы связывались минимальным числом дуг. Известно, что эта задача является NP-полной, и поэтому получение ее точного решения для графа большого размера практически невозможно. Тем не менее известно, что близкие задачи давно актуальны в области электронного САПР и для их удовлетворительного параллельного решения имеются готовые программные продукты.

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

Теорема CAP и отказ от поддержки ACID-транзакций

«Теорему» CAP (Consistensy — Cогласованность, Availability — Доступность, Partitioning — Разделение) ввел в обиход в 2000 году известный специалист в области распределенных систем Эрик Брюер в своем выступлении на симпозиуме, посвященном принципам организации распределенных систем. Слово «теорема» умышленно взято в кавычки, поскольку в силу своей очевидности и неформальности утверждение Брюера теоремой считать нельзя.

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

Несмотря на тривиальность «теоремы» CAP, ею серьезно заинтересовались теоретики распределенных систем. В частности, в статье Сета Джильберта и Нэнси Линч “Brewer's conjecture and the feasibility of consistent, available, partition-tolerant web services“ эта «теорема» по-настоящему формализуется и доказывается, не переставая в общем-то быть очевидной. Самое странное в «теореме» CAP то, что ее «выводами» руководствовались вдохновители части направления, которое принято называть NoSQL, отказываясь от поддержки в своих распределенных системах управления данными ACID-транзакций. Кстати, поскольку представители этой части NoSQL отрицают не столько язык SQL, сколько ACID-транзакции, было бы правильно называть его NoACID. Путаницу внесло использование термина consistency в аббревиатурах ACID и CAP в разных смыслах. В контексте ACID имеется в виду логическая согласованность базы данных — соответствие ее содержимого некоторому инвариантному логическому значению. В контексте CAP имеется в виду физическая идентичность реплик. Нужно было бы отдельно исследовать возможность поддержки логической целостности в двух одновременно доступных разделах сети, в которой работает транзакционная СУБД, но уж точно логическую согласованность можно поддерживать, если разрешить продолжать работу только в одном разделе.

В направлении NoACID работают весьма квалифицированные специалисты, которые не могли не заметить эту путаницу, а истинной причиной отказа от поддержки ACID-транзакций в системах NoACID является нежелание иметь дело с фиксацией распределенных транзакций. Возникающие при этом проблемы дают существенно более серьезный повод для отказа от ACID, чем «теорема» CAP. Получается, что сообщество NoACID борется не за соответствие формулировке «теоремы» CAP, а за горизонтальную масштабируемость своих систем.

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

***

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