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

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

Зайдем на портал Windows Azure и создадим новую базу данных AdventureWorksFed, как описывалось <a href="http://blogs.technet.com/b/isv_team/archive/2012/01/28/3477624.aspx">здесь</a>. Она будет корнем будущей федерации. Корень является логической точкой входа, который знает, как устроена федерация и маршрутизирует соединения к соответствующему шарду на основе значения ключа. Также корневая база хранит другие метаданные: имена пользователей, пароли, роли и т.д. Заходим в администрирование базы, как описывалось <a href="http://blogs.technet.com/b/isv_team/archive/2012/01/29/3477711.aspx">здесь</a> и нажимаем кнопку New в верхнем меню. Создаем схему федерации, задавая ее имя, а также имя и тип ключа. В диалекте облачного T-SQL этому действию соответствует команда CREATE FEDERATION. Очень похоже на создание схемы партиционирования. На данный момент поддерживаются типы INT, BIGINT, UNIQUEIDENTIFIER и VARBINARY(n), где n не может превышать 900. Из способов распределения на данный момент единственно возможный - по диапазонам (range), как и в партиционировании.

 

image001 

Рис.1

 

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

 

image002

Рис.2

 

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

 

image003

Рис.3

 

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

 

image004

Рис.4

 

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

 

image005

Рис.5

 

image006

Рис.6

 

Принципиальным моментом выступает предикат federated on, привязывающий ключ таблицы (ID) к ключу федерации (cid). Для наполнения созданной таблицы выберем один из рассмотренных ранее способов. Я, в частности, воспользуюсь <a href="http://blogs.technet.com/b/isv_team/archive/2012/05/03/3495835.aspx">SSIS</a>, чтобы перенести результаты запроса

 

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

 

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

 

image007

Рис.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

 

image008

Рис.8

 

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

 

image009

Рис.9

 

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

 

image010

Рис.10

 

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

image011

Рис.11

 

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

 

image012

Рис.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, позволяющая объединять две соседних партиции. Пожелания о введении подобного оператора со стороны сообщества пользователей <a href="https://social.msdn.microsoft.com/Forums/pl-PL/ssdsgetstarted/thread/ec8cbc2d-a20b-481c-bfcf-fd53d4ab03b6">продолжают высказываться</a>. Пока единственный способ состоит в том, чтобы дропнуть член федерации, предварительно сохранив с него данные, которые потом залить на один из оставшихся.