Объяснения по шардинга баз данных
С каждым днем увеличивается количество функций, активных пользователей и данных. Ваша база данных начинает тормозить работу приложения. Многие люди не понимают, что такое шардинг баз данных, который может решить их проблемы. В этой статье рассказывается о шардинге баз данных, его преимуществах, а также о том, как его использовать и когда этого делать не следует.
Ваше приложение становится все лучше. У него появляется все больше функций, все больше активных пользователей, и каждый день оно собирает все больше данных. Теперь ваша база данных приводит к замедлению работы остальной части приложения. Разделение баз данных может стать решением ваших проблем, но многие не знают, что это такое и, главное, когда его следует использовать. В этой статье мы поговорим о том, что такое шардинг баз данных, как он работает и как его лучше использовать.
Прежде чем перейти к рассмотрению этого вопроса, необходимо понять, почему мы разделяем хранилища данных и какие существуют различные варианты, прежде чем приступать к их использованию.
Когда таблицы достигают определенного размера, люди часто считают, что шардинг – это волшебное решение всех проблем масштабирования. Однако у меня были таблицы с миллиардами строк, и я не видел убедительных причин для шардинга, поскольку мой паттерн использования хорошо подходит для одной таблицы, и я не видел никаких веских причин (кроме управления такой большой таблицей, что в некоторых случаях является достаточной причиной) для шардинга таблицы.
Что такое разбиение базы данных на части?
Шардинг – это метод распределения данных между несколькими машинами. Шардинг становится особенно удобным, когда ни одна машина не может справиться с ожидаемой рабочей нагрузкой.
Шардинг – это пример горизонтального масштабирования, в то время как вертикальное масштабирование – это просто приобретение все более крупных машин для поддержки большей нагрузки.
Инженеры часто зацикливаются на том, чтобы сделать все процессы наиболее сложным способом, но если все упростить на начальном этапе, то впоследствии, по мере развития приложения, работать с ним гораздо проще.
Теперь, когда мы обсудили возможные серверные архитектуры, давайте поговорим о компоновке данных.
Вы можете разделить данные на несколько частей и переместить определенные таблицы в свои базы данных, что во многом напоминает архитектуру микросервисов, где определенный аспект приложения имеет свой сервер базы данных. Приложение знает, где искать каждую из них. В качестве альтернативы можно хранить строки одной и той же таблицы на нескольких узлах базы данных, что позволяет реализовать такие идеи, как шардирование ключей; подробнее об этом мы поговорим позже.
Более современные базы данных, такие как Cassandra и другие, абстрагируют этот вопрос от логики приложения и поддерживают его на уровне базы данных.
Каковы возможности шардингома?
Как и любая распределенная архитектура, разделение баз данных требует затрат. Настройка шардов, поддержание данных на каждом из них в актуальном состоянии и обеспечение отправки запросов на нужные шарды требует много времени и сил. Прежде чем приступать к созданию шардов, стоит посмотреть, не подойдет ли вам один из других вариантов.
Вариант 1: Ничего не делать.
Меня неоднократно спрашивали, является ли шардинг хорошей идеей, если нет явного узкого места или ограничивающего фактора работы бд. Если ничего не сломано, не надо чинить.
Вариант 2: Вертикальное масштабирование
Мы уже упоминали об этом, просто приобретите машины с большими ресурсами, добавьте дополнительную оперативную память, добавьте больше ядер процессора для тяжелых вычислительных нагрузок и добавьте дополнительную память. Все это варианты, не требующие перестройки архитектуры приложений и баз данных. Другие возможные ограничения, такие как пропускная способность (сетевая или внутренняя), также могут заставить вас перейти к шардингу.
Вариант 3: Репликация
Если большая часть работы с данными сводится к их чтению, репликация может сделать их более доступными и ускорить процесс чтения. Это поможет избежать некоторых сложностей, связанных с разделением баз данных. Производительность чтения может быть повышена за счет создания большего числа копий базы данных. Конечно, при условии, что вы уже дополнили ее кэшем. Это можно сделать за счет балансировки нагрузки или маршрутизации запросов в зависимости от их местоположения в мире. Однако репликация усложняет работу с большими объемами записи, поскольку каждая запись должна быть скопирована на каждый узел. Это может зависеть от хранилища данных, некоторые из них делают это асинхронно, в то время как другие могут задерживать первоначальную запись, чтобы убедиться, что она была скопирована.
Журнал WAL (write ahead log) – это дополнительная структура на диске, которая может быть только добавлена. Перед записью изменений в базу данных они сначала записываются в журнал, причем этот журнал должен находиться на долговременном носителе. Он используется для восстановления после сбоев и потерянных транзакций. Этот журнал также используется для поддержки репликации в некоторых базах данных, таких как PostgreSQL и MySQL.
Варианты 4: Специализированные базы данных
Низкая производительность обусловлена тем, что база данных должна быть лучше спроектирована для той рабочей нагрузки, которую она обслуживает. Например, хранение поисковых данных в реляционном хранилище данных не имеет смысла. Более эффективным будет перемещение таких данных в Elasticsearch. Перемещение блобов в объектное хранилище, например S3, может оказаться более выгодным, чем хранение их в реляционном хранилище. Возможно, имеет смысл передать эту функцию на аутсорсинг, а не пытаться разделить всю базу данных.
Разделяемая база данных может оказаться наилучшим решением, если ваша прикладная база данных управляет большим объемом данных, нуждается в большом количестве операций чтения и записи и/или должна быть доступна в любое время. Давайте рассмотрим плюсы и минусы шардирования.
Sharding If You Must
Шардинг может обеспечить практически неограниченную масштабируемость с точки зрения увеличения пропускной способности системы, емкости хранилища и доступности. При этом появляется множество небольших, более простых в управлении систем, каждая из которых может самостоятельно увеличиваться и уменьшаться с помощью соответствующих реплик.
Все эти преимущества компенсируются сложностью эксплуатации, накладными расходами на приложения и стоимостью инфраструктуры для поддержки этой новой конструкции.
Как это работает?
Перед тем как разделить базу данных, необходимо ответить на несколько важных вопросов. От того, как вы ответите на эти вопросы, будет зависеть ваш план.
- Как распределить данные между шардами? Есть ли потенциальные “горячие точки”, если данные распределены неравномерно?
- Какие запросы мы будем выполнять и как будут взаимодействовать таблицы?
- Как будут расти данные? Как их нужно будет перераспределять в дальнейшем?
Hotspot 6860189
Термин hotspot означает, что загрузка узла превысила пороговое значение по определенному ресурсу (память, io и т.д.). Существует забавный случай, который называется Bieber Bug.
Прежде чем перейти к следующему разделу, важно понять следующую терминологию.
Shard Key – это часть первичного ключа, которая определяет, как должны быть распределены данные. С помощью ключа-обломка можно быстро находить и изменять данные, направляя операции в нужную базу данных.
На одном узле хранятся записи, имеющие одинаковый ключ shard. Группа данных, имеющих один и тот же ключ, называется логическим шардом. В один узел базы данных, называемый также физическим, включается несколько логических разделов.
Самое важное предположение – это то, которое сложнее всего изменить в будущем. Логический шард может занимать только один узел, поскольку он является атомарной единицей хранения. В случае, когда размер шарда слишком велик для одного узла, в кластере баз данных фактически не остается места.
Разделение на основе ключей
Алгоритмически разделенные базы данных используют хэш-функцию для поиска данных. Это позволяет, задав определенный ключ шарда, найти нужный физический шард для запроса данных.
Данные распределяются только по хэш-функции. Она не учитывает размер полезной нагрузки и занимаемое пространство. Преимущества хэширования заключаются в более равномерном распределении данных в случае отсутствия подходящего ключа раздела, а при наличии соответствующего ключа раздела местоположение может быть рассчитано “на лету”.
Если вам интересно узнать о детерминированных функциях хеширования, посмотрите мою статью о Redis.
Недостатком такой стратегии чередования является то, что повторное чередование данных может быть затруднено, а поддержание согласованности при доступности – еще сложнее.
Разделение на основе диапазонов
При чередовании на основе диапазонов данные делятся на куски в зависимости от диапазонов определенного значения.
Для этого необходимо создать таблицу поиска, в которой будут храниться данные. Поддержание согласованности такой таблицы и, очевидно, выбор диапазонов здесь очень важны.
При выборе ключа шарда для данного типа шардинга необходимо выбрать ключ с высокой кардинальностью, чтобы количество возможных значений для этого ключа было большим. Например, ключ с возможными значениями North, South, East и West имеет низкую кардинальность, поскольку их всего 4. В сочетании с этим в идеале желательно иметь хорошее распределение в пределах этой кардинальности.
Если все попадет в 50% возможных значений, то на некоторых осколках начнут появляться “горячие точки”. Провести такой эксперимент с точными данными достаточно просто с помощью нескольких строк кода. Сначала выберите ключ и диапазоны и изучите потенциальное распределение.
Разделение на основе отношений
Этот механизм совместного использования позволяет хранить связанные данные на одном физическом осколке. Например, в реляционной базе данных связанные данные часто распределены по нескольким таблицам.
Например, в таком приложении, как Instagram, пользователь и все связанные с ним данные будут размещены на одном физическом узле, что включает в себя сообщения и комментарии, соответственно. Можно получить больше пользы от одного раздела, поместив связанные сущности в один и тот же раздел. В результате обеспечивается более высокая согласованность данных в физическом шарде и уменьшается количество межфизических запросов к шарду.
Межсерверные транзакции
В заключение я хочу рассказать о сложностях, которые могут возникнуть при выполнении транзакций, охватывающих несколько хранилищ. Как бы вы ни планировали, сервис или приложение, живущее достаточно долго, рано или поздно столкнется с несколькими транзакциями между шардами.
По сути, это означает, что вам нужны гарантии транзакций, предоставляемые ACID-совместимой базой данных, но на разных шардах, где база данных не обеспечивает такого соответствия, поскольку данные, с которыми вы оперируете, находятся вне области действия транзакции, которая ее начала.
Такую транзакцию принято называть глобальной, когда несколько подтранзакций должны координироваться и успешно выполняться. Как правило, чем дольше открыта транзакция, тем больше противоречий и потенциально возможен сбой.
Двухфазная фиксация
Двухфазная фиксация проста в теории, но сложна на практике.
- Лидер записывает долговременную запись транзакции, указывающую на межосколочную транзакцию.
- Участники записывают постоянную запись о своем желании совершить транзакцию и уведомляют лидера.
- Лидер фиксирует транзакцию, обновляя постоянную запись транзакции после получения всех ответов. (Он может прервать транзакцию, если никто не ответил).
- Участники могут показать новое состояние после того, как лидер объявит решение о фиксации. (Если лидер прерывает транзакцию, то они удаляют это состояние).
Усиление чтения и записи в протокольном тракте является серьезной проблемой. Усиление записи происходит потому, что необходимо записать запись транзакции и обеспечить длительную фиксацию, что требует как минимум одной записи на каждого участника. В результате чрезмерной записи могут возникнуть проблемы с блокировками и нестабильность приложения. База данных должна дополнительно фильтровать каждое чтение, чтобы не видеть состояния, зависящего от ожидающей транзакции между шардами, что влияет на все чтения в системе, даже нетранзакционные.
Заключение
Мы обсудили, когда следует использовать шардинг и как его настраивать. Шардинг – отличное решение для приложений, которым необходимо управлять большим объемом данных и иметь их в свободном доступе для чтения и записи в больших объемах. Тем не менее, это усложняет работу. Прежде чем приступать к внедрению, следует подумать о том, стоят ли преимущества затрат или есть более простое решение.