Горизонтальное масштабирование для SQL Server баз данных: как это сделать
Если вы или ваша компания ищете возможность горизонтального масштабирования для SQL Server баз данных, тогда эта статья для вас.
Устоявшиеся привычки, дороговизна адаптации новых решений, обязывающий стек технологий — это далеко не полный список причин, по которым многие проекты продолжают работать с реляционными базами, адаптируя их под требования больших данных. В этой статье я поделюсь собственным опытом реализации и внедрения scale-out масштабирования при помощи Elastic Database Tools.
Кратко обо мне. Я Lead Developer в компании Sitecore с опытом работы в IT более 12 лет. Ex-leader Dnipropetrovsk MongoDB User Group in Dnipro, Leader Dnepr SQL User Group, докладчик на различных конференциях (SQLSaturday, MyWebTech, SitecoreSaturday). С базами данных веду плотную дружбу вот уже 7 лет.
Кратко о главном
Долгое время базы данных классифицировали по двум типам SQL и NoSQL (реляционные и нереляционные). Модные докладчики больших конференций и авторы статей использовали ‘NoSQL’ как buzzword для привлечения внимания.
Многие знакомы с этими типами хранилищ и, возможно, могут написать не один десяток статей на эту тему. Для тех же, кто ещё не успел с головой окунуться в совершенно несовершенный мир баз данных, давайте немного рассмотрим главные отличия.
Реляционные базы данных — это гигант и прародитель всех существующих и всех будущих вариаций хранения данных. SQL-хранилища позволяют хранить данные в структурированном, нормализованном виде с поддержкой комплексных транзакций и сложных JOIN-операций. Этот тип хранения отлично справляется с задачами, где консистентность данных очень важна. Дабы не быть многословной, приведу пример с банковским переводом в миниатюрном изложении. Когда денежный перевод осуществляется от клиента А клиенту Б, очень важно убедиться, что средства, списанные со счёта отправителя, успешно зачислились на счёт получателя. В данном случае на помощь приходят транзакции.
С течением времени объем и разнообразие хранимой информации выросли до невероятных масштабов. Никого уже не удивишь термином BigData (ещё одна вариация на тему buzzword). К сожалению, у SQL хранилищ с этим возникли определенные проблемы.
Классическим способом масштабирования реляционных баз данных является вертикальное масштабирование. При необходимости обрабатывать и хранить больше данных приходится расширять вычислительную мощь машины, добавлять дополнительную память, переходить на более объемные и быстродействующие диски. Естественно, такой подход имеет свои пределы и является достаточно дорогостоящим.
Альтернативой вертикальному масштабированию является горизонтальное. Его основная идея — это возможность добавлять дополнительные вычислительные узлы в так называемый кластер, агрегированная мощность и объем которых позволяют хранить и обрабатывать внушительные объемы данных. Ну правда, прикольно же: как только видим, что скоро достигнем предела, покупаем машинку, инсталим софт, подключаем в кластер — и продолжаем собирать данные?
Но не всё так просто, как кажется на первый взгляд. Во-первых, для поддержки горизонтального масштабирования необходимо было отказаться от критически важного SQL-функционала:
- Complex Transaction;
- JOIN;
- Static Schema.
Отказ от этих фичей и поддержка горизонтального масштабирования привели к поиску новых моделей и способов хранения данных и, как следствие, к новым типам хранилищ. Они получили название NoSQL (Not Only SQL) как хранилища нереляционного типа. Попросту говоря, NoSQL базы данных позволяют моделировать данные таким образом, что часть из них хранится на одной машине, другая часть — на другой машине, и вместе эти данные представляют собой одно большое физически распределённое хранилище. Таким образом, мы не ограничены физическими возможностями одной машины. Стоимость горизонтального масштабирования гораздо ниже вертикального, а также доступны практически безлимитные возможности скейлинга.
На данный момент существует огромное разнообразие NoSQL баз данных, которые также делятся на подвиды:
- Key-value;
- Column Family;
- Graph;
- Document-oriented.
Каждая из них имеет свой стек задач, свою модель хранения, свои сильные и слабые стороны.
Одним из последних трендов развития баз данных являются NewSQL хранилища со своей спецификой, объединяющей сильные стороны как SQL, так и NoSQL хранилищ, но это тема для отдельной статьи.
В этой статье я бы хотела рассказать о подходе, который мы реализовали в нашем продукте для поддержки горизонтального масштабирования для SQL баз данных. Да-да, это не ошибка, оказывается, реляционные базы данных тоже можно масштабировать горизонтально... Дальше — больше, оставайтесь с нами.
Немного истории
Тем, кто любит переходить сразу к сути, предлагаю пропустить раздел «когда мы были молодыми» и читать следующий раздел.
Мы, как и любая продуктовая компания, когда-то начинали с очень простых техник, которые со временем эволюционировали и в итоге превратились в клубок всевозможных технологий. Вы не поверите, но на заре развития нашего продукта — назовём её нулевой фазой — данные хранились в обычных
Затем произошел естественный переход на реляционные хранилища (SQL Server, Oracle), которые долгое время служили верой и правдой до того, как понадобилось хранить большие объёмы данных. На тот момент решение было очевидным: переписать всё и перейти на NoSQL базы данных, в частности на MongoDB.
MongoDB является документно-ориентированной базой данных, поддерживающей возможности гибкой схемы и горизонтального масштабирования, что решало проблему хранения большого объема разнообразной информации.
При всех достоинствах MongoDB были и некоторые сложности. На тот момент это была достаточно новая технология, требующая вложения средств в обучение персонала и затрат на деплоймент и поддержку платформы. Поиски идеального решения не прекращались...
Microsoft продолжала инвестировать средства в поиски решений проблемы горизонтального масштабирования для SQL баз данных. Спустя время были анонсированы Elastic Database Tools — набор библиотек и Azure-сервисов для возможностей горизонтального масштабирования в SQL Azure.
Теперь самое интересное, погнали...
Elastic Database Tools
Набор Elastic Database Tools состоит из следующих фичей и сервисов:
- Elastic Database Client Library;
- Elastic Database Jobs;
- Elastic Database Query;
- Elastic Database Transactions;
- Split-Merge Tool.
Несмотря на то, что этот набор фичей изначально был рассчитан на работу с SQL Azure, часть из библиотек и подходов мы смогли успешно внедрить и в On-Premise решения.
Elastic Database Client Library
Для работы с распределенными данными, как On-Premise, так и SQL Azure, мы использовали библиотеку Elastic Database Client Library. При помощи этой библиотеки был написан инструмент для создания и конфигурации sharded cluster, провайдера для работы с данными в шардах, а также, в более поздних версиях, добавили возможность горизонтального масштабирования (scale-in/scale-out).
В отличие от MongoDB, где вся работа с кластером, балансирование данных, отказоустойчивость являются встроенными фичами, не требующими ни одной строчки кода, работа с Elastic Database Tools предполагает несколько больше усилий, так как она предоставляет инструменты и библиотеки, внедрение которых не является бесплатным.
С добавлением возможности распределенного хранения данных для реляционных таблиц мы все так же работали с транзакциями и JOIN-операциями для связанных данных, однако подход к моделированию схемы базы пришлось пересмотреть. Связанные данные всегда должны находиться в одном шарде, то есть связанные таблицы должны использовать один и тот же ключ распределения по шардам. Это требование является одним из ключевых для поддержки консистентности данных и возможности использования транзакций.
Table Types
Elastics Database Client Library поддерживает два типа таблиц: sharded tables и reference tables.
Sharded Tables
Sharded Tables — это тип таблиц, которые хранят данные, распределенные по инстансам кластера по определенному ключу, его ещё называют shard key.
Мы определяем колонку таблицы, которая будет нашим ключом распределения (shard key) между шардами, определяем диапазон уникальных ключей для каждого из шардов и работаем с кластером как с единой логической базой данных. Вся мета-информация по конфигурации шардов хранится в каталоге, в самой ключевой базе данных — Shard Map Manager, но о ней чуть позже.
Важно понимать, что каждая из таблиц может распределяться по шардам индивидуально, то есть у каждой таблицы может быть собственный ключ, но при проектировании схемы по правилам Elastic Databases необходимо хранить все связанные данные, которые нужны для обработки запроса в рамках одного шарда. Другими словами, связанные таблицы должны иметь одинаковые колонки, которые и будут выступать в качестве ключа распределения, а связанные данные будут иметь одно и то же значение в ключевых колонках.
Reference Tables
Большинство баз данных содержат таблицы словарей, которые хранят ограниченный набор данных. Такие таблицы бессмысленно распределять между шардами, так как накладные расходы для работы с таким типом данных, распределенными по шардам, могут быть очень нерациональными. Для таких случаев существуют Reference Tables — это небольшие таблички, копии которых хранятся на каждом из инстансов кластера, что даёт возможность выполнять JOIN-операции в рамках одного шарда очень эффективно.
Shard Key
При проектировании распределённой базы данных ключевым моментом является анализ запросов к базе данных для определения правильного shard key для каждой из таблиц.
Запросы, в предикате которых будет использоваться shard key, будут максимально эффективными, так как запрос будет направляться и обрабатываться на определенном шарде. В противном случае запрос будет обрабатываться на всех инстансах кластера, и результат будет агрегироваться на уровне приложения, что является очень неэффективным.
Существует ряд правил при выборе ключа/колонки, которая будет использоваться как ключ распределения:
- Избегайте инкрементных, монотонно-растущих ключей, так как при вставке новых данных шард с открытой границей будет наиболее загружен.
- Избегайте ключей, у которых очень маленький диапазон уникальных значений, так как этот диапазон будет вашим лимитом на количество возможных шардов в кластере.
- Используйте в качестве ключа поле, которое чаще всего используется в предикатах ваших запросов, для более эффективной обработки.
Shard Map
В Elastic Client Library существует концепция Shard Map. По сути, это карта соответствия ключ-шард. При помощи Shard Map вы определяете, к каким шардам относятся какие ключи, сохраняете эту информацию в Shard Map Manager, а Elastic Client Library API умеет с этой мета-информацией работать (к примеру, открывать соединение к нужному шарду по ключу в предикате запроса).
Существует два типа shard map: List Shard Map и Range Shard Map. Из названия можно определить, что List определяет соответствие точечных ключей к шардам, а Range — диапазоны ключей к шардам.
К каждой таблице применяется один из зарегистрированных Shard Map, связанные таблицы должны использовать один и тот же Shard Map.
Elastic Scale — Key Elements
Ключевые элементы Elastic Scale:
- Shard Map Manager;
- Data-Dependent Routing;
- Multi-Shard Query.
Shard Map Manager
Я уже упоминала об этой базе данных — по сути, она хранит мета-информацию по распределению данных. Эта база хранит данные по зарегистрированным шардам в кластере, зарегистрированным Shard Map, хранит схему shard key для возможностей scale-in и scale-out масштабирования. Shard Map Manager является одним из ключевых элементов системы.
Data-Dependent Routing
Механизм Data-Dependent Routing (DDR) позволяет направлять запрос на определенный шард, используя предикат запроса, в котором применяется shard key колонка.
DDR работает через Elastic Scale Client Library API. Другими словами, DDR выполняет роутинг запроса на определенный шард, используя мета-информацию распределения из Shard Map Manager.
Запросы, в предикате которых присутствует ключ распределения, являются очень эффективными. Стоит минимизировать запросы, в предикате которых нет shard key, так как они будут направлены на все шарды кластера и являются Multi-Shard Query запросами.
Multi-Shard Query
Запросы, в предикате которых отсутствует ключевая колонка, будут выполнены на всех шардах, и задача провайдера — собрать все результаты и вернуть агрегированный результат.
Количество запросов не по shard key должно быть минимальным. Если ваша система нуждается в различных вариациях запросов, где вероятность использования ключа разбиения невозможна, следует задуматься о другом подходе к хранению данных либо пересмотреть модель данных.
Data Modeling
Хотелось бы ещё раз обратить ваше внимание на моделирование схемы базы. Связанные данные должны находиться в рамках одного шарда. К связанным данным можно отнести таблицы, которые связаны по Foreign Keys.
Такой подход к моделированию схемы позволит эффективно работать с данными, выполнять JOIN-операции, транзакции и хранить данные в консистентном состоянии.
Replication
Репликация является одним из ключевых элементов High Availability. При использовании Elastic Scale для On-Premise решений удобным будет использование AlwaysOn Availability Groups и концепции AG Listeners.
При конфигурации Availability Group необходимо создавать AG Listener, который выполняет лишь функцию перенаправления запроса на Primary Replica. Приложение, работающее с базой данных, в качестве строки соединения к базе данных принимает конфигурацию Listener, что даёт возможность автоматически переключать систему на живую базу при отказе Primary Replica.
Shard Map Manager хранит всю конфигурацию shard cluster, включая имя сервера для каждого зарегистрированного инстанса/shard. При настройке репликации необходимо заменить имя сервера для каждого из шардов на AG Listener созданного для каждой из AG.
При выходе из строя одного из инстансов Listener автоматически перенаправит соединение к Secondary Replica, сконфигурированной в Availability Group.
Rebalancing, Split/Merge Service
Возможность горизонтального масштабирования — одно из основных требований любой Elastic системы. Для этих целей был разработан Split/Merge Service, который является одним из компонентов Elastic Database Tools. К сожалению, Split/Merge Service умеет работать лишь с SQL Azure базами.
Split/Merge — это cloud service, в обязанности которого входит перемещение данных из одного шарда в другой с использованием диапазона ключей.
Split/Merge service предоставляет веб-страницу, на которой вы можете указать source и target инстанс, выбрать диапазон ключей для перемещения и запустить механизм перемещения данных. Также есть возможность запустить перемещение данных при помощи PowerShell скриптов. В большинстве случаев веб-страницы вполне достаточно.
При перемещении данных необходимо указать Shard Map, по которой и будет происходить миграция данных. Данные перемещаются в связанном виде, то есть одновременно из нескольких таблиц.
При необходимости сократить количество шардов в кластере Split/Merge Service позволяет склеить/переместить данные из шарда, который мы намерены удалить, в другой или другие шарды, тем самым мы можем сократить расходы на содержание shard cluster, когда в этом нет необходимости.
К сожалению, на данный момент нет возможности настроить автоматическое балансирование данных, хотя при наличии API можно написать собственный инструмент.
Заключение
Горизонтальное масштабирование для SQL баз данных — не миф, а вполне реально работающая фича, хотя процесс внедрения scale-out не является тривиальным и требует пересмотра схемы базы, а также внедрения Elastic Database Tools.
При реализации этого подхода в нашем проекте мы полностью пересмотрели схему базы данных, переписали уровень доступа к данным, используя Elastic Database Client Library, и внедрили возможность scale-in/scale-out масштабирования для SQL Azure при помощи Split/Merge Cloud Service.