NewSQL: CockroachDB, YugabyteDB, TiDB и распределённый SQL

↑ 4.3 Поиск и специализированные БД · ← Предыдущая

Желательно~7 мин чтенияУровень: senior

Зачем это на собесе

На уровне senior спрашивают: «Что делать, когда PostgreSQL на одном узле не хватает, а отказ от SQL не вариант?» Ответ строится вокруг выбора: вертикальное масштабирование, реплики, шардирование, либо распределённый SQL со своими компромиссами.

Подтемы

  • Что такое NewSQL
  • Как это устроено: Raft и диапазоны данных
  • CockroachDB, YugabyteDB, TiDB, Spanner
  • Цена распределённости
  • Когда выбирать

Объяснение

Идея

NewSQL (распределённый SQL) стремится сочетать SQL и ACID-транзакции реляционных баз с горизонтальным масштабированием и отказоустойчивостью NoSQL. Данные автоматически делятся на диапазоны (ranges, tablets), каждый диапазон реплицируется на несколько узлов алгоритмом консенсуса Raft.

flowchart LR
    C["Клиент (SQL)"] --> N1["Узел 1"]
    C --> N2["Узел 2"]
    C --> N3["Узел 3"]
    subgraph Кластер
      N1 --- N2 --- N3
      R1["Диапазон A<br/>лидер: узел 1"]
      R2["Диапазон B<br/>лидер: узел 2"]
      R3["Диапазон C<br/>лидер: узел 3"]
    end

Каждый диапазон имеет лидера и реплики. Запись подтверждается, когда большинство реплик записали её. Потеря узла не останавливает кластер: лидерство переходит к другой реплике.

Представители

СистемаСовместимостьОсобенности
CockroachDBпротокол PostgreSQL (с отличиями), собственный SQLстрогая сериализуемость по умолчанию, геораспределение, автоматическое перебалансирование
YugabyteDBслой YSQL на коде PostgreSQL и YCQL в стиле Cassandraвысокая совместимость с PostgreSQL, Raft
TiDBпротокол MySQLразделение вычислений и хранения (TiKV), аналитика через TiFlash
Google Spannerоблачный сервисглобально согласованные транзакции (TrueTime), управляемый сервис

Цена распределённости

  • Задержка. Транзакция требует подтверждения большинством реплик, часто по сети: запись заметно медленнее, чем на одном PostgreSQL.
  • Распределённые транзакции дороже: охватывают несколько диапазонов и узлов.
  • Повторы транзакций. Конфликты приводят к ошибкам сериализации (SQLSTATE 40001), приложение должно повторять транзакцию.
  • Совместимость не стопроцентная. Расширения, типы, хранимые процедуры и планы запросов отличаются от PostgreSQL.
  • Стоимость и сложность эксплуатации. Минимум три узла, мониторинг, сетевые требования.

Когда выбирать

flowchart TD
    A["Нужно больше одного узла PostgreSQL"] --> B{"Что именно?"}
    B -- "чтение" --> C["Реплики чтения"]
    B -- "запись упирается в один узел" --> D{"Данные делятся по ключу<br/>(тенант, пользователь)?"}
    D -- да --> E["Шардирование / Citus"]
    D -- нет --> F{"Нужны глобальные транзакции<br/>и отказоустойчивость по регионам?"}
    F -- да --> G["Распределённый SQL<br/>(CockroachDB, YugabyteDB, TiDB, Spanner)"]
    F -- нет --> H["Вертикальное масштабирование,<br/>оптимизация запросов"]

Распределённый SQL оправдан, когда нужны горизонтальное масштабирование записи, автоматическая отказоустойчивость и транзакции на разных данных, и команда готова принять задержки и эксплуатацию.

Примеры

Повтор транзакции при ошибке сериализации

async Task<T> RetryAsync<T>(Func<NpgsqlTransaction, Task<T>> work, NpgsqlDataSource ds, int attempts = 5)
{
    for (var i = 1; ; i++)
    {
        await using var conn = await ds.OpenConnectionAsync();
        await using var tx = await conn.BeginTransactionAsync();
        try
        {
            var result = await work(tx);
            await tx.CommitAsync();
            return result;
        }
        catch (PostgresException ex) when (ex.SqlState == "40001" && i < attempts)
        {
            await tx.RollbackAsync();
            await Task.Delay(TimeSpan.FromMilliseconds(50 * i * i));   // нарастающая пауза
        }
    }
}

Так работают с CockroachDB и любыми системами со строгой изоляцией: ошибка 40001 это нормальный сигнал «повторите», а не сбой.

Запуск CockroachDB для экспериментов

docker run -d --name crdb -p 26257:26257 -p 8080:8080 \
  cockroachdb/cockroach start-single-node --insecure
docker exec -it crdb ./cockroach sql --insecure -e "CREATE DATABASE shop;"

Это одиночный узел для обучения, в продакшене нужен кластер из трёх и более узлов с TLS.

Нюансы и подводные камни

  • Выбор первичного ключа. Монотонно растущие ключи создают «горячий» диапазон. Используйте UUID, составные ключи или хэш-шардированные индексы.
  • Проверяйте совместимость. Перед миграцией прогоните тесты на реальных запросах и расширениях.
  • Транзакции должны быть короткими и идемпотентными: они повторяются.
  • Геораспределение. Данные и лидеры привязывают к регионам, иначе задержка между регионами убьёт скорость.
  • Не нужен «на всякий случай». Для большинства приложений реплики, партиционирование и хороший индекс решают задачу дешевле.
  • Лицензии. У продуктов разные лицензии и ограничения бесплатных версий, проверяйте актуальные условия.

Вопросы с ответами

Связанные темы