CockroachDB — это распределенная SQL-база данных с открытым исходным кодом, разработанная для обеспечения высокой устойчивости, согласованности и горизонтальной масштабируемости. Ее архитектура вдохновлена Google Spanner, и она позиционирует себя как «база данных, которую невозможно убить». Давайте разберем ее ключевые концепции и посмотрим, как она работает на практике, через призму кода.
В основе CockroachDB лежит концепция «монолитной» реплицированной ключ-значение хранилки (RockSDB от Facebook), поверх которой реализован совместимый с PostgreSQL SQL-слой. Данные автоматически шардируются (разбиваются) на диапазоны (ranges) размером около 64 МБ, каждый из которых реплицируется (по умолчанию в 3 копии) across different nodes and even geographic regions. Распределенный протокол согласия Raft обеспечивает консенсус между репликами каждого диапазона.
Давайте начнем с простейшего SQL-взаимодействия. Подключимся к кластеру (например, к локальному одноузловому кластеру, запущенному через `cockroach demo`) и создадим таблицу.
CREATE DATABASE bank;
USE bank;
CREATE TABLE accounts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
balance DECIMAL NOT NULL DEFAULT 0,
currency STRING NOT NULL,
last_updated TIMESTAMP DEFAULT now()
);
Этот SQL будет знаком любому, кто работал с PostgreSQL. CockroachDB поддерживает большинство типов данных и функций Postgres. Теперь вставим данные.
INSERT INTO accounts (balance, currency) VALUES (1000.50, 'USD'), (750.00, 'EUR');
Теперь рассмотрим одну из сильнейших сторон — распределенные транзакции с гарантированной согласованностью (Serializable isolation). Допустим, мы хотим перевести деньги с одного счета на другой. В CockroachDB это делается стандартным SQL, но база гарантирует, что даже если строки `accounts` находятся на разных физических серверах (нодах), транзакция будет атомарной и согласованной.
BEGIN;
SELECT balance FROM accounts WHERE id = '...id1...' FOR UPDATE;
-- Приложение проверяет достаточность средств
UPDATE accounts SET balance = balance - 100.00 WHERE id = '...id1...';
UPDATE accounts SET balance = balance + 100.00 WHERE id = '...id2...';
COMMIT;
Ключевая фраза `FOR UPDATE` блокирует строки, предотвращая условия гонки. CockroachDB использует метод MVCC (Multiversion Concurrency Control) и временные метки (HLC — Hybrid Logical Clocks) для ordering транзакций в распределенной среде.
Теперь посмотрим на шардирование и расположение данных. CockroachDB автоматически создает первичный ключ как ключ шардирования по умолчанию. Но мы можем управлять этим с помощью `PARTITION BY`. Более важной является возможность настройки локали данных (Data Placement), например, для соответствия GDPR.
ALTER TABLE accounts CONFIGURE ZONE USING
num_replicas = 5,
constraints = '{"region": "eu-west-1"}',
lease_preferences = '[[+region=eu-west-1]]';
Этот код (упрощенно) указывает, что реплики таблицы `accounts` должны размещаться в регионе `eu-west-1`. CockroachDB будет автоматически перемещать диапазоны для соблюдения этих ограничений.
Еще одна мощная функция — это Change Data Capture (CDC) и создание материализованных представлений. CockroachDB может отправлять изменения в Kafka или облачный storage.
CREATE CHANGEFEED FOR TABLE accounts INTO 'kafka://broker:9092' WITH updated, resolved = '10s';
Это создаст поток изменений в реальном времени. Для аналитических запросов полезны индексы и инвертированные индексы для работы с JSONB.
CREATE INDEX idx_currency ON accounts(currency, balance DESC);
SELECT id, balance FROM accounts WHERE currency = 'USD' ORDER BY balance DESC LIMIT 10;
CockroachDB оптимизирует выполнение этого запроса, возможно, используя только индекс (covering index scan), даже в распределенном кластере.
При программировании на Go (родной язык для CockroachDB) взаимодействие выглядит стандартно, но с учетом retry-логики для транзакций из-за возможных конфликтов.
import (
"database/sql"
"fmt"
_ "github.com/lib/pq"
)
func transferMoney(db *sql.DB, from, to string, amount float64) error {
tx, err := db.Begin()
if err != nil { return err }
// Рекомендуется использовать конструкцию WHERE ... = $1 для безопасности
_, err = tx.Exec("UPDATE accounts SET balance = balance - $1 WHERE id = $2", amount, from)
if err != nil {
tx.Rollback()
return err
}
_, err = tx.Exec("UPDATE accounts SET balance = balance + $1 WHERE id = $2", amount, to)
if err != nil {
tx.Rollback()
return err
}
return tx.Commit()
}
Важно помнить, что в высоконагруженных сценариях CockroachDB рекомендует использовать подготовленные выражения и, возможно, паттерн «транзакции с перезапуском при ошибке сериализации».
В заключение, CockroachDB предлагает мощную абстракцию «просто SQL» над сложным распределенным кластером. Ее сила — в автоматическом управлении данными, репликацией и отказоустойчивостью. Однако за это приходится платить: операции, затрагивающие несколько шардов, могут иметь большую latency, чем в локальной БД, а требования к ресурсам (CPU, память, диск) для каждого узла значительны. Правильное проектирование схемы (выбор первичного ключа, индексы, географическое размещение) критически важно для производительности.
Разбор CockroachDB с примерами кода: от основ до практического использования
Практическое руководство по CockroachDB с примерами SQL и Go кода. Статья объясняет ключевые концепции распределенной SQL БД, демонстрирует создание таблиц, выполнение распределенных транзакций, настройку размещения данных и работу с changefeeds. Рассмотрены как сильные стороны, так и компромиссы при использовании CockroachDB.
137
2
Комментарии (7)