Всем привет. На связи Владислав Родин.
В настоящее время я преподаю курсы по архитектуре программного обеспечения и архитектуре программного обеспечения для высоких нагрузок в OTUS. В ожидании старта нового потока курсов «Архитектор высоких нагрузок» Я решил написать небольшой фрагмент оригинального материала, которым хочу поделиться с вами.
Введение
Из-за того, что HDD может выполнять всего около 400-700 операций в секунду (что несравнимо с типичными rps на высоконагруженной системе), классическая дисковая база данных является узким местом архитектуры.Поэтому необходимо обратить особое внимание на закономерности масштабирования этого хранилища.
В настоящее время существует два шаблона масштабирования базы данных: репликация и сегментирование.
Шардинг позволяет масштабировать операцию записи и, как следствие, снижать количество запросов в секунду на запись на сервер в кластере.
Репликация позволяет делать то же самое, но с операциями чтения.
Именно этому шаблону посвящена данная статья.
Репликация
Если посмотреть на репликацию на очень высоком уровне, то все просто: у вас был один сервер, на нем были данные, а потом этот сервер уже не справлялся с нагрузкой по чтению этих данных.Вы добавляете еще пару серверов, синхронизируете данные на всех серверах, и пользователь может читать с любого сервера вашего кластера.
Несмотря на кажущуюся простоту, существует несколько вариантов классификации различных реализаций этой схемы: По ролям в кластере (мастер-мастер или мастер-слейв)
По отправленным объектам (по строкам, по операторам или смешанным)
По механизму синхронизации узлов
Сегодня мы разберемся с пунктом 3.
Как происходит фиксация транзакции?
Эта тема не имеет прямого отношения к репликации; Об этом можно написать отдельную статью, но поскольку дальнейшее чтение бесполезно без понимания механизма фиксации транзакций, напомню самые основные вещи.Фиксация транзакции происходит в 3 этапа: Запись транзакции в журнал базы данных.
Использование транзакции в ядре базы данных.
Возврат подтверждения клиенту об успешном применении транзакции.
В разных базах данных этот алгоритм может иметь нюансы: например, в движке InnoDB базы данных MySQL есть 2 журнала: один для репликации (двоичный журнал), а другой для ведения ACID (журнал отмены/повтора), а в PostgreSQL существует один журнал, который выполняет обе функции (журнал упреждающей записи = WAL).
Но то, что представлено выше, является именно общей концепцией, позволяющей не учитывать подобные нюансы.
Синхронная (синхронная) репликация
Добавим логику для репликации полученных изменений в алгоритм фиксации транзакции: Запись транзакции в журнал базы данных.Использование транзакции в ядре базы данных.
Отправка данных на все реплики.
Получение подтверждения от всех реплик о том, что на них совершена транзакция.
Возврат подтверждения клиенту об успешном применении транзакции.
При таком подходе мы получаем ряд недостатков: клиент ждет, пока изменения будут применены ко всем репликам.
по мере увеличения количества узлов в кластере мы уменьшаем вероятность того, что операция записи будет успешной.
Если с 1-м пунктом все более-менее понятно, то стоит объяснить причины 2-го пункта.
Если при синхронной репликации мы не получаем ответа хотя бы от одного узла, мы откатываем транзакцию.
Таким образом, увеличивая количество узлов в кластере, вы увеличиваете вероятность того, что операция записи завершится неудачно.
Можем ли мы дождаться подтверждения только от определенного процента узлов, например, от 51% (кворум)? Да, можем, но в классическом варианте требуется подтверждение со всех узлов, ведь так мы можем обеспечить полную согласованность данных в кластере, что является несомненным преимуществом такого типа репликации.
Асинхронная (асинхронная) репликация
Модифицируем предыдущий алгоритм.Мы отправим данные на реплики «когда-нибудь позже», и «когда-нибудь позже» изменения будут применены к репликам: Запись транзакции в журнал базы данных.
Использование транзакции в ядре базы данных.
Возврат подтверждения клиенту об успешном применении транзакции.
Отправка данных в реплики и применение к ним изменений.
Такой подход приводит к тому, что кластер работает быстро, поскольку мы не заставляем клиента ждать, пока данные дойдут до реплик и даже зафиксируются.
Но условие сброса данных на реплики «когда-нибудь позже» может привести как к потере транзакции, так и к потере транзакции, подтвержденной пользователем, поскольку если данные не успели реплицироваться, подтверждение клиенту об успехе операции было отправлено, а на узле, на который поступили изменения, произошел сбой HDD, мы теряем транзакцию, что может привести к весьма неприятным последствиям.
Полусинхронная репликация
Наконец мы дошли до полусинхронной репликации.Этот тип репликации не очень хорошо известен и не очень распространен, но он представляет значительный интерес, поскольку может сочетать в себе преимущества как синхронной, так и асинхронной репликации.
Попробуем объединить 2 предыдущих подхода.
Клиента мы держать долго не будем, но потребуем, чтобы данные были реплицированы: Запись транзакции в журнал базы данных.
Использование транзакции в ядре базы данных.
Отправка данных на реплики.
Получение подтверждения от реплики о том, что изменения получены (они будут применены «когда-нибудь позже»).
Возврат подтверждения клиенту об успешном применении транзакции.
Обратите внимание, что при использовании этого алгоритма потеря транзакции происходит только в случае сбоя как узла, получающего изменения, так и узла реплики.
Вероятность такого сбоя считается низкой, и эти риски принимаются.
Но при таком подходе возможен риск фантомного чтения.
Представим себе следующую ситуацию: на шаге 4 мы не получили подтверждения ни от одной реплики.
Мы должны откатить эту транзакцию и не возвращать клиенту подтверждение.
Поскольку данные были применены на шаге 2, между окончанием шага 2 и откатом транзакции существует временной промежуток, в течение которого параллельные транзакции могут увидеть изменения, которых не должно быть в базе данных.
Полусинхронная репликация без потерь
Если немного подумать, то можно просто повернуть шаги алгоритма вспять и решить проблему фантомного чтения в этом сценарии: Запись транзакции в журнал базы данных.Отправка данных реплики.
Получение подтверждения от реплики о том, что изменения получены (они будут применены «когда-нибудь позже»).
Использование транзакции в ядре базы данных.
Возврат подтверждения клиенту об успешном применении транзакции.
Теперь мы фиксируем изменения только в том случае, если они были реплицированы.
Заключение
Как всегда, идеальных решений не существует; существует набор решений, каждое из которых имеет свои преимущества и недостатки и подходит для решения разных классов задач.Это абсолютно справедливо для выбора механизма синхронизации данных в реплицируемой базе данных.
Набор преимуществ, которыми обладает полусинхронная репликация, достаточно солиден и интересен, что может считаться достойным внимания, несмотря на малую распространенность.
Вот и все.
Увидимся в курс ! Теги: #Разработка веб-сайтов #Высокая производительность #Распределенные системы #Промышленное программирование #кластер #высокая нагрузка #высоконагруженные системы #масштабирование #высокая нагрузка #веб-разработка #репликация базы данных #репликация #база данных #db #высокая нагрузка
-
Встреча Сообщества Ускорения 28/01
19 Oct, 24 -
Сервисы Google Для Домохозяек
19 Oct, 24