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

Что такое паттерн 2PC?

2.2 Middle🔥 181 комментариев
#Тестирование

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

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

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

Паттерн 2PC (Two-Phase Commit)

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

Структура 2PC

2PC состоит из двух фаз:

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

  1. Координатор (Coordinator) отправляет запрос "prepare" всем участникам (Participants)
  2. Каждый участник:
    • Выполняет необходимые операции
    • Получает экслюзивные блокировки на ресурсы
    • Проверяет возможность завершения операции
    • Отправляет ответ "Yes" (готов коммитить) или "No" (не может)
  3. Координатор ждёт ответы от всех участников

Фаза 2: Commit (Выполнение)

  1. Если все ответили "Yes":

    • Координатор отправляет "commit" всем участникам
    • Участники выполняют коммит и освобождают блокировки
    • Отправляют подтверждение
  2. Если хотя бы один ответил "No" или не ответил:

    • Координатор отправляет "rollback" всем участникам
    • Участники откачивают изменения и освобождают блокировки
    • Отправляют подтверждение

Пример реализации в Python

import uuid
from enum import Enum
from typing import Dict, List
from datetime import datetime

class TransactionState(Enum):
    PREPARE = "prepare"
    COMMITTED = "committed"
    ABORTED = "aborted"

class Participant:
    def __init__(self, name: str):
        self.name = name
        self.state = None
        self.data = {}
    
    def prepare(self, transaction_id: str, operation: Dict) -> bool:
        """Фаза 1: проверка возможности выполнения операции"""
        print(f"[{self.name}] Preparing transaction {transaction_id}")
        try:
            # Проверяем, можна ли выполнить операцию
            if self._validate(operation):
                # Получаем блокировку и сохраняем состояние
                self.state = TransactionState.PREPARE
                return True
            return False
        except Exception as e:
            print(f"[{self.name}] Error during prepare: {e}")
            return False
    
    def commit(self, transaction_id: str) -> bool:
        """Фаза 2: коммит операции"""
        print(f"[{self.name}] Committing transaction {transaction_id}")
        if self.state == TransactionState.PREPARE:
            self.state = TransactionState.COMMITTED
            print(f"[{self.name}] Transaction committed")
            return True
        return False
    
    def rollback(self, transaction_id: str) -> bool:
        """Откат операции"""
        print(f"[{self.name}] Rolling back transaction {transaction_id}")
        self.state = TransactionState.ABORTED
        print(f"[{self.name}] Transaction rolled back")
        return True
    
    def _validate(self, operation: Dict) -> bool:
        """Валидация операции"""
        # Простая проверка: операция должна иметь ключ value
        return "value" in operation and operation["value"] > 0

class TransactionCoordinator:
    def __init__(self):
        self.participants: List[Participant] = []
        self.transactions: Dict[str, Dict] = {}
    
    def add_participant(self, participant: Participant):
        self.participants.append(participant)
    
    def execute_transaction(self, operation: Dict) -> bool:
        """Выполняет распределённую транзакцию"""
        transaction_id = str(uuid.uuid4())
        print(f"\n=== Starting transaction {transaction_id} ===")
        
        # Фаза 1: Prepare
        print("\n[Phase 1] Preparing...")
        votes = []
        for participant in self.participants:
            vote = participant.prepare(transaction_id, operation)
            votes.append(vote)
        
        # Проверяем голоса
        if all(votes):
            # Фаза 2: Commit
            print("\n[Phase 2] All participants voted YES. Committing...")
            for participant in self.participants:
                participant.commit(transaction_id)
            self.transactions[transaction_id] = {"status": "committed", "time": datetime.now()}
            print(f"\n=== Transaction {transaction_id} COMMITTED ===")
            return True
        else:
            # Откат
            print(f"\n[Phase 2] Participant voted NO. Rolling back...")
            for participant in self.participants:
                participant.rollback(transaction_id)
            self.transactions[transaction_id] = {"status": "aborted", "time": datetime.now()}
            print(f"\n=== Transaction {transaction_id} ABORTED ===")
            return False

# Пример использования
if __name__ == "__main__":
    # Создаём координатор и участников
    coordinator = TransactionCoordinator()
    db1 = Participant("Database 1")
    db2 = Participant("Database 2")
    db3 = Participant("Database 3")
    
    coordinator.add_participant(db1)
    coordinator.add_participant(db2)
    coordinator.add_participant(db3)
    
    # Успешная транзакция
    coordinator.execute_transaction({"value": 100, "type": "transfer"})
    
    # Неудачная транзакция (невалидная операция)
    coordinator.execute_transaction({"value": -50, "type": "transfer"})

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

  • Гарантия атомарности: либо все, либо ничего
  • Консистентность данных: невозможны частичные обновления
  • Надёжность: восстановление после сбоев
  • Простота концепции: легко понять и объяснить

Недостатки 2PC

  • Производительность: блокировки во время подготовки замораживают ресурсы
  • Масштабируемость: плохо работает с большим числом участников
  • Восстановление: сложно восстанавливаться после сбоя координатора
  • Network partition: не работает при разделении сети
  • Latency: все участники должны ответить перед продолжением

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

  • Saga Pattern: асинхронная последовательность локальных транзакций
  • Event Sourcing: хранение событий вместо состояния
  • BASE (Basically Available, Soft state, Eventual consistency): слабая консистентность

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

Что такое паттерн 2PC? | PrepBro