← Назад к вопросам

Что такое двухфазный коммит?

1.0 Junior🔥 131 комментариев
#Другое

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое двухфазный коммит?

Two-Phase Commit (2PC) — это протокол для управления распределёнными транзакциями в системах с несколькими базами данных или сервисами. Его цель — гарантировать, что либо все участники успешно выполнят операцию, либо все откатят её. Это обеспечивает ACID свойства в распределённой среде.

Фазы двухфазного коммита

Фаза 1: Prepare (Голосование)

  1. Coordinator (координатор) отправляет всем участникам (participants) запрос подготовки
  2. Каждый участник:
    • Выполняет операцию
    • Если всё успешно — блокирует ресурсы и отвечает "READY"
    • Если ошибка — откатывает операцию и отвечает "ABORT"
  3. Участники ждут финального решения координатора

Фаза 2: Commit (Принятие решения)

  1. Если ВСЕ участники ответили "READY":
    • Coordinator отправляет COMMIT
    • Все участники фиксируют изменения
  2. Если хотя бы один ответил "ABORT":
    • Coordinator отправляет ROLLBACK
    • Все участники откатывают операции

Диаграмма 2PC

Координатор                  Участник 1        Участник 2
    |                            |                  |
    |------- PREPARE --------->  |                  |
    |                    (execute & lock)           |
    |                            |                  |
    |<------- READY -------------|                  |
    |                                               |
    |------- PREPARE --------->                     |
    |                            |         (execute & lock)
    |                            |                  |
    |<------- READY -------------|<--- READY ------|
    |                                               |
    |            ГОТОВЫ ВСЕ, КОММИТИМ             |
    |                                               |
    |------- COMMIT -----------> |                  |
    |                    (apply changes)           |
    |                            |                  |
    |<------ ACK COMMIT ---------|                  |
    |                                               |
    |------- COMMIT ----------->                    |
    |                            |         (apply changes)
    |                            |                  |
    |<------ ACK COMMIT ---------|<- ACK COMMIT ---|

Практический пример: Переводы между банками

class TwoPhaseCommit:
    def __init__(self):
        self.participants = []
        self.status = {}
    
    def register_participant(self, db_connection):
        """Регистрируем участника транзакции"""
        self.participants.append(db_connection)
    
    def phase_1_prepare(self, operation):
        """Фаза 1: Подготовка"""
        print("Фаза 1: PREPARE")
        
        for participant in self.participants:
            try:
                # Выполняем операцию и блокируем ресурсы
                participant.execute(operation)
                self.status[participant] = 'READY'
                print(f"  {participant}: READY")
            except Exception as e:
                self.status[participant] = 'ABORT'
                print(f"  {participant}: ABORT ({e})")
                # Откатываем все если хотя бы один ошибку
                return False
        
        return True
    
    def phase_2_commit(self):
        """Фаза 2: Коммит (если все готовы)"""
        print("\nФаза 2: COMMIT")
        
        for participant, status in self.status.items():
            if status == 'READY':
                # Применяем изменения
                participant.commit()
                print(f"  {participant}: COMMITTED")
            else:
                # Откатываем
                participant.rollback()
                print(f"  {participant}: ROLLED BACK")
    
    def execute_transaction(self, operation):
        """Выполняем двухфазный коммит"""
        # Фаза 1
        if not self.phase_1_prepare(operation):
            # Если подготовка не удалась, откатываем все
            for participant in self.participants:
                participant.rollback()
            print("\nТранзакция ОТМЕНЕНА")
            return False
        
        # Фаза 2
        self.phase_2_commit()
        print("\nТранзакция УСПЕШНО ЗАВЕРШЕНА")
        return True

# Пример использования
coordinator = TwoPhaseCommit()
bank_a = DatabaseConnection('bank_a')
bank_b = DatabaseConnection('bank_b')

coordinator.register_participant(bank_a)
coordinator.register_participant(bank_b)

# Переводим 100 рублей со счёта A на счёт B
operation = '''
UPDATE accounts SET balance = balance - 100 WHERE id = 'account_a';
UPDATE accounts SET balance = balance + 100 WHERE id = 'account_b';
'''

coordinator.execute_transaction(operation)

Преимущества 2PC

  • Гарантия консистентности — либо всё, либо ничего
  • ACID свойства — сохраняются в распределённой среде
  • Предсказуемость — чёткое определение состояния

Недостатки 2PC (ОЧЕНЬ ВАЖНО!)

  1. Блокировки на длительное время — ресурсы заблокированы до коммита
  2. Снижение производительности — синхронная координация
  3. Проблемы с отказом — если координатор упадёт, участники зависнут
  4. Не масштабируется — неэффективен в крупных распределённых системах
  5. Deadlock-подверженность — повышенный риск deadlock'ов

Проблема: отказ координатора

ИСХОДНОЕ СОСТОЯНИЕ:
- Участник 1: заблокирован в режиме READY
- Участник 2: заблокирован в режиме READY
- Координатор: упал, не успев отправить COMMIT

РЕЗУЛЬТАТ:
- Участники ждут решения координатора (DEADLOCK!)
- Ресурсы остаются заблокированными
- Другие транзакции не могут выполняться

Альтернативы 2PC

Saga Pattern (более гибкий для микросервисов):

class SagaTransaction:
    def __init__(self):
        self.steps = []
        self.compensations = []  # Откаты для каждого шага
    
    def add_step(self, action, compensation):
        self.steps.append(action)
        self.compensations.append(compensation)
    
    def execute(self):
        for i, step in enumerate(self.steps):
            try:
                step()  # Выполняем шаг
            except Exception as e:
                # Откатываем все успешные шаги в обратном порядке
                for j in range(i - 1, -1, -1):
                    self.compensations[j]()
                raise

# Пример: перевод денег через несколько систем
saga = SagaTransaction()

# Шаг 1: снять со счета A
saga.add_step(
    action=lambda: bank_a.withdraw(100),
    compensation=lambda: bank_a.deposit(100)
)

# Шаг 2: пополнить счет B
saga.add_step(
    action=lambda: bank_b.deposit(100),
    compensation=lambda: bank_b.withdraw(100)
)

saga.execute()

Когда использовать 2PC

  • Система имеет несколько БД, которые должны быть всегда консистентны
  • Система не критична к задержкам (банковские системы, критичные операции)
  • Отказоустойчивость управляется на уровне инфраструктуры

Когда избегать 2PC

  • Микросервисы — используйте Saga
  • Высоконагруженные системы — используйте eventual consistency
  • Необходима высокая масштабируемость — используйте асинхронные подходы

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

Что такое двухфазный коммит? | PrepBro