Горизонтальное масштабирование облачного SQL Server (шардинг)

Шардинг является в известном смысле аналогом партиционирования в SQL Azure. Партиционирование в on-premise SQL Serverпозволяет распределить таблицу по файл-группам (след., по дискам) в пределах одной базы. Шардинг в SQL Azure используется для распределения таблицы по федерации облачных БД. Как мы знаем, сервер SQL Azure выступает логической сущностью, и находящиеся под его управлением базы данных будут, скорее всего, расположены на различных физических серверах. Федерация представляет собой набор партиций, определяемых схемой распределения федерации. Схема федерации задает ключ, на основе которого происходит разбиение. Федерирование оказывается полезным в ряде практических сценариев, например, чтобы превзойти ограничение на размер облачной базы (на момент написания поста 150 гиг), разбив массивную таблицу заказов по их номерам, или в мультитенантном сценарии, когда каждый CustomerID хранится на отдельном сервере, и т.д.

Зайдем на портал Windows Azure и создадим новую базу данных AdventureWorksFed, как описывалось здесь. Она будет корнем будущей федерации. Корень является логической точкой входа, который знает, как устроена федерация и маршрутизирует соединения к соответствующему шарду на основе значения ключа. Также корневая база хранит другие метаданные: имена пользователей, пароли, роли и т.д. Заходим в администрирование базы, как описывалось здесь и нажимаем кнопку New в верхнем меню. Создаем схему федерации, задавая ее имя, а также имя и тип ключа. В диалекте облачного T-SQL этому действию соответствует команда CREATE FEDERATION. Очень похоже на создание схемы партиционирования. На данный момент поддерживаются типы INT, BIGINT, UNIQUEIDENTIFIER и VARBINARY(n), где n не может превышать 900. Из способов распределения на данный момент единственно возможный  - по диапазонам (range), как и в партиционировании.

 

Рис.1

Обратите внимание, что вместе со схемой автоматически создался первый член федерации - служебная база данных по имени system-<guid>. Она не видна на портале в списке пользовательских баз. Чтобы ее увидеть, нужно соединиться с БД master и выполнить команду select * from sys.databases. Поле is_federation_member имеет у нее значение true.

Рис.2

Возвратимся в корень федерации - БД AdventureWorksFed. Ознакомиться с метаданными федерации можно при помощи системныхпредставлений sys.federations, sys.federation_distributions, sys.federation_member_distributions order by federation_id, range_low. Так, мы видим, что на данный момент она состоит из одного члена, готового вместить данные, начиная с минимального значения bigint(range left в терминах партиционирования).

Рис.3

Список федераций показывается на портале Windows Azure в Administration корневой базы:

Рис.4

Кликнем на стрелку напротив единственной федерации в БД AdventureWorksFed и создадим на ней таблицу Customer, которая будет автоматически распределять свои данные по ее членам:

Рис.5

Рис.6

Принципиальным моментом выступает предикат federated on, привязывающий ключ таблицы (ID) к ключу федерации (cid). Для наполнения созданной таблицы выберем один из рассмотренных ранее способов. Я, в частности, воспользуюсь SSIS, чтобы перенести результаты запроса

select CustomerID, AccountNumber, format(ModifiedDate, 'yyyy-MM-dd') as ModifiedDate from Sales.Customer

в созданную на Рис.6 в Облаке Table1. 

Рис.7

Всего в таблице Table1 содержатся клиенты с ID от

select min(ID), max(ID) from Table1 ------------------------------------------------------ 1 30118

Найдем в этом диапазоне границу, чтобы слева и справа от нее находилось примерно одинаковое количество записей.

with cte as (select ID, ntile(2) over (order by ID) ntile from Table1) select ntile, count(1), max(ID) from cte group by ntile

Рис.8

Рассплитим таблицу Table1 по границе 20208. Вместо одного члена федерации образуется два, данные (и нагрузка) будут разделены между ними. 

Рис.9

Сплит занимает некоторое время:

Рис.10

После чего по нажатию кнопки Refresh можно видеть, что таблица состоит уже из двух партиций:

Рис.11

Метаданные первого члена федерации (подобно Рис.3):

Рис.12

Аналогично, для второго:

USE FEDERATION CustomerFederation(cid=25000) WITH RESET, FILTERING=OFF GO SELECT db_name() db_name SELECT * FROM sys.federation_member_distributions SELECT MIN(ID) [CustomerID Low], MAX(ID) [CustomerID High] FROM Table1 db_name system-52d6b399-6425-4cfa-a16d-b64944637acb federation_id member_id distribution_name range_low range_high 65536 65538 cid 20208 NULL CustomerID Low CustomerID High 20208 30118

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