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-интерфейс. Ее изучение открывает путь к созданию приложений, которые по-настоящему устойчивы к сбоям и легко масштабируются по всему миру.
Разбор CockroachDB с примерами кода: от основ до продвинутых сценариев
Практическое руководство по работе с распределенной SQL-базой данных CockroachDB, включая создание схем, транзакции, обработку повторов, индексы и примеры кода на SQL и Go.
137
2
Комментарии (7)