Non progredi est regredi. Не идти вперед – значит, идти назад. Древние, изрекая эту универсальную мудрость, конечно же, предвидели, что применима она будет и спустя века. Так и вышло.

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

Проверенное временем и успешной эксплуатацией в десятках сервисов NoSQL-решение Bigtable оказалось неспособно справиться с главным требованием, предъявляемым Google+, – непротиворечивостью пользовательских данных, тысячи копий которых разбросаны по сотням тысяч серверов Google.

Хранилище Bigtable просто не имело механизмов, обеспечивающих абсолютную упорядоченность во времени действий над репликами пользовательских данных Google+. Механизмов, которые отлично работают в реляционных СУБД, поддерживающих концепцию ACID по отношению к выполнению распределённых SQL-транзакций.

Именно поэтому Google пришлось сделать ещё один шаг вперёд, вступив на заполненную конкурентами площадку SQL-решений. Но новый путь – потому и новый, что идёт в стороне от проторённых дорог. И детище поискового гиганта – распределённое хранилище Spanner, анонсированное в 2012 году, – именно новое, поскольку относится к решениям NewSQL.

NewSQL – масштабируемость системы и непротиворечивость данных в одной упряжке

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

Google Spanner, конечно, не единственное NewSQL-решение, но однозначно самое масштабное, а уникальная архитектура делает его особенно интересным.

Архитектурным предком Spanner, безусловно, является Bigtable, от которой новичок унаследовал принцип разделения таблиц на таблеты и распределение их по множеству span-серверов.

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

Архитектура кластера Spanner настолько масштабна, что именуется Вселенной (Universe). В ее «галактиках» Zones обитают множество span-серверов, способных обрабатывать SQL-запросы.
Архитектура кластера Spanner настолько масштабна, что именуется Вселенной (Universe). В ее «галактиках» Zones обитают множество span-серверов, способных обрабатывать SQL-запросы.

Поддержка SQL и распределённых транзакций – это важно, однако главное достоинство Spanner заключается в другом. Новая система хранения Google способна дать гарантию, что при выполнении транзакций в многочисленных репликах её таблиц не появится расхождений, влияющих на целостность и непротиворечивость данных.

Вот простой пример. Предположим, один из пользователей Google+ решает исключить из своего списка друзей другого пользователя. Это не было бы проблемой, если бы таблицы со списками хранились в единственном экземпляре, но увы: социальная сеть устроена куда сложнее. Spanner создаёт многочисленные реплики данных пользователей в сотнях географически распределенных ЦОДов. Наличие реплик – залог быстрого доступа пользователей социальной сети к информации друзей из разных стран, но из-за них простое изменение списка друзей выливается в необходимость синхронно модифицировать многочисленные копии данных, разбросанные по всему свету.

Одно дело, если база данных пользователей социальной сети работает на одном кластере Google. И совсем другое - когда реплики этой базы разбросаны по множеству тысяч кластеров в сотнях ЦОДов. Непротиворечивость данных в этих репликах становится серьёзной проблемой.
Одно дело, если база данных пользователей социальной сети работает на одном кластере Google. И совсем другое – когда реплики этой базы разбросаны по множеству тысяч кластеров в сотнях ЦОДов. Непротиворечивость данных в этих репликах становится серьёзной проблемой.

Каким образом Spanner справляется с этой проблемой? С помощью специального программного интерфейса, именуемого TrueTime API (от английского true time – “истинное время”).

Настенные часы и мастера Армагеддона на службе строгой непротиворечивости

В основе TrueTime API лежит концепция единого времени, действующего в рамках всего хранилища Spanner, распределённого по нескольким дата-центрам. Оно называется global wall-clock time – “глобальные настенные часы”, общие для всех дата-центров.

Глобальное время в Spanner, конечно же, хранится не в одном месте. Оно тоже распределено. В составе каждого ЦОДа выделяются специальные серверы, именуемые “мастерами времени” (Time Master Devices – TMD). Большинство из них получает сигналы точного времени от спутниковой системы GPS, однако для надёжности (мало ли что может случиться со спутниковым каналом в самый ответственный момент) в числе TMD есть особые экземпляры, которые называются “мастерами Армагеддона” (Armageddon Masters). Эти «часовщики» несут на своем борту… точнейшие атомные часы. И даже это не всё. Все сервера TMD постоянно синхронизируют время, общаясь друг с другом с помощью специального сервиса timeslave. Вот уж воистину TrueTime.

Чтобы библиотека TrueTime могла вернуть каждому из Span-серверов точное глобальное время, в состав Spanner включены «мастера времени» - узлы, связанные с системой GPS и даже использующие собственные атомные часы.
Чтобы библиотека TrueTime могла вернуть каждому из Span-серверов точное глобальное время, в состав Spanner включены «мастера времени» – узлы, связанные с системой GPS и даже использующие собственные атомные часы.

Глобальное время в TrueTime API не так уж и глобально. Правильнее будет назвать его временем с ограниченной неопределённостью. Почему? В распределённой системе практически невозможно добиться мгновенного отклика всех узлов «здесь и сейчас». Поэтому функция TT.now() библиотеки TrueTime возвращает значение времени в виде интервала (TT.now().latest – TT.now().earliest), учитывающего неизбежную погрешность. Таким образом, вызов любым узлом Spanner функции TT.now() приводит к возвращению текущего глобального времени, но в виде временного интервала.

Наличие этого временного зазора и позволяет узлам Spanner решать вопросы согласованности изменяемых в ходе транзакции данных. Базовым в данном случае является так называемый протокол двухфазной фиксации транзакции (2-Phase Commit Protocol), отлично зарекомендовавший себя в распределённых реляционных СУБД. Он основан на очень простом принципе: если транзакция, выполняемая в одном узле, влияет на изменения в узлах, содержащих реплики данных этого узла, то она должна быть или зафиксирована (результат изменения сохранён на диске узла) на всех этих узлах, или же не выполнена (откат) ни на одном из них.

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

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

Для реализации протокола двухфазной фиксации в Spanner применяется проверенный в ходе эксплуатации Bigtable алгоритм разрешения коллизий Paxos.

Узел, инициирующий транзакцию (например, удаление пользователя из списка друзей), автоматически становится координатором этой транзакции. О транзакции он уведомляет все узлы-участники, содержащие реплику изменяемых данных. Каждый из узлов получает для формирования временной метки обновляемых данных интервал текущего глобального времени TT.now(). Естественно, что у географически распределённых узлов он будет разным. Все узлы-участники дают координатору обещание, что постараются выполнить фиксацию транзакции в отведенный TT.now() интервал. Получение такого фьючерс-обещания завершает фазу готовности. Дальше каждый из узлов реализует транзакцию (запись изменений в журнал redolog и выполнение изменений данных в оперативной памяти) независимо от других участников, но зафиксировать её участники могут лишь по команде координатора. Задачей координатора при этом является не только передача такой команды, но и рассылка всем участникам общей временной метки измененных данных, единой для всех реплик. При этом координатор внимательно следит, чтобы значение этой временной метки не превысило границу интервала TT.now().earliest, то есть находилось в рамках общего глобального времени.

Что случится, если хотя бы один из узлов не успеет отрапортовать о готовности к фиксации в отведённый временной интервал? Координатор откатит выполнение транзакции и не даст команду на её фиксацию узлам-участникам, которые тоже будут вынуждены выполнить откат. После этого попытка выполнения транзакции повторится.

Реализация процесса двухфазной фиксации транзакции на примере удаления пользователя социальной сети из списка друзей.
Реализация процесса двухфазной фиксации транзакции на примере удаления пользователя социальной сети из списка друзей.

Очевидно, что реализация протокола двухфазной фиксации в высоконадежных серверах баз данных, на которых выполняются реляционные СУБД, сильно отличается от его реализации в заведомо ненадежных серверах кластеров Google. И именно поэтому уникальной является комбинация аппаратно реализованной в TMD-узлах технологии синхронизации глобального времени и функций библиотеки TrueTime, представляющей это время в виде интервала, в рамках которого узлы успевают «договориться» о согласованности данных.

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

Сейчас в среде Spanner, наряду со страницами пользователей Google+, хранятся критически важные для бизнеса Google данные её системы контекстной рекламы Google F1 и корреспонденция почтового сервиса Gmail. В будущем, вероятно, сфера его применения станет ещё шире.