Шардинг базы данных – это прием, который позволяет разбивать данные между разными физическими серверами (горизонтальное масштабирование), что позволяет создавать масштабируемые приложения или сервисы.
SQL Azure – это облачный сервис от Microsoft, который предоставляет функционал базы данных, как услугу. Этот вид услуги предоставляет множество преимуществ, включая быстрое выделение ресурсов, эффективную масштабируемость, высокую доступность и снижение расходов на администрирование. SQL Azure в сочетании sharding обеспечивает неограниченную масштабируемость данных для приложений. Эта статья содержит обзор sharding в SQL Azure, описывает основные концепции построения шардинг приложений и подкреплена жизненными примерами. Важно понимать, что сам по себе шардинг – это бизнес сценарий, который может иметь множество реализаций (например SQL Azure Federations это одна из реализаций шардинга) и в данной статье он будет рассматриваться именно так.
В этом руководстве большое количество специфических терминов, перевод которых достаточно неоднозначен, поэтому сразу опишем их значение:
Шард приложение / архитектура / концепция – это приложение / архитектура / концепция, которые используют горизонтальное масштабирование данных (Sharding).
Шардинг – это процесс разнесения данных между физическими серверами (горизонтальное масштабирование).
Некоторые .NET термины были переведены на русский язык, но для разработчиков и архитекторов систем привычно видеть их так, как они пишутся в коде приложений. Для таких терминов в скобках указан их первоначальный вид.
1 Введение
Этот документ является руководством по построению приложений, в архитектуру которых заложено горизонтальное разбиение данных, называемое Шардинг (Sharding). Данные в нем разносятся между несколькими физическими базами данных, что обеспечивает масштабируемость приложения или сервиса по мере его роста и увеличения объема данных.
Цель этого руководства – показать преимущества построения шард приложений на базе SQL Azure, для обеспечения высокой эффективности и масштабируемости.
В данном руководстве вы найдете описания типовых сценариев и лучших практик реализации шардинга используя SQL Azure. Представлены концепции реализации шард решений на базе .NET и SQL Azure, а так же дано описание недавно появившейся реализации шардинга - SQL Azure Federations.
Это руководство является базовым для понимания того, как внедрять шардинг сегодня с SQL Azure и как он будет развиваться в ближайшем будущем. В следующих руководствах будут описаны дополнительные аспекты шардинга более подробно.
Кроме того, в этом руководстве будут освещены следующие темы:
1.1 Горизонтальное масштабирование данных
Шардинг (Sharding) – это шаблон разработки приложения для обеспечения масштабируемости решений с большим объемом данных. Шардинг приложения – это процесс разбиение логических баз на маленькие куски (Shard в переводе с английского – это «Осколок») и разнесение этих кусков между разными физическими базами данных, чтоб обеспечить масштабируемость. Каждая физическая база данных в этой структуре и есть Shard (“Шард”).
Отличием горизонтально масштабируемых от вертикально масштабируемых решений является то, что в случае с горизонтальным масштабированием разносятся непосредственно данные, что обеспечивает потенциально больше возможностей для расширения, т.к. нет привязки к таблицам базы данных. Чего не скажешь о вертикально масштабируемых решениях, которые предполагают вынесения отдельных таблиц или схем баз данных на разные сервера.
Процесс шардинга предполагает разнесения данных между шардами путем применения одной или нескольких стратегий формирования ключа шардинга (sharding key), который является первичным ключом сущности, которая хранится в шарде. Связанные сущности группируются в связанный набор данных по заданному ключу, и этот набор называется атомарной единицей. Иными словами, Атомарная единица (Atomic Unit) – это набор сущностей с одинаковым значением ключа шардинга и имеющих свой собственный ключ атомарной единицы. Все сущности атомарной единицы всегда хранятся в одном шарде.
Атомарная единица является основной архитектурной концепцией, которая используется sharding инфраструктурой в качестве основы как для последовательного доступа к данным, так и для динамического добавления и удаления шардов. Шард инфраструктура обеспечивает, что все записи в атомарной единице всегда будут находится в пределах одного физического шарда. Это облегчает соединение и агрегирование, а также является ключевым преимуществом для использования SQL Azure Federations, которая в свою очередь реализует шардинг инфраструктуру для гибкого масштабирования решений.
Преимущества шардинга с SQL Azure
Шардинг как концепция существует в нескольких формах, и на протяжении многих лет развивалась как реализация локальной инфраструктуры и призвана удовлетворять следующие потребности:
Действительно, можно строить шард решения на своих собственных площадках. Многие так и делают. Но у этого подхода есть и ряд недостатков:
SQL Azure предоставляет уникальную возможность решить эти вопросы используя преимущества, которые предоставляются облачной платформой Azure как сервис.
Ключевые преимущества:
Однако, на сегодняшний день при проектировании шардинг архитектуры на базе SQL Azure нужно быть осторожным в следующих нюансах:
Для облегчения этих трудностей и обеспечения гибкого шардинга часть необходимых возможностей уже добавлена в SQL Azure Federations и показана на рисунке 1.
Рисунок 1: SQL Azure Federations
1.3 Остальная часть руководства
Далее мы рассмотрим концепции и шаблоны построение щардинг приложения, покажем часто используемые приемы для управления шардами в SQL Azure.
2 Обзор Sharding
Sharding – это шаблон построения приложений, в котором данные разбиты горизонтально между разными физическими базами данных.
2.1 Multi-Master шардинг архетип
Рисунок 2: Multi-master шардинг архетип
В этом архетипе все шарды могут считывать и записывать данные, без репликации данных между собой. Эта модель имеет название “shared nothing” потому что данные не являются общими и не реплицируются между шардами. Этот архетип стоит использовать если:
В multi-master архетипе, по причине того, что данные не являются общими и не реплицируются между шардами, приложение для своей работы должно знать, как данные распределены между шардами и как управлять последовательным и параллельным доступом ко всем шардам. Это может быть накладно, если размер базы данных и количество шардов растет. Об этом мы погорим далее.
2.2 Проблемы шардинга
Шардинг предоставляет прекрасные возможности масштабирования приложений, но он и усложняет приложение по многим причинам. Приложение должно распределять свои данные между разными серверами баз данных, координировать доступ к серверу и данным.
Для этого в приложении можно внедрить некий сервис или класс (например DatabaseManager), который будет обеспечивать доступ к данным, распределенным между шардами и решать следующие задачи:
Поэтому приложения, построенные для управления шардингом, вместо связей таблиц могут применять следующие приемы:
Все изменения структуры сущности (включая добавление, удаление, или манипуляции с ключами) необходимо проводить на всех шардах, которые представляют эту сущность. По мере увеличения числа шардов этот процесс становится все более громоздким, поэтому, желательно выключать приложение во время внесения изменений в схему базы данных.
3 Концептуальный шаблон для шардинга с SQL Azure
На основании описанного выше multi-master архетипа мы можем приступить к описанию процесса реализации шардинга на базе SQL Azure.
Описание принципы могут быть применены к любой технологии и являются базовой концепцией для построения надежных шардинг решений.
В следующих руководствах будут представлены примеры реализации шардинга. В секции 4 будет описано потенциально возможное применение библиотеки ADO.NET, а в секции 5 будет дано детальное описание концепций, описанных в секции 4 для построения multi-master шардинг архетипа.
3.1 Высокоуровневая концепция дизайна
Ниже приведена таблица, в которой даны краткие описания различных понятий, которые помогут Вам в реализации собственных шардинг решений на базе SQL Azure. Описанная ниже терминология будет использоваться в дальнейшем.
Термин
Описание
Физические шарды
Абстракции физических баз данных.
Виртуальные шарды
Логические конструкции, которые симулируют большее (по сравнению с реально существующими физическими шардами) количество шардов для абстрагирования приложений от особенностей физических шардов.
Определения сущности
Структура данных, которая будет хранится и использоваться для генерации запросов и управления схемами.
Атомарная единица
Логически сгруппированные сущности, которые должны хранится в рамках одного шарда с одним ключом для них.
Стратегии шардинга
Определение того, как ключи атомарных единиц привязаны к шардам и как они могут быть извлечены.
Каталог метабазы шарда
Хранилище конфигурационной информации для определения приложением структуры шардинга.
3.2 Основные решения и ограничения
Ниже представлены критерии относительно дизайна приложений для максимального использования возможностей SQL Azure и достижения экстремальной эффективности работы приложения.
3.2.1 Сущности данных и атомарные единицы
3.3 Концептуальные элементы для шард приложения с использованием SQL Azure
3.3.1 Каталог метабазы шарда (shard metabase directory)
Каталог метабазы шарда это хранилище (репозиторий) всей конфигурационной информации. Приложение регулярно выгружает эту информацию для обеспечения алгоритмов шардинга. Информация состоит из описаний физических шардов, связку виртуальных и физических шардов, определения сущностей, атомарных единиц, стратегий определения ключей атомарных единиц и их привязку к виртуальным шардам, информацию о маршрутизации запросов между шардами.
3.3.2 Физические шарды (Physical Shards)
Физический шард – это отдельная база SQL Azure, которая используется для хранения атомарных единиц. Каждый шард хранит подмножество данных, структура которых соответствует конфигурациям стратегий, описанным в каталоге метабазы шарда.
3.3.3 Виртуальные шарды (Virtual Shards)
Виртуальные шарды играют важную роль в обеспечении гибкости шард решений. Они дают возможность симулировать большое количество шардов при использовании меньшего (или эквивалентного) количества физических шардов, а так же предоставляют возможность перераспределения без изменения ключей шардов.
Основополагающим руководством при работе с виртуальными шардами является планирование максимального количества шардов, которое может понадобится для хранения информации, еще на этапе проектирования приложения. Мыслите широко, т.к. количество виртуальных шардов не ограничено, но должны быть фиксированным. При изменении количества виртуальных шардов необходимо будет перераспределять ключи шардов.
В качестве примера рассмотрим практический сценарий того, как можно использовать виртуальные шарды для обеспечения масштабируемости в зависимости от спроса в магазине. Предположим, что у нас есть приложение для продажи билетов на какие-то мероприятия проходящие на стадионе, и нам необходимо распределить нагрузку при обработке данных, когда билеты поступят в продажу, но сохранить количество баз при отсутствии нагрузки.
В решении этого вопроса нам помогут виртуальные шарды. Для каждой секции стадиона определяем один виртуальный шард. Пока продажи не начались, количество транзакций минимально, все виртуальные шарды могут быть привязаны к одному физическому SQL Azure шарду. Когда продажи начались, можно провести перераспределение: добавить для каждой секции стадиона по одному физическому шарду, тогда соотношение виртуальных и физических шардов будет 1:1, и отдельный физический шард будет обрабатывать отдельную секцию стадиона. Когда нагрузка спадет, физические шарды могут быть объединены, а виртуальные шарды перенаправлены на оставшийся один физический шард (как это было до начала продаж). Лишние физические шарды будут удалены.
3.3.4 Определения сущности (Entity Definitions)
Сущность является описанием элементов данных, которые формируют фундаментальную единицу для хранения в шарде и обычно соответствует структуре таблиц данных внутри шарда. Эти описания используются для определения того, как выполнять различные задачи, включая генерацию запросов и управления схемой шарда.
3.3.5 Атомарные единицы (Atomic Units)
Атомарная единица – это набор связанных сущностей с общим ключом шардинга и собственным ключом атомарной единицы. Они (атомарные единицы) хранятся в пределах одного шарда. В каждой сущности любой атомарной единицы должно быть определено поле, где будет хранится ключ атомарной единицы. Ключ шардинга хранится во всех сущностях и используется провайдером данных для группировки связанных сущностей и определения связей между сущностями и атомарными единицами. Например, покупатель и его покупки могут быть в одной атомарной единице. В этом случае ID пользователя будет являться ID этой атомарной единицы. Покупки пользователя должны всегда записываться в тот же шард, где записан пользователь и с тем же ключом пользователя.
3.3.6 Стратегии шардинга (Sharding Strategies)
Стратегии шардинга определяют правила, по которым приложение распределяет и извлекает данные (атомарные единицы) между шардами. Политика – это набор стратегий для генерации ключа, доступа, развязки, выбора и комбинирования. В стратегиях так же описывается, каким образом будут распараллеливаться запросы, когда несколько шардов идентифицированы как кандидаты для индивидуальной атомарной единицы и как объединить результат, который пришел из нескольких шардов.
Ниже описаны рекомендации к реализации каждой из категорий:
3.3.6.1 Генерация ключей
Стратегия генерирования ключей информирует приложение о том, как выделять ключи для сущностей и связанных атомарных единиц. Есть три рекомендованных стратегии генерации ключей: На стороне приложения, последовательно с типом данных Big Integer и GUID
3.3.6.1.1 На стороне приложения.
Приложение отвечает за генерирования ID для сущностей, и может использовать идентификаторы с типом данных Big Integer или GUID. В этом случае приложение должно либо определить диапазон, либо использовать хеш для генерирования ID для шардов.
3.3.6.1.2 Последовательно с типом данных Big Integer
Использование этой стратегии предполагает последовательную генерацию числовых значений, что гарантирует уникальность ключей для всей инфраструктуры шардинга. Для этого приложению необходимо определить диапазон значений ID (обычно это от 0 до максимального значения типа Big Integer).
3.3.6.1.3 GUID
Использование этой стратегии так же обеспечивает уникальность ключей и применима для недружелюбных (unfriendly) ID в сущностях приложения. При использовании этого метода приложению необходимо брать хеш от ID для выбора шарда.
3.3.6.2 Доступ
Стратегии доступа используются для информирования, каким образом обрабатывать операции между разными шардами (а именно: каким образом распределять запроси и собирать результат с различных шардов). Стратегии доступа бывают последовательными и параллельными.
3.3.6.2.1 Последовательный доступ
Стратегия последовательного доступа определяет последовательность распределения запросов между шардами и подразумевает ожидание результата от текущего шарда прежде чем запустить запрос к следующему. Эта стратегия достаточно убыточна в случае, если данные равномерно распределены по шардам. В этом случае максимальная нагрузка будет приходится на те шарды, которые первые в списке. И вместе с этим применение данной стратегии поможет сохранить производительность системы, если запрашиваемые данные находятся в первых по списку шардах и в применении параллельных запросах нет необходимости.
3.3.6.2.2 Параллельный доступ
Стратегия параллельного доступа описывает, как распределять запросы между всеми шардами и подразумевает ожидание результата от всех запрошенных шардов и комбинирование его на базе стратегии комбинирования. Этот тип стратегии применим, когда данные распределены случайным образом по шардам.
3.3.6.3 Выбор
Стратегия выбора применяется, когда необходимо определить, в каком шарде нужно сохранить новую запись. Существует четыре стратегии выбора: По диапазону ID, по хешу ID, циклически и все шарды.
3.3.6.4 Развязка
Стратегия развязки используется для определения набора шардов, в которых атомарная единица может «жить» после ее записи. Есть две основных стратегии развязки: Все и на основании ID
3.3.6.5 Комбинирование
Стратегия комбинирования показывает, каким образом объединять данные, которые были возвращены из нескольких шардов. Таких стратегий две: «Комбинировать все» и «выводить первую».
3.4 Генерирование ID атомарной единицы и привязка к шарду.
Безусловно, самая важная часть шардинга является генерация и привязка ключей шардов к самим шардам. Это делается приложением согласно тем стратегиям, которые прописаны в каталоге метабазы шарда.
Есть разные функции генерирования ключей и привязки их. Их выбор имеет прямое влияние на эксплуатационные характеристики шардов.
Ниже описаны два рекомендованных типа:
Тип генерации
Последовательный Big Integer
Генерация уникальных big integer инкрементов в пределах заданного минимума и максимума.
GUID
Генерирует поток GUID.
3.5 Обнаружение атомарной единицы
Определенная атомарная единица может хранится в любом шарде, и в любой момент, в ходе перераспределения, может быть перемещена на другой шард. Из-за этого приложению необходимо каждый раз определять значение шард ключа для атомарной единицы для возможности выполнять запросы к шарду, в котором находится атомарная единица.
Обнаружение ключа шарда может быть сделано двумя способами:
1) При создании атомарной единицы ей присваивается ключ шарда, который можно сохранить в отдельной таблице. 2) Выполнить запрос ко всем шардам по атрибутам сущности. Из возвращенного объекта извлечь ключ шарда
Как только ключ к шарду определен, может быть применена стратегия развязки для определения виртуального шарда. Полученную информацию можно использовать для извлечения сущности.
3.6 Управление соединениями
Шард приложение должно поддерживать множество соединений с базами данных. Для оптимизации процесса, на уровне приложения необходимо обеспечить «умный» менеджмент соединений (использовать повторно открытые соединения и т.д.). В текущей реализации ADO.NET, каждая уникальная строка подключения создает свой пул соединений и с увеличением количества шардов важно правильно управлять соединениями.
3.7 Перераспределение
Под перераспределением следует понимать разделение и объединение шардов. Это подразумевает добавление или удаление одного или нескольких шардов и перемещение атомарных единиц между шардами. При разделении шарда происходит создание дополнительного шарда и перемещение атомарных единиц с одного шарда на другой, при этом к созданному шарду привязывается как минимум один виртуальные шард. Шарды так же могут быть объединены путем перемещения атомарных единиц с одного шарда на другой, удаления пустого шарда и перераспределения виртуальных шардов.
3.8 Управление схемами
Приложению, которое работает с шардами необходимо убедится, что схема сущностей, которую использует приложение, является одинаковой во всех шардах. Любое изменение структуры таблицы (добавление/удаление колонок, переименование объектов, манипулирование ключами…) должно быть распространено на все шарды. Если шардов много, процесс применения изменений может быть длительным и во избежание конфликтов данных, желательно на время внесения таких изменений выводить приложение в офлайн.
4 Дизайн приложения для Sharding с SQL Azure и ADO.NET
В этом разделе будут рассматриваться основные моменты построения шард приложений с использованием SQL Azure. Описываемые компоненты построены на .NET используют ADO.NET как провайдер данных для SQL Azure. Ключевой компонент описанный тут может быть альтернативой функционалу SQL Azure Federations. Основные возможности SQL Azure Federations описаны в секции 6.
4.1 Цели
Описанный тут высокоуровневый дизайн для шардинга на базе библиотеки ADO.NET реализует концепцию, описанную в секции 3. Использование ADO.NET обеспечивает стандартный, привычный шаблон и набор абстракций для разработки приложений.
Специальные цели:
Описанные модели будут использованы в других разделах настоящего руководства. Это руководство является базовым, дальнейшее уточнение будет дано в разделе 5, в котором будут описаны чуть подробнее потенциал реализации.
4.2 Библиотека шардинг на базе ADO.NET
Это набор классов, которые реализуют ADO.NET DataSet и DataAdapter шаблоны, а так же шаблон шардинга, описанный в секции 3. Библиотека на базе ADO.NET будет использовать шардинг каталог метабазы чтоб получить структуру данных и стратегии, чтоб поддерживать шардинг в приложении.
Библиотека шардинга содержит два основных класса: класс соединения (Connection Class) и адаптер данных (Data Adapter)
4.2.1 Класс соединения
Класс соединения не будет содержать в себе строку соединения, но будет содержать информацию о том, как загружать каталог метабазы шарда. Он будет считывать эту информацию и понимать, как управлять соединениями между шардами, как работать с адаптерами данных для построения распределенных запросов, и как возвращать результат приложению по заданным стратегиям.
4.2.2 Шард адаптер данных
Манипуляция с данными в шард приложении по умолчанию отключена и осуществляется только через адаптер данных. Кроме того, при использовании каталога метабазы шарда нет необходимости в insert, delete, update или select командах потому, что описание таблицы в каталоге метабазы шарда содержит эти команды, и их можно оптимизировать при необходимости.
Новые объекты и атомарные единицы могут быть сохранены путем вставки строк в наборы данных (data sets) и таблицы представляющие объект в метабазе. Экземпляры этих таблиц могут быть извлечены из объекта соединения (connection object) или адаптеров данных (data adapters) и иметь соответствующие структуры и метаданные. Для обработки update запросов адаптер данных будет использовать указанные в нем политики выбора шардов для сохранения указанных данных или атомарных единиц, так же, как и генерирование новых идентификаторов для атомарных единиц.
4.3 Концепция использования
Ниже описаны необходимые шаги для использования sharding библиотеки.
4.3.1 Создание соединений
Класс соединения реализован для шардов. Во время инициализации он предоставляет ссылку на каталог метабазы шарда (вместо строки соединения) которая считывается и используется для предоставления управления соединением процессу шардинга. Соединения явным образом не управляются приложением, класс соединения создан для оптимизации управления соединениями между шардами от имени приложения.
4.3.2 Адаптеры данных
Любая манипуляция данных осуществляется через адаптеры данных (Data Adapter). Эти адаптеры содержат в себе информацию их каталога метабазы шарда от объекта соединения, и используют эту информацию для построения запросов и выполнения их согласно заданным стратегиям. У адаптера данных для шарда прописаны Delete, Insert, Update и Select команды в виде соответствующих свойств.
4.3.3 Манипуляция данными
Данные могут быть извлечены путем составления дерева выражений и внесения этого дерева в адаптер данных, который в свою очередь разберет его, сгенерирует соответствующий T-SQL и запустит его к необходимым шардам. Базовые запросы работают только к одной таблице. Класс адаптера данных (Data Adapter) предоставляет методы, которые можно использовать для простого генерирования базовых деревьев выражений, которые используются для связки выражений и свойств сущности в адаптере.
Хорошая практика для базового запроса – это найти ключ шарда атомарной единицы путем сопоставления первичных свойств сущности (entities properties) к значениям для того, чтобы на выходе получить единую сущность с ключом шардинга атомарной единицы. Второй вариант – это извлечь все сущности для указанного ключа шарда из атомарной единицы.
Добавление осуществляется путем создания в адаптере данных (Data Adapter) новой таблицы данных (DataTable) для необходимой сущности, а дальше, следуя стандартному ADO.NET шаблону, создаётся новая строка, добавляется в таблицу данных (DataTable), проставляются значения в столбцах строки. Затем вызывается Update в адаптере данных (Data Adapter)
Что касается обновления данных, то это выполняется в несколько этапов. Вначале выбираются необходимые для обновления строки в таблицу данных (DataTable) с помощью базового запроса. Таблица данных (DataTable) и адаптер данных (Data Adapter) хранят в себе информацию о шардах, с которых были выбраны записи. После изменения нужных полей в таблице данных, как и в предыдущем случае, вызывается Update в адаптере данных (Data Adapter), который отправит соответствующий T-SQL на необходимые шарды.
Удаление данных можно провести двумя способами.
Первый: выбираются необходимые данные с помощью базового запроса, затем удаляются строки из таблицы данных (DataTable), после чего выполняется метод Update в адаптере данных (Data Adapter), который в свою очередь отправит T-SQL запросы на удаление данных из соответствующих шардов.
Второй: Адаптер данных (Data Adapter) позволяет провести оптимизацию, с помощью которой можно задать дерево выражений (как в сценарии базового запроса), после чего передать выражение через адаптер данных (Data Adapter) нужного шарда, который сгенерирует соответствующий T-SQL и передаст его на выполнение в необходимый шард или шарды.
4.3.4 Комплексные запросы (Соединения, группировки, агрегации, …)
Библиотека на базе ADO.NET предоставляет две реализации для выполнения комплексных запросов к шардам. Результат выполнения этих запросов будет возвращен как часть результата, который возвращает вызов метода Fill адаптера данных (Data Adapter), в объект DataSet. Возвращенными строками можно манипулировать, но они не привязаны в шардам, из которых они возвращены, а потому не могут быть использованы для изменения, удаления или добавления новых строк. Бывают два типа команд.
Первый тип команды обеспечивает выборку только из одной атомарной единицы по ключу шарда. Иными словами, запрос будет отправлен на один шард без изменения. Это можно использовать для соединения таблиц в рамках одной атомарной единицы (Например: извлечение всех продуктов, которые являются частью заказа определенного пользователя.)
Второй тип подразумевает запуск T-SQL запроса ко всем шардам, которые реализуют сущность указанной атомарной единицы. Результат с каждого шарда будет добавлен в единый результат. Например, SUM запрос выполнен на десяти шардах, соответственно, в итоге получим 10 строк с результатами. Этот результат можно обработать на стороне приложения. Это удобно, когда необходимо извлечь результат со всех атомарных единиц, хранимых во всех шардах.
4.3.5 Транзакции
Транзакции поддерживаются в рамках стандартного шаблона программирования ADO.NET, но применимы к запросам в рамках одной атомарной единице. Это связано с политиками, согласно которым, транзакции работаю только в рамках одного физического шарда.
4.3.6 Массовое внесение данных
Для массового внесения данных необходимо писать скрипт или приложение, которое сможет считать входные данные для импорта в таблицы данных (DataTable), которые созданы на базе адаптера данных (Data Adapter).
4.3.7 Отчетность
Отчетность в случае с шард архитектурой является достаточно трудоемким процессом ввиду того, что данные разнесены между несколькими шардами и по факту системы отчетности не знают, как извлекать данные при такой архитектуре. Как вариант, можно использовать ADO.NET библиотеку для извлечения информации со всех шардов и передачи ее в систему отчетности.
5 Применение описанной ADO.NET шардинг библиотеки
Ниже представлены примеры использования ADO.NET шардинг библиотеки для multi-master архетипа, который был описан ранее в этом руководстве.
В целях демонстрации будет использован типовой для социальных сетей сценарий: Пользователь системы может изменять свой статус и добавлять фотографии на свой аккаунт, чтоб делится ими с другими пользователями. Хранилище данных должно быть масштабируемым, т.к. предполагается привлечение миллионов пользователей в эту систему.
Это отличный пример для использования шард решений, т.к. д��нные могут быть разбиты и масштабироваться разными способами, которые будут продемонстрированы. Мы рассмотрим три типа стратегий распределения данных между шардами.
Общими для всех реализаций являются задачи, о которых мы сейчас поговорим. Первая – это определение сущностей, которые будут хранится в шардах. Для упомянутой выше задачи они могут быть такими:
Сущность
Атрибуты
Типы данных
User
User ID
Big Integer
E-mail Address
Nvarchar(50)
Password
Status
Status ID
Text
Nvarchar(142)
Time
Date / Time
Photo
Photo ID
Blob URL
Nvarchar(250)
Вторая задача – это создание шардов, которые будут использоваться для хранения наших сущностей. Каждый шард – это одна SQL Azure база данных, которая может быть создана разными способами, включая портал администрирования SQL Azure или DDL. Для нашего примера будем предполагать, что они уже созданы, а строки подключения у них такие:
ID Шарда
Строка подключения
0
Server=tcp:xxxxxx.database.windows.net;Database=Shard1;User ID=adminuser@ xxxxxx;Password=myPassword;Trusted_Connection=False;Encrypt=True;
1
Server=tcp:xxxxxx.database.windows.net;Database=Shard2;User ID=adminuser@ xxxxxx;Password=myPassword;Trusted_Connection=False;Encrypt=True;
(учтите, что имена серверов придуманы, и не являются реальными).
Для обеспечения простоты нашего решения мы будем использовать два виртуальных шарда (которые соответствуют физическим шардам).
Далее нужно определить атомарную единицу и ключ шарда. В данном примере одна атомарная единица будет содержать все три сущности. Атомарная единица будет иметь свойство UserID из сущности User, которое является ключом шарда, и оно же будет ключом для атомарной единицы.
Во время создания таблиц, которые будут представлять наши три сущности, нужно добавить колонку, которая будет содержать ключ шарда и должна быть объявлена как внешний ключ к свойству UserID сущности User.
После того, как сущности, атомарные единицы и ключи шардов определены, можно приступить к созданию объектов базы данных. Для этого необходимо запустить ниже приведенные T-SQL запросы на всех шардах для создания этих объектов.
CREATE TABLE [dbo].[User](
[UserID] [bigint] NOT NULL,
[EmailAddress] [nvarchar](50) NOT NULL,
[Password] [nvarchar](50) NOT NULL
CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED
(
[UserID] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF))
CREATE TABLE [dbo].[Status](
[StatusID] [bigint] NOT NULL,
[Text] [nvarchar](142) NOT NULL,
[UserID] [bigint] NOT NULL
CONSTRAINT [PK_Status] PRIMARY KEY CLUSTERED
[StatusID] ASC
ALTER TABLE [dbo].[Status] WITH CHECK ADD CONSTRAINT [FK_Status_User] FOREIGN KEY([UserID])
REFERENCES [dbo].[User] ([UserID])
CREATE TABLE [dbo].[Photo](
[PhotoID] [bigint] NOT NULL,
[BlobURL] [nvarchar](255) NOT NULL,
CONSTRAINT [PK_Photo] PRIMARY KEY CLUSTERED
[PhotoID] ASC
ALTER TABLE [dbo].[Photo] WITH CHECK ADD CONSTRAINT [FK_Photo_User] FOREIGN KEY([UserID])
Теперь у нас готова физическая структура, обеспечивающая поддержку шардинга для приложения. В реальных сценариях эти конфигурации (или их части) должны хранится в конфигурационных файлах или доступны через веб сервис для объектов, которые будут подключатся. Это необходимо чтоб они (объекты, которые подключаются) имели возможность загружать эти конфигурации и иметь представление о структуре шардинга. Конечно, помимо конфигураций, необходимо каким-то образом еще хранить и стратегии шардинга, но это не нужно в нашем сценарии и будет рассмотрено в следующих руководствах.
Приложение будет использовать подключение к конкретным шардам для загрузки информации из конфигурационного файла, веб сервиса или базы данных. Эта информация содержит сущность, атомарную единицу, ключ шарда и все стратегии для управления шардами. Объект соединения имеет необходимую информацию для создания других объектов ADO.NET, необходимых для работы, но в первую очередь – это адаптер данных (Data Adapter), DataSet и DataTable, которые конфигурируются приложением в соответствии с конфигурацией в каталоге метабазы шарда.
Следующий шаг – это добавление одной или более первичных сущностей в пустые шарды. Они должны быть созданы в первую очередь, так как свойство UserID сущности User является ключом шарда и необходим для хранения сущностей status и Photo. В данном случае, ID будет генерировать приложения согласно своим стратегиям, но в иных случаях они могут быть сгенерированы sharding библиотекой.
Для добавления первичной сущности, приложение должно создать экземпляр адаптера данных (Data Adapter) для определенного шарда. В конструктор класса адаптера данных (Data Adapter) нужно передать объект соединения для того, чтоб адаптер считал метаданные шарда и мог работать с ним. После этого необходимо вызвать метод в адаптере данных (Data Adapter), который вернет объект DataSet, который содержит таблицу, описывающую объект User.
Этот DataSet можно использовать для добавления новых пользователей путем добавления новых строк и вызова метода Update в адаптере данных (Data Adapter). Адаптер данных (Data Adapter) в свою очередь, используя стратегии, описанные в метаданных шарда, добавит сущность User в соответствующие шарды.
Связанные сущности (photo, status) могут быть созданы аналогичным образом, путем создания новых строк в соответствующих таблицах. Каждая строка должна содержать соответствующий ключ шарда, который является ID пользователя. При обновлении этих сущностей ключ будет использоваться для маршрутизации запросов на соответствующие шарды.
5.1.1 Циклическое сохранение новых сущностей
Этот прием позволяет равномерно распределить данные по шардам используя циклический процесс. Он использует ключи к сущностям, которые хранит приложение. Поиск первичной сущности осуществляется по атрибуту, а не по ключу; для этого необходим циклический выбор стратегий, так как связки ключей и шардов не доступны при циклическом процессе.
Генерация ключей осуществляется приложением по единому алгоритму для всех сущностей. Ввиду того, что приложение хранит ключи, и стратегия выбора будет циклической, нет необходимости в функциях распределения.
Определения стратегий:
Стратегия
Методика
Аргументация
Генерация ключа
Приложение
Приложение хочет контролировать назначение ID для сущностей и атомарных единиц.
Доступ
Параллельный
Для обеспечения циклического процесса выбора необходим запуск одного запроса ко всем шардам ввиду того, что нет возможности определить шард по ID.
Выбор
Циклический
Циклическая запись новых сущностей в виртуальные шарды для равномерного распределения их по физическим шардам
Развязка
Все шарды
При циклическом выборе данные будут запрошены у всех шардов для определения, какие сущности в каких шардах хранятся.
Комбинирование
Запрос определенной сущности вернется из определенного шарда, но если мы запрашиваем несколько сущностей, нам бы хотелось комбинировать результат
Процесс добавления данных такой же, как был описан во введении. Инициализируем объект соединения к определенному шарду, создаем адаптер данных (Data Adapter) для этого шарда, содержащий уже инициализированный объект соединения, после чего создается DataSet с помощью адаптера данных (Data Adapter).
В нашем сценарии мы бы хотели создать две сущности User и сохранить их в наших шардах с помощью циклического процесса. Это можно сделать путем создания двух строк в User DataTable, проставив соответствующие значение столбцов, после чего вызвать метод Update в адаптере данных (Data Adapter). Адаптер данных (Data Adapter) в свою очередь будет в цикле, методом перебора, добавлять новые строки во все шарды.
Добавление сущностей Status и Photo может быть осуществлено путем создания новых строк в объекте DataTable и указания UserID соответствующего пользователя (UserID является ключом шарда). Адаптер данных (Data Adapter) после вызова метода Update на основании UserID добавит необходимые строки в соответствующие шарды.
5.1.2 Генерация атомарных ключей, хешированное распределение
Этот сценарий отличается от сценария, описанного в п. 5.1.1 тем, что здесь ключ шарда будет генерировать библиотека шардинга, а для определения шарда будет использоваться стратегия на базе извлечения хеша из ID.
Описание стратегий:
Последовательная (тип данных – Big Integer)
Min: 0, Max: Max Big Integer
Последовательный
Шард можно определить на базе извлечения хеша из ID для определения виртуального шарда. Нет необходимости в параллельном доступе.
Хеш ID
ID будут по модулю 2 (для двух виртуальных шардов)
ID
Применение хеш идентификатора к ID для определения шарда.
Первый результат
Вернутся должен один результат, берем первый.
Ключи шардов будут генерироваться библиотекой последовательно, обеспечивая уникальность ключа независимо от шарда. Работа с сущностями такая же, как была описана в п. 5.1.1 за исключением того, что нам не надо указывать ID для новых сущностей. Система сама проставит необходимый ID, и при вызове метода Update адаптер данных (Data Adapter) сам проставит этот ID. В нашем случае при записи первых двух сущностей им будет присвоен ID 0 и 1 соответственно. Первая сущность запишется в шард 0, вторая – в шард 1.
5.1.3 Генерация ключей атомарных единиц и шардов по диапазонам
Этот сценарий можно назвать модификацией сценария, описанной в п. 5.1.2. Выбор шарда осуществляется на базе заданных диапазонов ключей для виртуальных шардов. Каждый виртуальный шард должен бать сконфигурирован на использование определенного диапазона ключей. Например: шард 0 использует диапазон ключей от 0 до 99, а шард 1 – от 100 до 199.
Стратегия конфигурации будет отличатся от п. 5.1.2 по выбору, развязке и комбинированию:
На базе диапазонов
Виртуальный шард 1 -> 0 .. 99
Виртуальный шард 2 -> 100 .. 199
На базе ID
Используем стратегию выбора для поиска шарда.
Первый выбранный
Данные приходят с одного конкретного шарда.
Логика будет такой же, как и в п. 5.1.2, за исключением сохранения сущностей: при записи 200 пользователей, первые 100 запишутся в шард 0 (под ID от 0 до 99), остальные 100 в шард 1.
6 SQL Azure Federations
На текущий момент поддержка шардинга добавлена в SQL Azure в виде Федераций (Federations). Федерации – это коллекция слабо связанных шардов, которые хранят данные. Каждый шард (член федерации) реализован как отдельная база данных SQL Azure. Существует так же корень федерации, который содержит метаданные о членах федерации (пользователи и т.д.), на каком сервере они находятся и операции, которые можно над ними проводить.
Ниже приведена концептуальная схема SQL Azure федераций:
Рисунок 3: SQL Azure федерации
Точкой входа является корень федерации. Приложение соединяется к нему как к обычной базе и может манипулировать его объектами, перепрописывать T-SQL команды для перенаправления соединений к индивидуальным шардам на основе информации, которая хранится в схеме федерации. На основании этих изменений SQL Azure может объединять и разъединять шарды. При соединении к корню федерации и выбора шарда на основании ключа федерации на стороне SQL Azure происходит динамическая перемаршрутизация запросов к необходимым шардам, в которых хранится необходимая информация. Имеется возможность так же добавлять и удалять шарды на лету, соединится с конкретным шардом вместо соединения со всеми.
Схема федераций хранит информацию о том, как данные будут распределятся между одной или более логических баз SQL Azure. В схеме хранятся ключи федераций, типы данных ключей, и тип распределения ключа. Для одной федерации может существовать только одна схема федерации, а корень федерации может содержать более одной федерации, что удобно для определения в ней нескольких шардов (федераций) с разными ключами федераций.
Каждая федерация состоит из одного или более членов федерации. Существование членов федерации обусловлено несколькими причинами: Во первых, они содержат маршруты данных к физическим базам. Каждый член федерации реализован на отдельной базе SQL Azure.
Член федерации имеет одну или более таблиц, которые содержат ключ федерации. Записи в каждой таблице с одним ключом федерации всегда будут хранится в одном члене федерации и называться блоком федераци (federation unit) (Аналог атомарной единицы). Блоки федераций не могут быть разделены, что обеспечивает хранение этого блока в одной физической базе данных. Каждый член федерации может хранить информацию одного или более блоков федерации насколько это позволяет размер физической базы данных.
Член федерации может содержать не федеративные таблицы (таблицы без ключа федерации). Они существуют как обычные таблицы в базе данных. Эти таблицы можно использовать как справочники для федеративных таблиц (Например, если в каком-то после федеративной таблицы нужно хранить значение, которое пользователь на веб странице выбирает из списка, есть смысл вынести этот список в отдельную таблицу, пронумеровав каждый член этого списка, а в федеративной таблице хранить лишь номер выбранного значения).
Для определения, какую запись хранит блок федерации можно использовать ключ федерации или по диапазону, в который входит этот ключ. При создании новой федерации в корневой базе федераций, необходимо указать минимальное и максимальное значение ключей федерации и один член федерации, а федеративный блок будет содержать один из ключей указанного диапазона.
Член федерации может быть разделен на две федерации с помощью T-SQL команды «split», при выполнения которой будет создана физическая база данных, а данные переместятся с исходной базы исходя из того, какие были определены параметры команды «split».
Результат выполнения команды «split» будет разделение одной физической базы на две, а данные будут разнесены между ними. Во время разнесения данных SQL Azure будет контролировать целостность данных до тех пор, пока разнесение не будет завершено.
Так же поддерживается и выполнения команды «merge», которая позволяет объединить две федерации в одну, удалив при этом одну из баз. Как и в случае с командой «split», SQL Azure будет контролировать целостность данных до тех пор, пока объединение не будет завершено.
Динамическое добавление и удаление шардов может быть выполнено приложением посредствам T-SQL на основе анализа операционной информации в схеме федерации из корневой базы. В дальнейшем этот процесс может быть автоматизирован в ходе развития SQL Azure.
При построении приложения используя шардинг необходимо учитывать описанные выше особенности. Они могут изменится по мере развития SQL Azure, но используя шаблоны, описанные выше можно уже сейчас строить такие системы на базе SQL Azure Federations, которая уже доступна.
7 Итог
В описанном руководстве мы рассмотрели реализацию шардинга с помощью SQL Azure, построение библиотеки на базе ADO.NET для реализации рекомендованных шаблонов шардинга с SQL Azure.
Так же были описаны основные концепции перераспределения, управление схемами, добавления и удаления шардов. В дальнейшем, эти концепции будут освещены более детально.
Следите за новыми возможностями на: http://blogs.msdn.com/sqlazure/