Зачем Вам Может Понадобиться Полусинхронная Репликация?

Всем привет. На связи Владислав Родин.

В настоящее время я преподаю курсы по архитектуре программного обеспечения и архитектуре программного обеспечения для высоких нагрузок в OTUS. В ожидании старта нового потока курсов «Архитектор высоких нагрузок» Я решил написать небольшой фрагмент оригинального материала, которым хочу поделиться с вами.



Зачем вам может понадобиться полусинхронная репликация?



Введение

Из-за того, что HDD может выполнять всего около 400-700 операций в секунду (что несравнимо с типичными rps на высоконагруженной системе), классическая дисковая база данных является узким местом архитектуры.

Поэтому необходимо обратить особое внимание на закономерности масштабирования этого хранилища.

В настоящее время существует два шаблона масштабирования базы данных: репликация и сегментирование.

Шардинг позволяет масштабировать операцию записи и, как следствие, снижать количество запросов в секунду на запись на сервер в кластере.

Репликация позволяет делать то же самое, но с операциями чтения.

Именно этому шаблону посвящена данная статья.



Репликация

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

Вы добавляете еще пару серверов, синхронизируете данные на всех серверах, и пользователь может читать с любого сервера вашего кластера.

Несмотря на кажущуюся простоту, существует несколько вариантов классификации различных реализаций этой схемы: По ролям в кластере (мастер-мастер или мастер-слейв) По отправленным объектам (по строкам, по операторам или смешанным) По механизму синхронизации узлов Сегодня мы разберемся с пунктом 3.

Как происходит фиксация транзакции?

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

Фиксация транзакции происходит в 3 этапа: Запись транзакции в журнал базы данных.

Использование транзакции в ядре базы данных.

Возврат подтверждения клиенту об успешном применении транзакции.

В разных базах данных этот алгоритм может иметь нюансы: например, в движке InnoDB базы данных MySQL есть 2 журнала: один для репликации (двоичный журнал), а другой для ведения ACID (журнал отмены/повтора), а в PostgreSQL существует один журнал, который выполняет обе функции (журнал упреждающей записи = WAL).

Но то, что представлено выше, является именно общей концепцией, позволяющей не учитывать подобные нюансы.



Синхронная (синхронная) репликация

Добавим логику для репликации полученных изменений в алгоритм фиксации транзакции: Запись транзакции в журнал базы данных.

Использование транзакции в ядре базы данных.

Отправка данных на все реплики.

Получение подтверждения от всех реплик о том, что на них совершена транзакция.

Возврат подтверждения клиенту об успешном применении транзакции.

При таком подходе мы получаем ряд недостатков: клиент ждет, пока изменения будут применены ко всем репликам.

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

Если с 1-м пунктом все более-менее понятно, то стоит объяснить причины 2-го пункта.

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

Таким образом, увеличивая количество узлов в кластере, вы увеличиваете вероятность того, что операция записи завершится неудачно.

Можем ли мы дождаться подтверждения только от определенного процента узлов, например, от 51% (кворум)? Да, можем, но в классическом варианте требуется подтверждение со всех узлов, ведь так мы можем обеспечить полную согласованность данных в кластере, что является несомненным преимуществом такого типа репликации.



Асинхронная (асинхронная) репликация

Модифицируем предыдущий алгоритм.

Мы отправим данные на реплики «когда-нибудь позже», и «когда-нибудь позже» изменения будут применены к репликам: Запись транзакции в журнал базы данных.

Использование транзакции в ядре базы данных.

Возврат подтверждения клиенту об успешном применении транзакции.

Отправка данных в реплики и применение к ним изменений.

Такой подход приводит к тому, что кластер работает быстро, поскольку мы не заставляем клиента ждать, пока данные дойдут до реплик и даже зафиксируются.

Но условие сброса данных на реплики «когда-нибудь позже» может привести как к потере транзакции, так и к потере транзакции, подтвержденной пользователем, поскольку если данные не успели реплицироваться, подтверждение клиенту об успехе операции было отправлено, а на узле, на который поступили изменения, произошел сбой HDD, мы теряем транзакцию, что может привести к весьма неприятным последствиям.



Полусинхронная репликация

Наконец мы дошли до полусинхронной репликации.

Этот тип репликации не очень хорошо известен и не очень распространен, но он представляет значительный интерес, поскольку может сочетать в себе преимущества как синхронной, так и асинхронной репликации.

Попробуем объединить 2 предыдущих подхода.

Клиента мы держать долго не будем, но потребуем, чтобы данные были реплицированы: Запись транзакции в журнал базы данных.

Использование транзакции в ядре базы данных.

Отправка данных на реплики.

Получение подтверждения от реплики о том, что изменения получены (они будут применены «когда-нибудь позже»).

Возврат подтверждения клиенту об успешном применении транзакции.

Обратите внимание, что при использовании этого алгоритма потеря транзакции происходит только в случае сбоя как узла, получающего изменения, так и узла реплики.

Вероятность такого сбоя считается низкой, и эти риски принимаются.

Но при таком подходе возможен риск фантомного чтения.

Представим себе следующую ситуацию: на шаге 4 мы не получили подтверждения ни от одной реплики.

Мы должны откатить эту транзакцию и не возвращать клиенту подтверждение.

Поскольку данные были применены на шаге 2, между окончанием шага 2 и откатом транзакции существует временной промежуток, в течение которого параллельные транзакции могут увидеть изменения, которых не должно быть в базе данных.



Полусинхронная репликация без потерь

Если немного подумать, то можно просто повернуть шаги алгоритма вспять и решить проблему фантомного чтения в этом сценарии: Запись транзакции в журнал базы данных.

Отправка данных реплики.

Получение подтверждения от реплики о том, что изменения получены (они будут применены «когда-нибудь позже»).

Использование транзакции в ядре базы данных.

Возврат подтверждения клиенту об успешном применении транзакции.

Теперь мы фиксируем изменения только в том случае, если они были реплицированы.



Заключение

Как всегда, идеальных решений не существует; существует набор решений, каждое из которых имеет свои преимущества и недостатки и подходит для решения разных классов задач.

Это абсолютно справедливо для выбора механизма синхронизации данных в реплицируемой базе данных.

Набор преимуществ, которыми обладает полусинхронная репликация, достаточно солиден и интересен, что может считаться достойным внимания, несмотря на малую распространенность.

Вот и все.

Увидимся в курс ! Теги: #Разработка веб-сайтов #Высокая производительность #Распределенные системы #Промышленное программирование #кластер #высокая нагрузка #высоконагруженные системы #масштабирование #высокая нагрузка #веб-разработка #репликация базы данных #репликация #база данных #db #высокая нагрузка

Вместе с данным постом часто просматривают: