Двухфазный коммит
Я полагаю, что большинство людей знают, что такое 2PC (протокол двухфазной фиксации) и как его использовать в Java или в большинстве современных языков. По сути, он используется для обеспечения синхронизации транзакций, когда у вас есть 2 или более БД.
Предположим, у меня есть две БД (A и B), использующие 2PC в двух разных местах. Прежде чем A и B будут готовы к совершению транзакции, обе базы данных сообщат менеджеру транзакций, что они готовы к фиксации. Таким образом, когда диспетчер транзакций будет подтвержден, он отправит сигнал A и B, сообщая им, что нужно идти дальше.
Вот мой вопрос: допустим, А получил сигнал и совершил транзакцию. Когда все будет завершено, B собирается сделать то же самое, но кто-то отключает кабель питания, вызывая отключение всего сервера. Когда B вернется в онлайн, что будет делать B? И как Б это делает?
Помните, A зафиксирован, но B нет, и мы используем 2PC (так что дизайн 2PC перестает работать, не так ли?)
3 ответа
На двухфазной фиксации
Двухфазная фиксация не гарантирует, что распределенная транзакция не может завершиться неудачей, но она гарантирует, что она не может завершиться неудачно без уведомления TM.
Чтобы B мог сообщить о транзакции как о готовности к фиксации, B должен иметь транзакцию в постоянном хранилище (т. Е. B должен быть в состоянии гарантировать, что транзакция может быть зафиксирована при любых обстоятельствах). В этой ситуации B сохранил транзакцию, но менеджер транзакций еще не получил сообщение от B, подтверждающее, что B завершил фиксацию.
Менеджер транзакций снова запросит B, когда B вернется в оперативный режим, и попросит его совершить транзакцию. Если B уже совершил транзакцию, он сообщит о транзакции как о подтвержденной. Если B еще не зафиксировал транзакцию, он выполнит фиксацию, поскольку он уже сохранил ее и, таким образом, все еще в состоянии зафиксировать транзакцию.
Для того, чтобы B потерпел неудачу в этой ситуации, он должен был бы претерпеть катастрофический сбой, который потерял данные или записи журнала. Менеджер транзакций все равно будет знать, что B не сообщил об успешной фиксации.1
На практике, если B больше не сможет зафиксировать транзакцию, это будет означать, что авария, вызвавшая B, привела к потере данных, и B сообщит об ошибке, когда TM попросит его передать TxID, о котором он не знает или не думал, был в состоянии ответственности.
Таким образом, двухфазная фиксация не предотвращает катастрофического сбоя, но предотвращает его незамеченное. В этом случае диспетчер транзакций сообщит об ошибке приложению, если B не сможет зафиксировать.
Приложение по-прежнему должно иметь возможность восстановления после ошибки, но транзакция не может завершиться сбоем без уведомления приложения о несогласованном состоянии.
Семантика
Если диспетчер ресурсов или сеть отключаются на этапе 1, диспетчер транзакций обнаружит фатальную ошибку (не может подключиться к диспетчеру ресурсов) и пометит суб-транзакцию как неудачную. Когда сеть возвращается, она прерывает транзакцию на всех участвующих менеджерах ресурсов.
Если диспетчер ресурсов или сеть выйдут из строя на этапе 2, диспетчер транзакций будет продолжать опрашивать диспетчер ресурсов, пока не восстановится. Когда он снова подключается к диспетчеру ресурсов, он сообщает RM о совершении транзакции. Если RM возвращает ошибку в соответствии с "Неизвестным TxID", TM будет знать, что в RM существует проблема потери данных.
Если ТМ выходит из строя в фазе 1, тогда клиент будет блокироваться до тех пор, пока ТМ не вернется, пока он не отключится или не получит ошибку из-за разрыва сетевого соединения. В этом случае клиент узнает об ошибке и может либо повторить попытку, либо инициировать прерывание самостоятельно.
Если в фазе 2 TM отключается, он блокирует клиента до тех пор, пока TM не вернется. Он уже сообщил о транзакции как зафиксированной, и клиенту не следует сообщать о фатальной ошибке, хотя он может блокироваться до тех пор, пока ТМ не вернется. ТМ все еще будет иметь транзакцию в незафиксированном состоянии и будет запрашивать РМ для подтверждения, когда она возвращается.
События потери данных после фиксации в менеджерах ресурсов не обрабатываются менеджером транзакций и являются функцией устойчивости RM.
Двухфазная фиксация не гарантирует отказоустойчивость - см. Paxos для примера протокола, который обращается к отказоустойчивости - но это гарантирует, что частичный сбой распределенной транзакции не может остаться незамеченным.
- Обратите внимание, что такого рода сбой также может привести к потере данных из ранее совершенных транзакций. Двухфазная фиксация не гарантирует, что менеджеры ресурсов не могут потерять или повредить данные или что процедуры DR не испортятся.
Я считаю, что трехфазный коммит - гораздо лучший подход. К сожалению, я не нашел никого, кто бы внедрил такую технологию.
http://the-paper-trail.org/blog/consensus-protocols-three-phase-commit/
Вот основные части этой статьи:
Принципиальная трудность с 2PC заключается в том, что, как только координатор принял решение о коммите и сообщил его некоторым репликам, реплики продолжают работу и действуют в соответствии с оператором фиксации, не проверяя, получила ли каждая другая реплика сообщение. Затем, если реплика, которая совершила сбой, падает вместе с координатором, система не может сказать, каков был результат транзакции (так как только координатор и реплика, получившая сообщение, точно знают). Поскольку транзакция, возможно, уже была зафиксирована в поврежденной реплике, протокол не может прервать пессимистически - поскольку транзакция могла иметь побочные эффекты, которые невозможно отменить. Точно так же протокол не может оптимистично заставить транзакцию совершить транзакцию, поскольку исходное голосование могло прервать выполнение.
Эта проблема - в основном - обойдена путем добавления дополнительной фазы к 2PC, что неудивительно, что дает нам протокол трехфазной фиксации. Идея очень проста. Мы разбиваем второй этап 2PC - 'commit' - на два подэтапа. Первый этап - "подготовиться к принятию". Координатор отправляет это сообщение всем репликам, когда он получил единодушные голоса "да" на первом этапе. После получения этих сообщений реплики попадают в состояние, в котором они могут совершить транзакцию - с помощью необходимых блокировок и т. Д. - но, что принципиально, не выполняют никакой работы, которую они не могут впоследствии отменить. Затем они отвечают координатору, сообщая, что сообщение "подготовиться к принятию" было получено.
Цель этого этапа - сообщить результат голосования каждой реплике, чтобы можно было восстановить состояние протокола независимо от того, какая реплика умирает.
Последняя фаза протокола выполняет почти то же самое, что и первоначальная фаза 'commit or abort' в 2PC. Если координатор получает подтверждение доставки сообщения "подготовиться к принятию" из всех реплик, тогда можно безопасно продолжать транзакцию. Однако, если доставка не подтверждена, координатор не может гарантировать, что состояние протокола будет восстановлено в случае его сбоя (если вы допускаете фиксированное число сбоев f, координатор может продолжить работу после получения f+1. подтверждения). В этом случае координатор прервет транзакцию.
Если в какой-то момент координатор потерпит крах, узел восстановления может принять транзакцию и запросить состояние из любых оставшихся реплик. Если реплика, которая зафиксировала транзакцию, потерпела крах, мы знаем, что каждая другая реплика получила сообщение "подготовка к фиксации" (в противном случае координатор не перешел бы на фазу фиксации), и поэтому узел восстановления будет удалось определить, что транзакция была в состоянии совершить, и безопасно составить протокол до ее заключения. Если какая-либо реплика сообщает узлу восстановления, что он не получил "подготовиться к принятию", узел восстановления будет знать, что транзакция не была зафиксирована ни в одной реплике, и поэтому сможет либо пессимистически прервать, либо повторно запустить протокол. с начала.
Так 3PC решает все наши проблемы? Не совсем, но это близко. В случае сетевого раздела колеса скорее отрываются - представьте, что все реплики, которые получили "готовятся к фиксации", находятся на одной стороне раздела, а те, которые не были, - на другой. Затем оба раздела продолжат работу с узлами восстановления, которые соответственно фиксируют или прерывают транзакцию, и при слиянии сети система будет иметь несовместимое состояние. Так что 3PC имеет потенциально небезопасные прогоны, как и 2PC, но всегда будет прогрессировать и, следовательно, удовлетворяет своим свойствам живучести. Тот факт, что 3PC не будет блокировать отказы одного узла, делает его гораздо более привлекательным для услуг, где высокая доступность важнее, чем низкие задержки.
Ваш сценарий не единственный, когда все может пойти не так, несмотря на все усилия. Предположим, что и A, и B сообщили об "готовности к принятию" к TM, а затем кто-то отключает линию между TM и, скажем, B. B ожидает одобрения (или запрета) от TM, но он определенно выиграл не ждите вечно, пока ТМ не восстановит соединение (его собственные ресурсы, задействованные в транзакции, должны оставаться заблокированными / недоступными в течение всего времени ожидания по очевидным причинам). Поэтому, когда B слишком долго ждет своего вкуса, он будет принимать то, что называется "эвристическими решениями". То есть, он решит фиксировать или откатить независимо от TM, основываясь на том, ну, я не совсем знаю, что, но это не имеет значения. Должно быть очевидно, что любые такие эвристические решения могут отличаться от фактического решения о коммите, принятого TM.