
Распределённые транзакции и консенсус. 2PC, 3PC
Что вообще такое распределённые транзакции.
Представь, что у тебя не одна база данных, а несколько — на разных серверах, или даже в разных странах.
Ты хочешь выполнить одну логическую операцию, которая должна быть атомарной:
либо всё выполнилось на всех, либо ничего.
Например:
- У тебя есть банк с двумя филиалами: Москва и Берлин.
- Клиент переводит €100 со счёта в Москве на счёт в Берлине.
Мы хотим, чтобы не получилось ситуации:
- деньги списались в Москве,
- но не зачислились в Берлине.
Как это решают — 2-фазный коммит (2PC)
Это протокол, который помогает нескольким узлам договориться о том, применить ли транзакцию.
Фаза 1 — “Готовность” (prepare)
- Центральный координатор говорит всем участникам:
“Готовы выполнить эту операцию?” - Каждый узел проверяет у себя (например, хватает ли баланса, нет ли ошибок)
и отвечает “Да” или “Нет”.
Фаза 2 — “Подтверждение” (commit)
- Если все сказали “Да”, координатор говорит: “Коммитим!”.
- Если хотя бы один сказал “Нет”, координатор говорит: “Откатываемся!”.
Пример:
Москва (узел A): снять €100
Берлин (узел B): зачислить €100
Координатор:
спрашивает у обоих “готовы?”
получает ответы:
- A: “Да”
- B: “Да”
отправляет команду: “Коммитим!”
Всё ок — транзакция выполнена.
Но проблема — если координатор завис?
Если координатор упал между шагами 1 и 2, узлы остаются “в подвешенном состоянии”:
они уже “готовы”, но не знают, надо ли коммитить или откатывать.
Это может привести к зависшим транзакциям, которые ждут решения бесконечно.
Трёхфазный коммит (3PC)
Чтобы избежать зависаний, придумали трёхфазный коммит (Three-Phase Commit).
Он добавляет дополнительную фазу, чтобы участники могли действовать,
даже если координатор потерялся.
Фаза 1 — “Можно ли?” (CanCommit)
Координатор спрашивает:
“Можете ли вы выполнить операцию?”
Участники проверяют и отвечают “Да” или “Нет”.
Фаза 2 — “Готовьтесь” (PreCommit)
Если все ответили “Да”, координатор говорит:
“Готовьтесь к коммиту, но пока не применяйте!”
Участники сохраняют изменения в журнале, но не делают их окончательными.
Теперь, если координатор упадёт, участники знают, что коммит почти точно будет
и могут договориться между собой — завершить или откатить.
Фаза 3 — “Коммитим!” (DoCommit)
Когда координатор уверен, он рассылает команду:
“Коммитим окончательно!”
Пример:
- Координатор: “Можете перевести 100 евро?”
- Оба узла отвечают “Да”.
- Координатор: “Окей, готовьтесь — почти коммитим.”
- Узлы записывают данные как “подготовленные”.
- Координатор: “Коммитим!”
- Узлы применяют изменения.
Если координатор пропал после шага 3,
узлы могут согласовать между собой, что раз все были готовы — можно завершить.
Это делает систему более живучей, чем 2PC.
Но если сеть сильно тормозит или узлы не успели обменяться — конфликт всё ещё возможен.
Что такое консенсус (Consensus)
Консенсус — это более надёжный способ, при котором все узлы
договариваются о решении вместе, даже если часть системы падает.
Здесь уже нет одного “всемогущего координатора”,
а решения принимаются голосованием и фиксацией большинства.
Пример:
Три сервера решают: “Коммитим или нет?”
- A говорит: “Коммитим”.
- B говорит: “Коммитим”.
- C недоступен.
Двое из трёх согласны → Коммитим.
Даже если один узел отвалился, большинство решает исход.
Как это реализуют — Paxos / Raft
Протоколы консенсуса позволяют узлам выбрать лидера,
который собирает голоса и ведёт порядок операций.
- Сервер A становится лидером.
- Клиент отправляет ему запрос: “x = 1”.
- A рассылает обновление B и C.
- Когда большинство подтвердили (2 из 3) — команда зафиксирована.
- Даже если один сервер упадёт, данные сохраняются.
Если лидер упал — выбирается новый, и система продолжает работу.
Почему консенсус лучше коммитов
- Нет “единой точки отказа” — система переживает падение узлов.
- Большинство узлов всегда могут принять решение.
- Нет зависаний, как в 2PC.
- Сложнее реализовать, но надёжнее при сбоях сети.