Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Паттерн 2PC (Two-Phase Commit)
2PC (Two-Phase Commit) — это распределённый протокол атомарности, гарантирующий, что транзакция либо будет успешно завершена на всех участников, либо откачена полностью. Он используется для координации транзакций между несколькими независимыми системами или базами данных.
Структура 2PC
2PC состоит из двух фаз:
Фаза 1: Prepare (Голосование)
- Координатор (Coordinator) отправляет запрос "prepare" всем участникам (Participants)
- Каждый участник:
- Выполняет необходимые операции
- Получает экслюзивные блокировки на ресурсы
- Проверяет возможность завершения операции
- Отправляет ответ "Yes" (готов коммитить) или "No" (не может)
- Координатор ждёт ответы от всех участников
Фаза 2: Commit (Выполнение)
-
Если все ответили "Yes":
- Координатор отправляет "commit" всем участникам
- Участники выполняют коммит и освобождают блокировки
- Отправляют подтверждение
-
Если хотя бы один ответил "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 остаётся важным паттерном для систем, требующих строгую консистентность, но в микросервисной архитектуре часто предпочитают более масштабируемые решения.