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

Какие знаешь способы достижения сильно согласованности?

2.4 Senior🔥 91 комментариев
#REST API и микросервисы#Базы данных и SQL

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

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

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

Способы достижения сильной согласованности (Strong Consistency)

Сильная согласованность - это один из самых важных аспектов распределенных систем. Это означает, что все узлы системы видят одни и те же данные в один и тот же момент времени. Рассмотрим основные подходы и техники для достижения этого.

Теория CAP и PACELC

CAP теорема гласит, что в распределенной системе можно гарантировать только два из трех свойств:

  • Consistency (согласованность) - все узлы видят одни и те же данные
  • Availability (доступность) - система всегда отвечает
  • Partition Tolerance (устойчивость к разделению сети) - система работает при разделении сети

PACELC теорема уточняет: если нет разделения сети (нормальный режим), выбирают между Latency и Consistency.

1. Синхронная репликация (Synchronous Replication)

Первичный узел не подтверждает запись, пока все реплики не подтвердят.

// Пример: Write с синхронной репликацией
@Transactional(isolation = Isolation.SERIALIZABLE)
public void transferMoney(Long fromId, Long toId, BigDecimal amount) {
  Account from = accountRepository.findByIdWithLock(fromId);
  Account to = accountRepository.findByIdWithLock(toId);
  
  from.setBalance(from.getBalance().subtract(amount));
  to.setBalance(to.getBalance().add(amount));
  
  accountRepository.save(from);
  accountRepository.save(to);
  // COMMIT ждет подтверждения от всех реплик (strong consistency)
}

Плюсы:

  • Гарантированная сильная согласованность
  • Нет риска потери данных

Минусы:

  • Высокая задержка (latency)
  • Если одна реплика недоступна, система может остановиться
  • Неподходит для высоконагруженных систем

2. Quorum-based Replication

Данные репликуются на N узлов, но подтверждение приходит от W узлов (W > N/2).

N = 3 узла (total replicas)
W = 2 узла (write quorum)
R = 2 узла (read quorum)

Write: PRIMARY пишет, ждет подтверждения от 2 узлов (включая себя)
Read: Клиент читает с 2 узлов и берет самую свежую версию

W + R > N => Strong Consistency

Пример в коде:

@Service
public class QuorumReplicationService {
  private static final int TOTAL_REPLICAS = 3;
  private static final int WRITE_QUORUM = 2;
  private static final int READ_QUORUM = 2;
  
  public void writeWithQuorum(String key, String value) {
    List<Replica> replicas = getReplicas(key);
    int successCount = 0;
    
    // Отправить на все реплики
    for (Replica replica : replicas) {
      if (replica.write(key, value)) {
        successCount++;
      }
      // Если достигли quorum, можно вернуть успех
      if (successCount >= WRITE_QUORUM) {
        return; // Strong consistency достигнута
      }
    }
    
    if (successCount < WRITE_QUORUM) {
      throw new QuorumException("Failed to write with required quorum");
    }
  }
  
  public String readWithQuorum(String key) {
    List<Replica> replicas = getReplicas(key);
    List<VersionedValue> values = new ArrayList<>();
    
    // Читаем с READ_QUORUM узлов
    for (int i = 0; i < READ_QUORUM; i++) {
      Replica replica = replicas.get(i);
      VersionedValue value = replica.read(key);
      if (value != null) {
        values.add(value);
      }
    }
    
    // Возвращаем значение с самой высокой версией
    return values.stream()
      .max(Comparator.comparingLong(VersionedValue::getVersion))
      .map(VersionedValue::getValue)
      .orElse(null);
  }
}

Применение:

  • W + R > N => strong consistency
  • W + R ≤ N => eventual consistency

Примеры:

  • Cassandra: N=3, W=3, R=3 => strong consistency
  • DynamoDB: N=3, W=2, R=2 => eventual consistency

3. Two-Phase Commit (2PC)

Добиться сильной согласованности в распределенных системах с помощью координатора.

Фаза 1 (Prepare):
  Координатор: "Готовы ли вы выполнить транзакцию?"
  Узлы: "Да, я заблокировал ресурсы"

Фаза 2 (Commit/Abort):
  Координатор: "Коммитьте" или "Откатитесь"
  Узлы: "OK, выполнили"

Пример:

@Service
public class TwoPhaseCommitService {
  
  public boolean executeDistributedTransaction(
      List<TransactionParticipant> participants,
      TransactionOperation operation) {
    
    // Фаза 1: Prepare
    List<TransactionParticipant> prepared = new ArrayList<>();
    for (TransactionParticipant p : participants) {
      if (!p.prepare(operation)) {
        // Если кто-то не готов, откатываем
        rollbackAll(prepared);
        return false;
      }
      prepared.add(p);
    }
    
    // Фаза 2: Commit
    for (TransactionParticipant p : prepared) {
      try {
        p.commit();
      } catch (Exception e) {
        // Раздельные транзакции - данные могут быть несогласованными
        log.error("Commit failed on node", e);
        return false;
      }
    }
    
    return true;
  }
  
  private void rollbackAll(List<TransactionParticipant> participants) {
    for (TransactionParticipant p : participants) {
      try {
        p.rollback();
      } catch (Exception e) {
        log.error("Rollback failed", e);
      }
    }
  }
}

Плюсы:

  • Гарантированная сильная согласованность
  • Работает для распределенных транзакций

Минусы:

  • Blocking операции - низкая производительность
  • Не масштабируется
  • Проблема с network failures

4. Paxos алгоритм

Это алгоритм консенсуса для распределенных систем. Гарантирует, что все узлы договорятся об одном значении.

Предложение (Proposal):
  Proposer отправляет номер предложения и значение
  Acceptors отвечают готовностью
  Если quorum согласился - принимаем значение

Используется в:

  • Google Chubby (хранилище конфигурации)
  • Apache ZooKeeper (координация)

5. Raft алгоритм

Упрощенная версия Paxos, более понятная для реализации.

Лидер выбирается (election)
Лидер логирует операции (log replication)
Если quorum логирует - операция committed
Все узлы применяют committed операции

Используется в:

  • etcd (распределенное хранилище ключей)
  • Consul (service mesh)
  • NATS (message broker)
@Service
public class RaftConsensusService {
  
  private String currentLeader;
  private List<String> followers;
  private LogEntry[] log;
  private int commitIndex;
  
  public boolean replicateLogEntry(LogEntry entry) {
    // Лидер добавляет в свой лог
    appendToLog(entry);
    
    // Отправляет на followers
    int replicas = 1; // сам лидер
    for (String follower : followers) {
      if (sendToFollower(follower, entry)) {
        replicas++;
      }
    }
    
    // Если quorum реплицировал - committed
    if (replicas > followers.size() / 2) {
      commitIndex = log.length - 1;
      applyToStateMachine(entry);
      return true;
    }
    
    return false;
  }
  
  private void applyToStateMachine(LogEntry entry) {
    // Применяем к state machine только после commit
    // Гарантирует, что все узлы применят в одном порядке
  }
}

6. Linearizability

Наиболее сильное понятие согласованности. Операции выглядят как выполняющиеся атомарно в некотором порядке.

// Пример линеаризуемой операции
public synchronized void writeWithLinearization(String key, String value) {
  // synchronized гарантирует линеаризуемость
  // Только один поток может выполнять эту операцию одновременно
  database.put(key, value);
}

public synchronized String readWithLinearization(String key) {
  return database.get(key);
}

7. Vector Clocks (для понимания порядка событий)

Помогает определить причинно-следственные отношения между событиями.

public class VectorClock {
  private Map<String, Integer> clock; // [node1: 1, node2: 2]
  
  public void increment(String nodeId) {
    clock.put(nodeId, clock.getOrDefault(nodeId, 0) + 1);
  }
  
  public boolean happensBefore(VectorClock other) {
    // Проверяем, что this произошло раньше other
    boolean less = false;
    for (String nodeId : clock.keySet()) {
      int thisVal = clock.getOrDefault(nodeId, 0);
      int otherVal = other.clock.getOrDefault(nodeId, 0);
      
      if (thisVal > otherVal) return false;
      if (thisVal < otherVal) less = true;
    }
    return less;
  }
}

8. Гибридные подходы

Strong Eventual Consistency:

// Используем strong consistency для критичных операций
// eventual consistency для не критичных

@Service
public class HybridConsistencyService {
  
  @Transactional(isolation = Isolation.SERIALIZABLE)
  public void criticalOperation() {
    // Strong consistency - финансовые транзакции
  }
  
  public void nonCriticalOperation() {
    // Eventual consistency - кэш, статистика
  }
}

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

Выбирай strong consistency когда:

  • Финансовые системы
  • Критические данные (инвентарь, заказы)
  • Регуляторные требования (соответствие юридическим нормам)

Выбирай eventual consistency когда:

  • Социальные сети (лайки, комментарии)
  • Аналитика
  • Кэш
  • High-performance требования

Инструменты для strong consistency:

  • PostgreSQL Synchronous Replication
  • etcd (Raft)
  • ZooKeeper (Paxos)
  • Consul (Raft)

Сильная согласованность - это дорогостоящая операция в распределенных системах. Выбирай её только когда действительно нужна!

Какие знаешь способы достижения сильно согласованности? | PrepBro