Шардинг базы данных – это прием, который позволяет разбивать данные между разными физическими серверами (горизонтальное масштабирование), что позволяет создавать масштабируемые приложения или сервисы.

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 и как он будет развиваться в ближайшем будущем. В следующих руководствах будут описаны дополнительные аспекты шардинга более подробно.

Кроме того, в этом руководстве будут освещены следующие темы:

  • Основные концепции горизонтального разбиения данных и шардинга
  • Проблемы, которые могут возникнуть при разработке шард приложений.
  • Типовые сценарии использования шардинга
  • Преимущества использования SQL Azure для разработки шард приложений
  • Высокоуровневый дизайн библиотеки на основе ADO.NET для работы с шардингом.
  • Введение в SQL Azure Federations.

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

Шардинг (Sharding) – это шаблон разработки приложения для обеспечения масштабируемости решений с большим объемом данных. Шардинг приложения – это процесс разбиение логических баз на маленькие куски (Shard в переводе с английского – это «Осколок») и разнесение этих кусков между разными физическими базами данных, чтоб обеспечить масштабируемость. Каждая физическая база данных в этой структуре и есть Shard (“Шард”).

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

Процесс шардинга предполагает разнесения данных между шардами путем применения одной или нескольких стратегий формирования ключа шардинга (sharding key), который является первичным ключом сущности, которая хранится в шарде. Связанные сущности группируются в связанный набор данных по заданному ключу, и этот набор называется атомарной единицей. Иными словами, Атомарная единица (Atomic Unit) – это набор сущностей с одинаковым значением ключа шардинга и имеющих свой собственный ключ атомарной единицы. Все сущности атомарной единицы всегда хранятся в одном шарде.

Атомарная единица является основной архитектурной концепцией, которая используется sharding инфраструктурой в качестве основы как для последовательного доступа к данным, так и для динамического добавления и удаления шардов. Шард инфраструктура обеспечивает, что все записи в атомарной единице всегда будут находится в пределах одного физического шарда. Это облегчает соединение и агрегирование, а также является ключевым преимуществом для использования SQL Azure Federations, которая в свою очередь реализует шардинг инфраструктуру для гибкого масштабирования решений.

Преимущества шардинга с SQL Azure

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

  • Масштабирование используя десятки, сотни и тысячи узлов баз данных, построенных на недорогом оборудовании вместо исполь��ования дорогих и высокопроизводительных серверов.
  • Достижение нужной производительности путем увеличения количества узлов.
  • Построение относительно дешевых решений с большим объемом данных.

Действительно, можно строить шард решения на своих собственных площадках. Многие так и делают. Но у этого подхода есть и ряд недостатков:

  • Если управление масштабированием сделано на уровне приложения, необ��одимо учитывать все детали разбиения данных (приложение должно управлять всеми параметрами, обеспечивающими масштабирование)
  • Доступность обеспечивается на уровне приложения, а не на уровне оборудования.
  • Перераспределение шардов является сложной задачей и происходит в офлайне,
  • Большие затраты на администрирование.
  • Большие затраты на оборудование.

SQL Azure предоставляет уникальную возможность решить эти вопросы используя преимущества, которые предоставляются облачной платформой Azure как сервис.

 Ключевые преимущества:

  • Готовая инфраструктура







    SQL Azure не требует администрирования и обрабатывает все задачи по обеспечению работы серверов такие как патчинг, хранилище, в то время как пользователю необходимо лишь обеспечивать администрирование логической базы данных.
  • Гибкое управление узлами







    Создание нового шарда (физической базы данных) и включение его в приложение может быть осуществлено через Windows Azure Management Portal или DDL. Это избавляет вас от необходимости терять несколько месяцев на приобретение, настройку, развертывание нового оборудование и систем баз данных. Кроме того, если для вашего приложения необходимы десятки, сотни или тысячи баз данных на небольшой период времени, с помощью SQL Azure добавить их можно легко и быстро. А когда они не нужны – быстро их удалить.
  • Оплата только реально используемого ресурса (Pay-as-you-go)







    SQL Azure имеет линейную стоимостную модель, что является привлекательным для построения шард решений, т.к. сумма к оплате за месяц будет пропорциональна суммарному используемому объему баз данных. Это позволяет клиенту прогнозировать свои затраты. Кроме того, существует несколько редакций баз данных (Web и Busines), в которых ограничен верхний предел объема базы данных.
  • Высокая доступность







    SQL Azure предоставляет высокую доступность (SLA: 99.9%) для всех баз данных, нет необходимости самостоятельно реализовывать RAID или другие приемы обеспечения доступности.

Однако, на сегодняшний день при проектировании шардинг архитектуры на базе SQL Azure нужно быть осторожным в следующих нюансах:

  • Индивидуальные базы SQL Azure ограничены в ресурсах







    Есть практические и технические ограничения в базах SQL Azure на максимальный размер одной базы. Хотя и размеры эти постоянно увеличиваются, они, вероятно, все равно будут меньше, чем требуется для решений с шардингом.
  • Ограничение соединений







    База данных SQL Azure обеспечивает полноценное многопользовательское обслуживание баз данных на основе общих источников. Чтобы обеспечивались хорошие условия работы для всех клиентов базы данных SQL Azure, соединение клиента со службой может быть закрыто при возникновении следующих условий:
  1. Чрезмерное использование ресурсов
  2. Соединения, неактивные в течение 30 минут и более
  3. Возникновение отказа в результате сбоя сервера
  • Sharding с SQL Azure реализуется на уровне приложение







    Текущая версия SQL Azure не предоставляет специальных средств для построения Sharded приложений, однако большинство Sharded приложений можно реализовать на базе SQL Azure Federations, что по сути является одной из реализаций шардинга.
  • Перераспределение шардов – это офлайн процесс.







    Добавление или удаление шардов может стать сложной задачей, так как правила поиска данных должны быть изменены (перераспределены ключи и т.д.) вместе с изменением физической инфраструктуры.

Для облегчения этих трудностей и обеспечения гибкого шардинга часть необходимых возможностей уже добавлена в 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), который будет обеспечивать доступ к данным, распределенным между шардами и решать следующие задачи:

  • Распределенный доступ к данным







    Ключевым моментом в шардинге является добавление и извлечение данных из нескольких экземпляров баз. Распределенный характер архитектуры влияет на методы доступа к данным (создание, чтение, обновление и удаление) и шардинг система должна иметь возможность знать, каким образом манипулировать данными в разных базах. Для этого приложение должно уметь определить:
    • В каком шарде(или шардах) должен быть сохранен объект.
    • Помнить в каком шарде сохранен объект, чтоб была возможнос��ь его извлечь, изменить или удалить.
  • Определение, в каком шарде должен быть сохранен новый объект







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







    Эффективное извлечение данных усложняется необходимостью определить шард, в котором хранится объект и зависит от правил, которые использует приложение для определения шарда с необходимыми данными. Приложению каким-то образом необходимо определить ключ к данным, а затем вывести шард с помощью одного или нескольких алгоритмов, или грубо использовать несколько параллельных запросов к нескольким шардам.
  • Генерация ID, который может быть использован для эффективного добавления или извлечения данных.







    Для сохранение и вывода объектов в приложениях может быть использован сгенерированный ID для них, на основании которых система может манипулировать этими объектами. Как вариант – можно использовать хешинг или маппинг функции, либо какие-то специфические для конкретного приложения алгоритмы генерирования ID.
  • Параллельный или последовательный запуск запросов к шардам







    Если необходимо запускать несколько T-SQL команд для опроса всех шардов, можно использовать параллельные или последовательные запросы. При этом выбор зависит исключительно из архитектуры приложения и его производительности. Так, например, при запуске параллельных запросов необходимо побеспокоится о много поточности приложения, о обработке вывода конкурирующих данных и т.д.
  • Направление данных к шардам







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







    В шард приложениях, запросы использующие joins, которые обращаются к разным шардам, являются слож��ыми и не эффективными. Существует несколько приемов построения Join запросов, но при условии, что приложение постоянно растет и количество шардов только увеличивается, весьма сложно определить, какие шарды необходимо включать в Join запрос.















    Есть три часто используемых приема:
    • Денормализация







      Денормализация – это сведение данных в один шард, для возможности их удобной выборки Join запросом.
    • Справочные таблицы







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







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







    Связывание данных может быть трудным в sharded системах, как и joins между шардами, которые в SQL Azure не поддерживаются на уровне облачной инфраструктуры. Ключи к данным должны генерироваться поверх базы данных (например на уровне приложения). Необходимо учитывать следующее:
    • Связи данных подразумевают соединение (Joins) разных таблиц / сущностей. В sharded решении таблицы могут находится в разных шардах, что порождает те же вопросы, которые были описаны в предыдущем пункте.
    • Ссылочная целостность не может быть обеспечена на основе базы данных, если соответствующие сущности с первичным или внешним ключом разнесены по шардам.

Поэтому приложения, построенные для управления шардингом, вместо связей таблиц могут применять следующие приемы:

  • Широкое использование денормализованых данных
  • Хранить связанные данные в одном шарде
  • Самостоятельно обеспечивать связи таблиц (например на уровне приложения)
  • Игнорировать тот факт, что сохраняемые данные могут нарушить уникальность записей. Но при этом обеспечить задачи очистки лишних данных.
  • Перераспределение шардов







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















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















    Для облегчения перераспределения, на основе ADO.NET библиотеки можно реализовать концепцию, известную как виртуальные шарды, которые позволяют изолировать влияние добавления или удаления шардов, а новые возможности SQL Azure позволят это делать в онлайн режиме.
  • Защита







    Шард приложения используют множество баз, разнесенных на множестве серверов. Соответственно, необходимость управлять разными аккаунтами является нормальным явлением (у каждого шарда должен быть свой логин и пароль для доступа к серверу). Это может быть громоздким в случае, если количество шардов динамически изменяется, и необходимо управлять большим количеством аккаунтов для того, чтоб иметь доступ к данным (Например, сущности хранятся на шарде А, после перераспределения шардов, они попали на шард B, соответственно для доступа к этим сущностям необходимо обращаться с логином и паролем к серверу B).







    SQL Azure предоставляет элегантное решение этой проблемы. Используя аккаунт SQL Azure можно создавать множество баз с правами, прописанными в «master» базе для аккаунта. Каждая индивидуальная база данных на аккаунте SQL Azure доступна по одному и тому же имени сервера, но с каталогом на разных нодах, и с одним аккаунтом для всех реплицированных инстансов, прописанных в «master» базе данных. Все что нужно изменить в строке подключения к каждому шарду – это имя каталога этого шарда (параметр «Initial Catalog»).
  • Массовое внесение данных







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







    Сортировка и агрегация – очень сложная задача для шард приложения потому, что необходимо запускать выборку и сортировку к каждому индивидуальному шарду, а затем полученные данные сортировать и агрегировать относительно друг друга.















    При использовании таких команд группировки, как SUM или AVERAGE, индивидуальные наборы записей с разных шардов должны быть идентифицированы, коррелированы с такими же результатами из других шардов, и в конце концов соединены. Дополнительно к полученным результатам должны быть запущены группировочные запросы.















    SUM и AVERAGES являются двумя разными по классу функциями, к которым должны быть применены разные алгоритмы пост-обработки. SUM подобные функции являются относительно простыми и все что нужно, повторно запускать их к полученным результатам. AVERAGE подобные функции требуют дополнительной информации перед повторным применением над полученным результатом. Исходя из этого, необходимо получить не только результат выполнения функции, но и количество строк в результате ее выполнения с каждого шарда, чтоб результат можно было скомпоновать.















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















    Для более сложных сценариев отчетности есть смысл выгружать экстракты шардов в отдельную базу и уже по ней делать необходимое выборки.
    • SQL Azure база данных существует в трех копиях, что обеспечивает высокую доступность. Если резервное копирование необходимо только для обеспечения доступности, в данном случае, в нем нет необходимости.
    • SQL Azure предоставляет инструмент для копирования баз данных (см. Copying Databases in SQL Azure ) который может быть использован для резервного копирования отдельных шардов. Однако копирование баз данных работает только в рамках одного дата цента в облаке и не может быть использовано для аварийного восстановления данных.
  • Управление соединениями







    Шард приложение должно поддерживать множество соединений с базами данных, и по мере увеличения количества шардов (и пользователей) увеличивается количество соединений к базе данных. Это может быть слишком ресурсоемко. Поэтому важно, чтобы приложение умело повторно использовать открытое соединение к базе данных (но в этом месте нужно быть внимательным и прочитать информацию об ограничениях SQL Azure относительно соединений. Более детальную информацию можно найти тут).















    Сложность управления соединениями увеличивается при использовании гибкого масштабирования шард архитектуры на базе SQL Azure. Шарды добавляются и удаляются, и данные перераспределяются между шардами, каждый отдельно взятый кусок данных может быть перемещен между шардами. Во время перемещения приложение может потерять эти данные, т.к. для извлечения данных приложению необходимо знать, откуда их запрашивать, а при перемещении изменится и строка подключения (В случае с SQL Azure изменится каталог, если базы создавались под одним аккаунтом SQL Azure).















    С ADO.NET, каждая уникальная строка подключения создает свой пул соединений, увеличение которых может привести к превышению порога ресурсов на стороне клиента так же, как и в системах, работающих между клиентом и сервером. Для облегчения этой проблемы в будущих релизах SQL Azure планируется добавить возможность выполнения динамической маршрутизации связи непосредственно в самой инфраструктуре SQL Azure.
  • Управление схемами
  • Резервное копирование и аварийное восстановление







    Если говорить о реализации шардинг архитектуры локально, на собственных площадках, то процесс резервного копирования и восстановления данных является очень трудным. При использовании SQL Azure для шард приложений, этот процесс немного упрощается:
  • Распределенные транзакции







    SQL Azure поддерживает транзакции в рамках одного шарда (физической базы данных). Распределенные транзакции между базами не поддерживаются. Если есть необходимость использовать распределенные транзакции в шард приложении – лучше всего провести денормализацию данных.

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


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 Сущности данных и атомарные единицы

  • Шард является отдельной базой данных SQL Azure.
  • Данные можно описать как набор сущностей, содержащих атрибуты (по типу таблиц и колонок).
  • Метаданные сущности используются для генерации запросов и управление схемами между несколькими шардами.
  • Шард реализует хранилище для одного или более наборов сущностей, которые являются атомарными единицами.
  • Атомарная единица – это набор связанных сущностей.
  • Все сущности в атомарной единице связаны между собой шард ключом и ключом атомарной единицы.
  • Все экземпляры сущностей одной атомарной единицы хранятся на одном физическом шарде.
  • Все таблицы, которые реализуют сущности, должны иметь поле (колонку) для хранения общего ключа шарда.
  • Приложение объединяет свои данные в атомарные единицы, которые будут обеспечивать гибкие возможности масштабирования в SQL Azure.
  • Ключи атомарных единиц могут иметь тип данных big integer или GUID.
  • Шард, который хранит определенную атомарную единицу, может быть определен на базе стратегий выбора шарда, по которым приложение, исходя из ключа, может выбрать необходимый шард.
  • Приложение, которое использует SQL Azure для обеспечения шардинга должно использовать эластичность SQL Azure
  • Логика приложения должна предполагать что SQL Azure, или другой процесс, может переместить атомарную единицу на другой шард во время перераспределения шардов (rebalancing).
  • Набор шардов изменяется динамически, соответственно и настройки соединения к атомарным единицам будет динамически меняться.
  • Приложение всегда определяет где находятся данные вместо того чтобы использовать статический список шардов.
  • После определения атомарной единицы необходимо строить запросы, не выходящие за ее приделы, что означает в пределах одного шарда.
  • Структура атомарных единиц критична при перераспределении (разделении и объединении) шардов. Процесс перераспределения будет следить за тем, чтоб связанные в одну атомарную единицу сущности не разделились.
  • Ввиду того, что связанные в атомарную единицу сущности хранятся в одном шарде, объединения (joins) и транзакции могут работать в рамках одной атомарной единицы.
  • Приложение задает фиксированное количество виртуальных шардов. Ключи для атомарных единиц генерируются и привязываются не к физическим, а к виртуальным шардам, которые в свою очередь привязываются к физическим. Этот прием будет полезен при перераспределении шардов: вместо перераспределения ключей атомарных единиц можно будет использовать результаты функций распределения.
  • Приложения не должны зависеть от строгой логической целостности сущностей, находящихся на разных шардах, равно как и целостность не может быть проверена между шардами (Иначе говоря, не следует в приложении пытаться внедрить зависимости между сущностями или атомарными единицам, которые находятся на разных шардах. Такие связи нарушают концепцию шардинга).
  • Транзакции между разными базами SQL Azure не поддерживаются, соответственно не нужно реализовывать их на стороне приложения. Транзакции поддерживаются только в рамках атомарной единицы и ее шарда.
  • Объединения (Joins) между разными базами SQL Azure не поддерживаются, соответственно не нужно реализовывать их на стороне приложения. Объединения (Joins) поддерживаются только в рамках атомарной единицы и ее шарда.
  • Связанные данные хранятся в одном шарде. Если нужно объединить данные из разных шардов, необходимо выгрузить необходимые записи со всех шардов и объединять результат на стороне приложения.
  • Данные не группируются и не агрегируються за пределами одного шарда, но при необходимости это может быть реализовано на стороне приложения.
  • Ключи шардов не могут изменятся (Read Only), и после того, как ключ был присвоен шарду, он не может быть изменен приложением.
  • Глобальные данные хранятся в базе, не имеющей отношения к шардам (не участвующей в инфраструктуре шардинга) и обновляются вручную по результатам запроса из шардов. Справочные данные хранятся в таблицах каждого шарда без идентификатора шарда.

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, циклически и все шарды.

  • По диапазону ID







    Эта стратегия информирует приложение о том, что распределять атомарные единицы между шардами нужно на базе диапазона ID, который задан для шардов. Для использования этой стратегии генерация ключей должна быть последовательной с типом данных big integer, и для каждого виртуального шарда зарезервирована часть этого диапазона.
  • По хешу ID







    Эта стратегия информирует приложение о том, что распределять атомарные единицы между шардами нужно на базе алгоритма хеширования ID. Обычно, хешируют Integer значения на базе количества виртуальных шардов, и GUID-ы на базе алгоритма генерирования GUID, который учитывает количество виртуальных шардов.
  • Циклический







    Эта стратегия подразумевает выбор шарда по кругу из общего набора шардов. Результат – данные распределяются равномерно между всеми шардами
  • Все шарды







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

3.3.6.4 Развязка

Стратегия развязки используется для определения набора шардов, в которых атомарная единица может «жить» после ее записи. Есть две основных стратегии развязки: Все и на основании ID

  • Все







    Даная стратегия уведомляет провайдер данных о том, что нужно опросить все известные шарды для определенной сущности. Это применимо в случае, если не возможно по ключу точно определить, в каком шарде хранится сущность или атомарная единица (например при циклической стратегии выбора), или ключ не известен и необходимо строить запрос на базе атрибутов.
  • На основании ID







    Эта стратегия используется для определения шарда на базе 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 обеспечивает стандартный, привычный шаблон и набор абстракций для разработки приложений.

Специальные цели:

  • Описание библиотеки на базе ADO.NET, в которой будет реализовано много особенностей шардинга
  • Предоставить поддержку множества различных типов стратегий для генерации таблиц и ключей атомарных единиц и определение, какой шард или шарды должны сохранить или извлечь информацию
  • Автоматическая паралелизация запросов к множеству шардов.
  • Работа с ключом шарда / атомарной единицы для обеспечения хранения связанных данных в одном шарде
  • Обеспечить прочную основу для поддержки шардинг функций, которые будут введены в SQL Azure, что позволит легко и просто перейти на новые возможности SQL Azure, когда они будут реализованы, просто изменив реализацию шардинг библиотеки.

Описанные модели будут использованы в других разделах настоящего руководства. Это руководство является базовым, дальнейшее уточнение будет дано в разделе 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

Nvarchar(50)

Status

Status ID

Big Integer

Text

Nvarchar(142)

Time

Date / Time

Photo

Photo ID

Big Integer

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

 )WITH (STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF))

 

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,

    [UserID] [bigint] NOT NULL

  CONSTRAINT [PK_Photo] PRIMARY KEY CLUSTERED

(

    [PhotoID] ASC

 )WITH (STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF))

 

ALTER TABLE [dbo].[Photo]  WITH CHECK ADD  CONSTRAINT [FK_Photo_User] FOREIGN KEY([UserID])

 REFERENCES [dbo].[User] ([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/