Разбор CockroachDB с примерами кода: от основ до продвинутых сценариев

Практическое руководство по работе с распределенной SQL-базой данных CockroachDB, включая создание схем, транзакции, обработку повторов, индексы и примеры кода на SQL и Go.
CockroachDB — это распределенная SQL-база данных с открытым исходным кодом, разработанная для обеспечения высокой устойчивости, согласованности и горизонтальной масштабируемости. Ее название («таракан») символизирует живучесть: система предназначена для выживания при сбоях оборудования и даже целых дата-центров. В основе лежит протокол распределенного консенсуса Raft и транзакционная модель, совместимая с ANSI SQL, что делает ее мощной альтернативой как традиционным SQL-СУБД, так и NoSQL-системам.

Архитектурно CockroachDB представляет собой кластер узлов (нод), каждый из которых хранит часть данных. Данные автоматически шардируются (разбиваются на диапазоны) и реплицируются (обычно 3 реплики) между узлами для отказоустойчивости. Клиент может подключиться к любому узлу, который выступает в роли шлюза для выполнения запросов. База обеспечивает строгую согласованность (Serializable isolation) по умолчанию, что является ее ключевым преимуществом.

Давайте рассмотрим работу с CockroachDB на практике, начиная с базовых операций. Предположим, у нас есть развернутый локальный кластер (можно запустить одним бинарным файлом). Подключимся с помощью стандартного клиента `cockroach sql`.

Создадим базу данных и таблицу для примера с пользователями и заказами:

CREATE DATABASE shop;

USE shop;

CREATE TABLE users (
 id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
 email STRING UNIQUE NOT NULL,
 full_name STRING,
 created_at TIMESTAMP DEFAULT now()
);

CREATE TABLE orders (
 id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
 user_id UUID REFERENCES users(id) ON DELETE CASCADE,
 amount DECIMAL(10, 2),
 status STRING DEFAULT 'pending',
 created_at TIMESTAMP DEFAULT now()
);

Это знакомый любому SQL-разработчику синтаксис. CockroachDB поддерживает множество типов данных, включая JSONB, массивы и Spatial типы (с помощью расширения). Теперь вставим данные:

INSERT INTO users (email, full_name) VALUES
('alice@example.com', 'Alice Johnson'),
('bob@example.com', 'Bob Smith');

Теперь выполним распределенную транзакцию, вставив заказ и обновив что-либо в рамках одного атомарного блока:

BEGIN;
SAVEPOINT cockroach_restart;
-- Находим ID пользователя
WITH user_cte AS (SELECT id FROM users WHERE email = 'alice@example.com')
INSERT INTO orders (user_id, amount)
SELECT id, 99.99 FROM user_cte;
-- Имитируем потенциальный конфликт или сложную логику
COMMIT;

В случае конфликта (например, из-за параллельной транзакции) CockroachDB может предложить повторить SAVEPOINT. Для этого используется паттерн «автоматического повтора» на стороне клиента. Вот как это может выглядеть на Go с использованием драйвера `pgx`:

import (
 "context"
 "github.com/jackc/pgx/v5"
 "log"
)

func placeOrder(ctx context.Context, conn *pgx.Conn, email string, amount float64) error {
 for {
 err := func() error {
 tx, err := conn.Begin(ctx)
 if err != nil { return err }
 defer tx.Rollback(ctx)

 // Объявляем SAVEPOINT для возможного перезапуска
 _, err = tx.Exec(ctx, "SAVEPOINT cockroach_restart")
 if err != nil { return err }

 var userID string
 err = tx.QueryRow(ctx, "SELECT id FROM users WHERE email = $1", email).Scan(&userID)
 if err != nil { return err }

 _, err = tx.Exec(ctx, "INSERT INTO orders (user_id, amount) VALUES ($1, $2)", userID, amount)
 if err != nil { return err }

 // Имитация бизнес-логики...
 _, err = tx.Exec(ctx, "RELEASE SAVEPOINT cockroach_restart")
 if err != nil { return err }

 return tx.Commit(ctx)
 }()
 if err == nil {
 return nil // Успех!
 }
 // Проверяем, является ли ошибка retryable
 if !isRetryableError(err) {
 return err // Неповторимая ошибка
 }
 // Цикл продолжается для повторной попытки
 }
}

Теперь рассмотрим более продвинутые возможности. CockroachDB поддерживает индексы, в том числе составные и инвертированные для JSON. Создадим индекс для быстрого поиска заказов по статусу и дате:

CREATE INDEX idx_orders_status_created ON orders(status, created_at DESC);

Для анализа производительности запросов используем `EXPLAIN ANALYZE`, который покажет, как запрос выполняется в распределенной среде:

EXPLAIN ANALYZE
SELECT u.full_name, SUM(o.amount) as total_spent
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE o.status = 'completed'
GROUP BY u.id, u.full_name
HAVING SUM(o.amount) > 1000;

В выводе вы увидите этапы выполнения на разных узлах, что критично для диагностики медленных запросов. Еще одна мощная функция — изменение схемы (DDL) без блокировки. Добавим колонку в таблицу `users`:

ALTER TABLE users ADD COLUMN phone_number STRING;

Эта операция выполняется онлайн и не блокирует чтение/запись существующих данных, что крайне важно для работающих 24/7 приложений.

Для геораспределенности CockroachDB позволяет настраивать локации узлов и правила выживаемости. Например, можно разместить три реплики одного диапазона данных в разных AWS регионах (us-east-1, eu-west-1, ap-southeast-1) и указать, что данные должны пережить потерю целого региона. Это настраивается через многоуровневые зоны выживания.

Наконец, рассмотрим интеграцию с приложением на Spring Boot через стандартный драйвер PostgreSQL, так как CockroachDB использует Postgres-совместимый протокол. В `application.properties`:

spring.datasource.url=jdbc:postgresql://localhost:26257/shop?sslmode=disable
spring.datasource.username=root
spring.datasource.password=

Затем можно использовать JPA репозитории как обычно. Важно лишь учитывать нюансы, такие как использование `UUID` для первичных ключей и понимание ограничений (например, отсутствие полноценных последовательностей в пользу `unique_rowid()` или `gen_random_uuid()`).

CockroachDB — это сложная, но элегантная система, которая абстрагирует множество сложностей распределенных систем, предоставляя разработчикам знакомый SQL-интерфейс. Ее изучение открывает путь к созданию приложений, которые по-настоящему устойчивы к сбоям и легко масштабируются по всему миру.
137 2

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

avatar
w5se02qf 27.03.2026
Интересно, как CockroachDB справляется с геораспределенностью и задержками? Есть ли best practices для таких сценариев?
avatar
1qdcsz4zi 28.03.2026
После проблем с отказоустойчивостью в Postgres, присматриваюсь к CockroachDB. Статья добавила аргументов 'за'.
avatar
i9vewm 28.03.2026
Актуально! Вопрос по коду: планируете ли вы рассмотреть работу с JSONB и пространственными данными?
avatar
px5uybd 29.03.2026
Попробовал развернуть кластер на трех нодах по гайду. Всё сработало, горизонтальное масштабирование впечатляет!
avatar
px5uybd 30.03.2026
Статья хорошая, но хотелось бы больше сравнений с PostgreSQL в плане производительности на конкретных ворклоадах.
avatar
bkputs 30.03.2026
Сложновато для новичка. Не хватает базового примера развертывания и первой миграции данных из обычной БД.
avatar
wak4rrv 31.03.2026
Отличный разбор! Особенно полезны примеры кода для распределенных транзакций. Жду продолжения про оптимизацию запросов.
Вы просмотрели все комментарии