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

Выполняется ли ACID в микросервисной архитектуре

2.0 Middle🔥 121 комментариев
#Архитектура и паттерны#Базы данных (SQL)

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

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

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

Выполняется ли ACID в микросервисной архитектуре

Коротко: НЕТ, не полностью. Это один из главных компромиссов микросервисной архитектуры.

ACID - это что?

ACID - это четыре гарантии для транзакций:

  • Atomicity (Атомарность): либо всё либо ничего
  • Consistency (Согласованность): данные всегда в валидном состоянии
  • Isolation (Изоляция): транзакции не мешают друг другу
  • Durability (Стойкость): если завершилась, она не потеряется
-- ACID гарантирует в одной БД:
BEGIN TRANSACTION
  UPDATE accounts SET balance = balance - 100 WHERE id = 1;
  UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;
-- Или выполнится всё, или ничего
-- Невозможен случай где деньги потеряются

Проблема в микросервисах

В микросервисной архитектуре данные распределены по разным БД:

User Service     Order Service     Payment Service
┌──────────┐    ┌──────────┐      ┌──────────┐
│PostgreSQL│    │PostgreSQL│      │PostgreSQL│
└──────────┘    └──────────┘      └──────────┘
     |               |                  |
     +───────────────+──────────────────+

Проблема: как сделать ACID операцию между несколькими БД?

1. Пользователь нажимает "Купить"
2. Order Service создаёт заказ
3. Payment Service снимает деньги
4. Inventory Service уменьшает остаток

Что если на шаге 3 ошибка?
Заказ создан, но деньги не снялись!

Решение 1: Distributed Transactions (2PC)

Two-Phase Commit - попытка сохранить ACID:

Coordinator
   |
   +---- "Prepare" Request to all services
   |      |
   |      Order Service: "Готов?"  ✓
   |      Payment Service: "Готов?"  ✓
   |      Inventory Service: "Готов?"  ✓
   |
   +---- "Commit" Request if all ready
          |
          Order Service: COMMIT ✓
          Payment Service: COMMIT ✓
          Inventory Service: COMMIT ✓
# Псевдокод 2PC
class DistributedTransaction:
    def execute(self):
        try:
            # Phase 1: Prepare
            order_service.prepare(order_data)
            payment_service.prepare(payment_data)
            inventory_service.prepare(inventory_data)
            
            # Phase 2: Commit
            order_service.commit()
            payment_service.commit()
            inventory_service.commit()
        except:
            # Rollback if any fails
            order_service.rollback()
            payment_service.rollback()
            inventory_service.rollback()

Проблемы 2PC:

  • Очень медленно (много сетевых операций)
  • Хрупко (если сервис упадёт в phase 1?)
  • Deadlocks
  • Не работает в облаке (высокая latency)
  • Это антипаттерн в микросервисах

Решение 2: Saga Pattern

Saga - это последовательность локальных транзакций:

Customer Service          Order Service        Payment Service
     |                        |                      |
     +--create order---------->|                      |
     |                         +--charge payment----->|
     |                         |<--payment OK---------|
     |<--order confirmed-------|                      |

Если Payment падает:
     +--cancel order---------->|
     |                         |

Есть два варианта:

Choreography (события)

# Order Service
async def create_order(order_data):
    order = Order.create(order_data)
    await publish_event('order.created', order_data)
    # Event Bus распространит это дальше

# Payment Service подслушивает
@event_listener('order.created')
async def charge_payment(order_data):
    try:
        payment = await charge_card(order_data.payment_info)
        await publish_event('payment.completed', payment)
    except:
        await publish_event('payment.failed', order_data)

# Inventory Service подслушивает
@event_listener('payment.completed')
async def reserve_inventory(payment_data):
    try:
        await reserve(payment_data.items)
        await publish_event('inventory.reserved', data)
    except:
        await publish_event('inventory.reservation.failed', data)

Orchestration (оркестр)

# Order Orchestrator управляет всеми
class OrderOrchestrator:
    async def process_order(self, order_data):
        try:
            # 1. Create order
            order = await order_service.create(order_data)
            
            # 2. Charge payment
            try:
                payment = await payment_service.charge(order)
            except:
                await order_service.cancel(order)
                raise
            
            # 3. Reserve inventory
            try:
                inventory = await inventory_service.reserve(order)
            except:
                await payment_service.refund(payment)
                await order_service.cancel(order)
                raise
            
            return order
        except Exception as e:
            # Compensating transaction
            await self._compensate(order)
            raise

    async def _compensate(self, order):
        # Откатываем всё что сделали
        pass

ACID в микросервисах - что сохранилось?

ACID в классике          ACID в микросервисах
┌──────────────────────┬──────────────────────┐
│ Atomicity   | 100%    │ 0% (Saga компенсирует)|
│ Consistency | 100%    │ ~70% (eventual)      │
│ Isolation   | 100%    │ 0% (no locking)      │
│ Durability  | 100%    │ 100%                 │
└──────────────────────┴──────────────────────┘

Eventual Consistency вместо ACID

В микросервисах используют Eventual Consistency:

Время:
T0: Деньги сняты (Payment Service)
T1: Заказ создан (Order Service)
T2: Инвентарь обновлен (Inventory Service)
T3: Email отправлен (Notification Service)

Между T0 и T3 система в inconsistent состоянии!
Но в конце концов (~100ms) всё согласовано.

Плюсы:

  • Быстро
  • Масштабируемо
  • Resilient (если сервис упадёт, другие работают)

Минусы:

  • Может быть lag между сервисами
  • Нужно handle race conditions
  • Сложнее написать код

Пример: очистка от неудачи

# Order Service создал заказ
order = Order.create(customer_id=123)

# Payment Service не ответил (timeout)
try:
    payment = await payment_service.charge(order_id=order.id)
except TimeoutError:
    # Что делать?
    # Опция 1: Отменить заказ (но может быть платёж прошёл!)
    await order.cancel()
    # Опция 2: Оставить в pending и retry
    await order.mark_pending()
    # Опция 3: Компенсирующая транзакция
    await compensate_order(order)

Когда НЕ использовать микросервисы

Если тебе нужен полный ACID:

# Финансовая операция - перевод денег
# Требует гарантии что одно из двух:
# 1. Деньги переведены
# 2. Ничего не произошло
# Вариант: остаться в monolith с одной БД

# Или используй:
# - Distributed transaction coordinator (dTCC)
# - Blockchain (если trust нужна)
# - Синхронные API вместо асинхронных (но медленнее)

Best Practices для микросервисов

# 1. Используй Saga для компенсации
# 2. Делай локальные транзакции в каждом сервисе
# 3. Используй идемпотентные операции (safe to retry)
# 4. Дай timeout и retry логику
# 5. Логируй все события для audit trail

# Идемпотентная операция:
def charge_payment(order_id, payment_id, amount):
    # Даже если вызовешь дважды - результат одинаков
    payment = Payment.find_or_create(
        payment_id=payment_id,
        order_id=order_id,
        amount=amount
    )
    return payment

Сравнение стратегий

                Monolith  | Microservices(2PC) | Microservices(Saga)
────────────────────────────────────────────────────────────────
ACID            | ✓✓✓    | ✓✓                | ✓
Скорость        | ✓✓     | ✓ (медленно)       | ✓✓✓
Масштаб         | ✓      | ✓✓✓               | ✓✓✓
Resilience      | ✓      | ✗                 | ✓✓✓
Complexity      | ✓      | ✗✗                | ✗✗

Реальный пример: Amazon

Amazon использует микросервисы без ACID:

1. Order создан (confirmed)
2. Payment submitted (async)
3. Inventory reserved (async)
4. Fulfillment scheduled (async)
5. Email sent (async)

Если payment failed:
- Order переходит в "payment failed"
- Inventory зарезервирован отменяется
- Customer получает email "Платёж не прошёл"
- Eventual consistency за ~5 секунд

Инструменты для Saga

  • Apache Kafka - event streaming
  • RabbitMQ - message broker
  • Temporal - orchestration
  • Axon - event sourcing
  • Camunda - BPMN workflow

Заключение

  • ACID не выполняется полностью в микросервисах
  • Используй Saga pattern для компенсирующих транзакций
  • Eventual Consistency - это обычно OK на практике
  • Не используй микросервисы если нужен полный ACID
  • Idempotency - важнее чем ACID в микросервисах