NoSQL и поисковые движки в Go
Зачем знать: Не все данные подходят под реляционную модель. Документы, time-series, full-text search, analytics — для каждого свой инструмент. На уровне Middle 2 ты должен знать, когда выбрать MongoDB вместо Postgres, чем Cassandra отличается от ScyllaDB, для чего нужен ClickHouse и Elasticsearch, и иметь представление о российской экосистеме: Tarantool, YDB. Это знание определяет, придёшь ты в команду с правильным выбором БД или будешь воевать с неподходящим инструментом.
Содержание
Заголовок раздела «Содержание»- Когда NoSQL вместо SQL
- MongoDB
- Cassandra и ScyllaDB
- ClickHouse
- Elasticsearch / OpenSearch
- Tarantool
- YDB (Yandex Database)
- Gotchas
- Production-практики
- Вопросы для собеседования
- Practice
- Источники
1. Когда NoSQL вместо SQL
Заголовок раздела «1. Когда NoSQL вместо SQL»1.1 Главные причины выбора NoSQL
Заголовок раздела «1.1 Главные причины выбора NoSQL»- Schema flexibility — данные имеют разную структуру, schema эволюционирует часто.
- Scale-out — горизонтальное масштабирование через sharding из коробки.
- High throughput — миллионы writes/sec.
- Specialized workloads — analytics, search, time-series.
- Low latency reads — key-value, in-memory.
1.2 Когда не надо NoSQL
Заголовок раздела «1.2 Когда не надо NoSQL»- Бизнес-логика с транзакциями (банк, ERP) — Postgres.
- ACID требуется — Postgres.
- Сложные ad-hoc queries и JOIN’ы — Postgres.
- Маленький проект (≤10M записей) — Postgres хватит.
- “Хочу хайпово” — Postgres.
Postgres 2026 поддерживает почти всё: JSONB (документы), pgvector (вектора), TimescaleDB extension (time-series), Citus (sharding), FTS (поиск). Часто это лучше отдельной БД.
1.3 Decision matrix
Заголовок раздела «1.3 Decision matrix»| Use case | Лучший инструмент |
|---|---|
| Гибкая схема, документы | MongoDB |
| Write-heavy, time-series, IoT | Cassandra / ScyllaDB |
| Analytics OLAP, observability backend | ClickHouse |
| Full-text search, log search | Elasticsearch / OpenSearch |
| In-memory + scripting | Tarantool / Redis |
| Russian cloud, distributed SQL | YDB |
| Vector search (AI/RAG) | pgvector / Weaviate / Qdrant |
| Graph data | Neo4j / Dgraph |
| Cache | Redis / Memcached / Tarantool |
| Geo | PostGIS / MongoDB geo / Elasticsearch geo |
2. MongoDB
Заголовок раздела «2. MongoDB»2.1 Концепция
Заголовок раздела «2.1 Концепция»Document-oriented: данные хранятся как JSON-подобные BSON документы.
{ "_id": ObjectId("..."), "username": "alice", "email": "alice@example.com", "preferences": { "theme": "dark", "language": "ru" }, "tags": ["admin", "power-user"], "lastLogin": ISODate("2026-05-21T10:30:00Z")}- Database → Collection (≈ table) → Document (≈ row).
- No schema enforced (но можно с
validator). - Sharding и replica sets — native.
2.2 mongo-go-driver
Заголовок раздела «2.2 mongo-go-driver»Официальный драйвер: go.mongodb.org/mongo-driver/v2.
import ( "context" "go.mongodb.org/mongo-driver/v2/mongo" "go.mongodb.org/mongo-driver/v2/mongo/options" "go.mongodb.org/mongo-driver/v2/bson")
ctx := context.Background()client, err := mongo.Connect(options.Client().ApplyURI("mongodb://localhost:27017"))if err != nil { log.Fatal(err)}defer client.Disconnect(ctx)
coll := client.Database("shop").Collection("users")
// Inserttype User struct { ID bson.ObjectID `bson:"_id,omitempty"` Name string `bson:"name"` Email string `bson:"email"` Tags []string `bson:"tags"`}
u := User{Name: "Alice", Email: "alice@x.com", Tags: []string{"admin"}}res, err := coll.InsertOne(ctx, u)
// Find by filterfilter := bson.M{"tags": "admin"}cursor, err := coll.Find(ctx, filter)defer cursor.Close(ctx)
var users []Userif err := cursor.All(ctx, &users); err != nil { log.Fatal(err)}
// Update_, err = coll.UpdateOne(ctx, bson.M{"_id": id}, bson.M{"$set": bson.M{"tags": []string{"admin", "vip"}}},)bson.M vs bson.D:
bson.M— map (unordered).bson.D— ordered slice. Нужно, когда порядок важен (aggregation pipeline, sort).
2.3 Aggregation Pipeline
Заголовок раздела «2.3 Aggregation Pipeline»Mongo-specific query language:
pipeline := bson.A{ bson.D{{"$match", bson.M{"status": "active"}}}, bson.D{{"$group", bson.M{ "_id": "$country", "total": bson.M{"$sum": 1}, }}}, bson.D{{"$sort", bson.M{"total": -1}}}, bson.D{{"$limit", 10}},}
cursor, err := coll.Aggregate(ctx, pipeline)Похоже на SQL:
SELECT country, COUNT(*) AS totalFROM users WHERE status = 'active'GROUP BY country ORDER BY total DESC LIMIT 10;2.4 Индексы
Заголовок раздела «2.4 Индексы»import "go.mongodb.org/mongo-driver/v2/mongo"
// Single fieldcoll.Indexes().CreateOne(ctx, mongo.IndexModel{ Keys: bson.D{{"email", 1}}, Options: options.Index().SetUnique(true),})
// Compoundcoll.Indexes().CreateOne(ctx, mongo.IndexModel{ Keys: bson.D{{"country", 1}, {"createdAt", -1}},})
// Textcoll.Indexes().CreateOne(ctx, mongo.IndexModel{ Keys: bson.D{{"name", "text"}, {"description", "text"}},})
// Geo (2dsphere)coll.Indexes().CreateOne(ctx, mongo.IndexModel{ Keys: bson.D{{"location", "2dsphere"}},})
// Partialcoll.Indexes().CreateOne(ctx, mongo.IndexModel{ Keys: bson.D{{"email", 1}}, Options: options.Index().SetPartialFilterExpression(bson.M{"verified": true}),})
// TTLcoll.Indexes().CreateOne(ctx, mongo.IndexModel{ Keys: bson.D{{"createdAt", 1}}, Options: options.Index().SetExpireAfterSeconds(86400), // 1 day})2.5 Sharding
Заголовок раздела «2.5 Sharding»Sharding key — выбирается per-collection. Документы распределяются по шардам по key.
Виды:
- Range sharding — диапазоны (lastName A-D, E-K, L-R, S-Z).
- Hashed sharding — хеш от key. Равномерное распределение.
- Zone sharding — данные привязаны к локациям (например, EU users в EU shards).
⚠️ Sharding key выбирается раз и навсегда. Поменять — болезненно (export/import).
2.6 Replica Set
Заголовок раздела «2.6 Replica Set»Стандартный setup: 3 узла (1 primary + 2 secondary).
Primary ←─ writes │ ├─── replication ───► Secondary 1 └─── replication ───► Secondary 2Read preferences:
primary(default) — strong consistency.secondary— eventual.nearest— ближайший.
В Go:
opts := options.Client().ApplyURI("mongodb://r1,r2,r3/?replicaSet=rs0"). SetReadPreference(readpref.SecondaryPreferred())2.7 Транзакции
Заголовок раздела «2.7 Транзакции»MongoDB 4.0+ поддерживает multi-document транзакции.
sess, err := client.StartSession()defer sess.EndSession(ctx)
err = mongo.WithSession(ctx, sess, func(ctx mongo.SessionContext) error { if err := sess.StartTransaction(); err != nil { return err }
if _, err := coll1.InsertOne(ctx, doc1); err != nil { sess.AbortTransaction(ctx) return err } if _, err := coll2.UpdateOne(ctx, filter, update); err != nil { sess.AbortTransaction(ctx) return err }
return sess.CommitTransaction(ctx)})⚠️ Транзакции в Mongo — медленные (overhead replica set coordination). Используй только когда реально нужно. Часто можно перепроектировать (denormalize в один документ).
2.8 MongoDB use cases
Заголовок раздела «2.8 MongoDB use cases»✓ Каталог товаров — каждый product имеет разные attributes. ✓ Профили пользователей — гибкая структура. ✓ CMS — статьи с разным набором полей. ✓ Logs (если не Elastic) — JSON-ish структура. ✗ Финансовые транзакции (medium fit; лучше Postgres). ✗ Heavy analytics (использовать ClickHouse).
3. Cassandra и ScyllaDB
Заголовок раздела «3. Cassandra и ScyllaDB»3.1 Концепция
Заголовок раздела «3.1 Концепция»Wide-column store (Bigtable-style). Данные хранятся в “wide rows” с миллионами колонок.
Cassandra — open source (Facebook → Apache). ScyllaDB — rewrite на C++, drop-in replacement, в разы быстрее.
Особенности:
- No master: peer-to-peer (кольцо узлов).
- Tunable consistency на уровне запроса.
- High write throughput (LSM-tree).
- Linear scaling — добавил узлы → пропорционально больше throughput.
3.2 Архитектура: кольцо
Заголовок раздела «3.2 Архитектура: кольцо» Node 1 / \ Node 4 Node 2 \ / Node 3Каждый узел отвечает за диапазон hash значений (token range). Данные реплицируются на N узлов (replication factor).
3.3 Consistency
Заголовок раздела «3.3 Consistency»Уровни:
ONE— записать на 1 replica, ответить.QUORUM— большинство ((RF/2) + 1).LOCAL_QUORUM— большинство в локальном DC.ALL— все replicas.
Strong consistency при чтении: R + W > RF.
- RF=3, W=QUORUM=2, R=QUORUM=2 → strong.
- RF=3, W=1, R=1 → eventual (быстро, но stale).
3.4 gocql driver
Заголовок раздела «3.4 gocql driver»import "github.com/gocql/gocql"
cluster := gocql.NewCluster("node1", "node2", "node3")cluster.Keyspace = "myks"cluster.Consistency = gocql.Quorumcluster.ProtoVersion = 4
session, err := cluster.CreateSession()defer session.Close()
// Inserterr := session.Query( `INSERT INTO users (id, name, email) VALUES (?, ?, ?)`, gocql.TimeUUID(), "Alice", "alice@x.com",).Exec()
// Selectvar name stringerr := session.Query( `SELECT name FROM users WHERE id = ?`, id,).Scan(&name)
// Iterateiter := session.Query(`SELECT id, name FROM users WHERE country = ?`, "KZ").Iter()defer iter.Close()
var id gocql.UUIDvar n stringfor iter.Scan(&id, &n) { log.Println(id, n)}3.5 CQL: Cassandra Query Language
Заголовок раздела «3.5 CQL: Cassandra Query Language»Похож на SQL, но с ограничениями:
- Нет JOIN.
- Нет subquery.
- WHERE только по primary key (или indexed columns с warnings).
CREATE KEYSPACE myks WITH REPLICATION = { 'class': 'NetworkTopologyStrategy', 'dc1': 3};
CREATE TABLE users ( country TEXT, user_id UUID, name TEXT, email TEXT, created_at TIMESTAMP, PRIMARY KEY ((country), user_id));
CREATE TABLE user_events ( user_id UUID, event_time TIMESTAMP, event_type TEXT, data TEXT, PRIMARY KEY ((user_id), event_time)) WITH CLUSTERING ORDER BY (event_time DESC);3.6 Primary Key: partition key + clustering columns
Заголовок раздела «3.6 Primary Key: partition key + clustering columns»PRIMARY KEY ((partition_key), clustering_col1, clustering_col2)- Partition key — определяет, в какой shard едут данные.
- Clustering columns — порядок внутри partition.
Запросы должны всегда включать partition key (иначе ALLOW FILTERING — медленно и опасно).
3.7 Data modeling: query-driven
Заголовок раздела «3.7 Data modeling: query-driven»В отличие от RDBMS, в Cassandra схема дизайнится под query. Денормализация — норма.
Пример: для запроса “все orders пользователя по дате” и “все orders в стране” — нужны две таблицы:
CREATE TABLE orders_by_user ( user_id UUID, order_id UUID, created_at TIMESTAMP, total DECIMAL, PRIMARY KEY ((user_id), created_at, order_id));
CREATE TABLE orders_by_country ( country TEXT, order_date DATE, order_id UUID, user_id UUID, total DECIMAL, PRIMARY KEY ((country, order_date), order_id));Дублирование данных = ОК.
3.8 Tombstones
Заголовок раздела «3.8 Tombstones»DELETE в Cassandra = записать tombstone (маркер удаления). Реальное удаление — при compaction.
⚠️ Проблема tombstones: если много DELETE → много tombstones → медленные SELECT (нужно их фильтровать). Особенно в очередях/queues.
Антипаттерн: использовать Cassandra как очередь.
3.9 ScyllaDB vs Cassandra
Заголовок раздела «3.9 ScyllaDB vs Cassandra»| Аспект | Cassandra | ScyllaDB |
|---|---|---|
| Язык | Java | C++ |
| Throughput | Базовый | 5-10x выше |
| Latency | Базовый | Стабильнее (нет GC pauses) |
| Совместимость | — | API compatible (CQL, drivers) |
| Resource usage | Высокий (JVM) | Низкий |
ScyllaDB — drop-in замена. Если у тебя есть Cassandra — переход относительно прямолинейный.
3.10 Cassandra use cases
Заголовок раздела «3.10 Cassandra use cases»✓ Time-series (метрики, логи) — IoT, monitoring. ✓ Write-heavy workloads — events, audit logs. ✓ Global apps с DC-aware replication. ✓ Big data (петабайты). ✗ Сложные queries — нет JOIN, GROUP BY. ✗ Очередь / queue. ✗ Финансовая ACID-логика.
4. ClickHouse
Заголовок раздела «4. ClickHouse»4.1 Концепция
Заголовок раздела «4.1 Концепция»Column-oriented analytical OLAP database. Создан Яндексом, open source.
- Данные хранятся по колонкам (а не по строкам как в OLTP).
- Compression — отличный (10-100x).
- Aggregations — очень быстрые (миллиарды строк за секунды).
- Vector engine (SIMD).
- Не для OLTP: одиночные INSERT медленные, нет ACID транзакций, плохо с UPDATE/DELETE.
4.2 Архитектура
Заголовок раздела «4.2 Архитектура»MergeTree — главный engine family.
- Данные пишутся в parts (отдельные файлы).
- Background — merge parts (а-ля LSM).
- Каждая колонка — отдельный файл (column-oriented).
CREATE TABLE events ( ts DateTime, user_id UInt64, event_type LowCardinality(String), properties String, revenue Decimal(10, 2)) ENGINE = MergeTree()PARTITION BY toYYYYMM(ts)ORDER BY (user_id, ts);PARTITION BY — обычно по дате (месяц/день). Старые partitions можно DROP PARTITION.
ORDER BY — primary key (data sorted on disk). Влияет на запросы (диапазоны быстры по prefix).
4.3 Go drivers
Заголовок раздела «4.3 Go drivers»Два варианта:
- github.com/ClickHouse/clickhouse-go (v2) — официальный, native protocol.
- github.com/mailru/go-clickhouse — HTTP, проще.
import "github.com/ClickHouse/clickhouse-go/v2"
conn, err := clickhouse.Open(&clickhouse.Options{ Addr: []string{"localhost:9000"}, Auth: clickhouse.Auth{Database: "default"},})
// Batch insert (NEVER одиночные INSERT!)batch, err := conn.PrepareBatch(ctx, "INSERT INTO events")for _, e := range events { batch.Append(e.Ts, e.UserID, e.EventType, e.Props, e.Revenue)}batch.Send()
// Queryrows, _ := conn.Query(ctx, ` SELECT toStartOfHour(ts) AS hour, count() AS n FROM events WHERE ts >= now() - INTERVAL 1 DAY GROUP BY hour ORDER BY hour`)4.4 INSERT batching
Заголовок раздела «4.4 INSERT batching»ClickHouse оптимизирован для больших батчей (10K-1M rows per insert).
⚠️ Антипаттерн: вставлять по 1 row в INSERT. Каждый INSERT создаёт parts → merge не справляется.
Решение: накапливать batch’ами (Kafka → consumer → batched insert каждые 10 сек или 100K rows).
4.5 Materialized Views
Заголовок раздела «4.5 Materialized Views»Pre-computed aggregations. Обновляются на каждый INSERT (incremental).
CREATE MATERIALIZED VIEW events_dailyENGINE = SummingMergeTree()PARTITION BY toYYYYMM(date)ORDER BY (date, event_type)ASSELECT toDate(ts) AS date, event_type, count() AS events_count, sum(revenue) AS total_revenueFROM eventsGROUP BY date, event_type;Запросы к events_daily — мгновенные.
4.6 Distributed tables
Заголовок раздела «4.6 Distributed tables»Sharding через несколько ClickHouse узлов:
-- На каждом узле — local tableCREATE TABLE events_local ON CLUSTER mycluster ( ...) ENGINE = MergeTree() ...;
-- Distributed table — view над local'амиCREATE TABLE events ON CLUSTER mycluster AS events_localENGINE = Distributed(mycluster, default, events_local, rand());INSERT в events — распределяется по узлам. SELECT — выполняется на всех и aggregates.
4.7 ClickHouse use cases
Заголовок раздела «4.7 ClickHouse use cases»✓ Analytics (BI, dashboards). ✓ Observability backend (metrics, traces, logs) — see Quickwit, SigNoz. ✓ Time-series. ✓ Real-time analytics. ✗ OLTP (не для transactional). ✗ Frequent updates.
В России — стандартный выбор для аналитики (Яндекс, VK, Tinkoff).
5. Elasticsearch / OpenSearch
Заголовок раздела «5. Elasticsearch / OpenSearch»5.1 Концепция
Заголовок раздела «5.1 Концепция»Full-text search engine на основе Lucene. Хранит документы, индексирует по терминам, ищет по relevance.
OpenSearch — fork от Amazon после ES license change. API-совместим.
5.2 Архитектура
Заголовок раздела «5.2 Архитектура»- Index ≈ table (collection of documents).
- Document — JSON.
- Shard — горизонтальное partitioning (primary + replicas).
- Node — узел кластера.
- Mapping — schema (типы полей, analyzers).
5.3 Mapping
Заголовок раздела «5.3 Mapping»PUT /products{ "mappings": { "properties": { "name": { "type": "text", "analyzer": "russian" }, "description": { "type": "text" }, "price": { "type": "double" }, "tags": { "type": "keyword" }, "created_at": { "type": "date" } } }}text— full-text indexed.keyword— exact match (для фильтров, sort, agg).date,integer,double— типизированы.
5.4 Go драйверы
Заголовок раздела «5.4 Go драйверы»import "github.com/elastic/go-elasticsearch/v8"
es, err := elasticsearch.NewClient(elasticsearch.Config{ Addresses: []string{"http://localhost:9200"},})
// Index documentdoc := map[string]interface{}{ "name": "iPhone 15", "price": 999, "tags": []string{"electronics", "phone"},}body, _ := json.Marshal(doc)res, err := es.Index( "products", bytes.NewReader(body), es.Index.WithDocumentID("1"),)
// Searchquery := `{ "query": { "bool": { "must": [ {"match": {"name": "iPhone"}}, {"range": {"price": {"lte": 1500}}} ] } }}`res, err = es.Search( es.Search.WithIndex("products"), es.Search.WithBody(strings.NewReader(query)),)Альтернатива: olivere/elastic (typed API, более популярная).
5.5 Query DSL
Заголовок раздела «5.5 Query DSL»{ "query": { "bool": { "must": [ {"match": {"description": "smartphone"}}, {"range": {"price": {"gte": 100, "lte": 1000}}} ], "filter": [ {"term": {"category": "electronics"}} ], "should": [ {"match": {"tags": "new"}} ] } }, "sort": [ {"price": "asc"} ], "from": 0, "size": 20}must— обязательное условие (влияет на score).filter— фильтр (без score, кэшируется).should— optional, boost score.
5.6 Aggregations
Заголовок раздела «5.6 Aggregations»{ "aggs": { "by_category": { "terms": {"field": "category", "size": 10}, "aggs": { "avg_price": {"avg": {"field": "price"}} } } }}5.7 Полнотекстовый поиск + русский
Заголовок раздела «5.7 Полнотекстовый поиск + русский»{ "settings": { "analysis": { "analyzer": { "russian_analyzer": { "tokenizer": "standard", "filter": ["lowercase", "russian_morphology", "russian_stop"] } } } }, "mappings": { "properties": { "title": {"type": "text", "analyzer": "russian_analyzer"} } }}Plugin analysis-morphology для русской морфологии.
5.8 Elasticsearch use cases
Заголовок раздела «5.8 Elasticsearch use cases»✓ Site search (e-commerce, content). ✓ Logs (ELK / Elastic stack). ✓ APM / observability. ✓ Geo search. ✓ Vector search (с 8.x). ✗ Не для primary storage (нет ACID, всё в JSON).
5.9 Trade-offs
Заголовок раздела «5.9 Trade-offs»- Кушает RAM (off-heap для Lucene + heap для JVM).
- Тяжёлые updates (re-index segment).
- Не CP — eventual consistency.
- Сложные ops (cluster recovery, shard balancing).
6. Tarantool
Заголовок раздела «6. Tarantool»6.1 Концепция
Заголовок раздела «6.1 Концепция»In-memory DB + application server + queue + cache в одном. Российская разработка (Mail.ru/VK).
- Lua-based scripting (можно писать функции и хранимые процедуры в Lua).
- Async network I/O (fiber-based).
- Replication, sharding (vshard).
- Disk-based mode для durability.
6.2 Spaces
Заголовок раздела «6.2 Spaces»Tarantool аналог table = space.
box.schema.space.create('users')box.space.users:format({ {name='id', type='unsigned'}, {name='name', type='string'}, {name='email', type='string'},})box.space.users:create_index('primary', {parts={'id'}})box.space.users:create_index('email', {parts={'email'}, unique=true})
-- Insertbox.space.users:insert{1, 'Alice', 'alice@x.com'}
-- Selectlocal u = box.space.users:get(1)6.3 Go драйвер
Заголовок раздела «6.3 Go драйвер»import "github.com/tarantool/go-tarantool/v2"
conn, err := tarantool.Connect("localhost:3301", tarantool.Opts{ User: "guest",})
// Insert_, err = conn.Insert("users", []interface{}{1, "Alice", "alice@x.com"})
// Selectresp, err := conn.Select("users", "primary", 0, 1, tarantool.IterEq, []interface{}{1})
// Call stored procedureresp, err := conn.Call("myproc", []interface{}{"arg1", 42})6.4 Tarantool Cartridge / TDG
Заголовок раздела «6.4 Tarantool Cartridge / TDG»Cartridge — framework для кластера. TDG (Tarantool Data Grid) — enterprise решение для микросервисов.
6.5 Use cases (в РФ 2026)
Заголовок раздела «6.5 Use cases (в РФ 2026)»✓ Кэш + storage (вместо Redis + Postgres).
✓ Сессии.
✓ Высоконагруженные сервисы в финтехе (Tinkoff, VK, Mail.ru, Sber).
✓ Queue (через queue модуль).
✗ Сложные queries.
✗ Big data analytics.
7. YDB (Yandex Database)
Заголовок раздела «7. YDB (Yandex Database)»7.1 Концепция
Заголовок раздела «7.1 Концепция»Distributed SQL database от Yandex. Open source.
- Сильная consistency (Multi-Raft consensus).
- Distributed transactions across shards (Calvin-like).
- SQL (YQL — variant), API близок к Postgres.
- Используется в Yandex Cloud, Yandex Search, Yandex.Metrica.
7.2 Архитектура
Заголовок раздела «7.2 Архитектура»- Tablet-based (single-shard primitives).
- Distributed coordinator.
- Auto-sharding по primary key.
- Replication factor 3.
7.3 YQL
Заголовок раздела «7.3 YQL»CREATE TABLE users ( id Uint64, email Utf8, name Utf8, PRIMARY KEY (id));
UPSERT INTO users (id, email, name) VALUES (1, 'alice@x.com', 'Alice'), (2, 'bob@x.com', 'Bob');
SELECT * FROM users WHERE id BETWEEN 1 AND 100;7.4 Go SDK
Заголовок раздела «7.4 Go SDK»import "github.com/ydb-platform/ydb-go-sdk/v3"
db, err := ydb.Open(ctx, "grpcs://endpoint/?database=/ru-central1/db", ydb.WithAccessTokenCredentials(token))defer db.Close(ctx)
err = db.Table().Do(ctx, func(ctx context.Context, s table.Session) error { _, _, err := s.Execute(ctx, table.DefaultTxControl(), "SELECT * FROM users WHERE id = $id", table.NewQueryParameters( table.ValueParam("$id", types.Uint64Value(1)), ), ) return err})7.5 Use cases
Заголовок раздела «7.5 Use cases»✓ Yandex Cloud managed services. ✓ High-scale OLTP в РФ. ✓ Когда нужен distributed SQL с strong consistency.
В 2026 — конкурирует с CockroachDB и Spanner.
8. Gotchas
Заголовок раздела «8. Gotchas»8.1 ⚠️ MongoDB: BSON vs JSON
Заголовок раздела «8.1 ⚠️ MongoDB: BSON vs JSON»BSON shop поддерживает больше типов (ObjectID, Decimal128, Date). При marshalling/unmarshalling в Go — использовать bson tag, а не json.
type User struct { ID bson.ObjectID `bson:"_id,omitempty" json:"id"` Email string `bson:"email" json:"email"`}8.2 ⚠️ MongoDB: Sharding key — без шанса изменить
Заголовок раздела «8.2 ⚠️ MongoDB: Sharding key — без шанса изменить»Выбран неправильно — миграция через export/import. Поэтому 10x подумать.
8.3 ⚠️ Cassandra: query-driven schema
Заголовок раздела «8.3 ⚠️ Cassandra: query-driven schema»Если ты дизайнишь как RDBMS — нормализуешь — будет тормозить. Денормализация + одна таблица на запрос.
8.4 ⚠️ Cassandra: ALLOW FILTERING — анти-паттерн
Заголовок раздела «8.4 ⚠️ Cassandra: ALLOW FILTERING — анти-паттерн»Если запрос без partition key — Cassandra ругается. Можно ALLOW FILTERING — но это значит full scan. Никогда в production.
8.5 ⚠️ Cassandra: tombstones и очереди
Заголовок раздела «8.5 ⚠️ Cassandra: tombstones и очереди»Используешь Cassandra как очередь? DELETE → tombstones → читать всё медленнее. Не делай.
8.6 ⚠️ ClickHouse: одиночные INSERT
Заголовок раздела «8.6 ⚠️ ClickHouse: одиночные INSERT»INSERT INTO ... VALUES (...) по 1 row — убивает performance. Батчи 10K+. Использовать buffer engines или kafka engine.
8.7 ⚠️ ClickHouse: UPDATE/DELETE
Заголовок раздела «8.7 ⚠️ ClickHouse: UPDATE/DELETE»ALTER TABLE ... DELETE WHERE ... — медленный (mutation). Использовать только в редких случаях. Дизайн без updates.
8.8 ⚠️ Elasticsearch: re-index при mapping change
Заголовок раздела «8.8 ⚠️ Elasticsearch: re-index при mapping change»Изменить mapping существующего поля — почти нельзя. Создать новый index → reindex → swap alias.
8.9 ⚠️ Elasticsearch: heap size
Заголовок раздела «8.9 ⚠️ Elasticsearch: heap size»JVM heap не > 32 GB (compressed OOPs). Остальное — для off-heap (Lucene). Memory tuning — сложная тема.
8.10 ⚠️ Elasticsearch: split brain
Заголовок раздела «8.10 ⚠️ Elasticsearch: split brain»При network partition — несколько nodes могут думать, что они master. Quorum-based master election (discovery.zen.minimum_master_nodes — устарело, теперь автоматически).
8.11 ⚠️ MongoDB: $where с JavaScript — медленно
Заголовок раздела «8.11 ⚠️ MongoDB: $where с JavaScript — медленно»Avoid $where queries — выполняются JS engine. Лучше — aggregation pipeline.
8.12 ⚠️ Tarantool: всё в RAM
Заголовок раздела «8.12 ⚠️ Tarantool: всё в RAM»Если данных больше RAM — vinyl engine (LSM, на диске). Memtx (in-memory) — fastest, но capacity limited.
8.13 ⚠️ Tarantool: Lua — не Go
Заголовок раздела «8.13 ⚠️ Tarantool: Lua — не Go»Stored procedures на Lua. Команда должна это знать. Иначе — лимит логики на клиенте.
8.14 ⚠️ YDB: ecosystem меньше
Заголовок раздела «8.14 ⚠️ YDB: ecosystem меньше»Документация мейнли русская. Сообщество меньше, чем у Postgres. Tooling (мониторинг, миграции) — exists but newer.
8.15 ⚠️ Все NoSQL: нет JOIN
Заголовок раздела «8.15 ⚠️ Все NoSQL: нет JOIN»В большинстве случаев — приложение должно делать “JOIN” в коде. Денормализация — стандартный подход.
8.16 ⚠️ Eventual consistency — UX challenge
Заголовок раздела «8.16 ⚠️ Eventual consistency — UX challenge»После INSERT не сразу видно в reads с другого реплики. UI должен это учитывать (optimistic update, retry).
8.17 ⚠️ Hot partitions
Заголовок раздела «8.17 ⚠️ Hot partitions»Если sharding key плохой → один shard перегружен.
Пример Cassandra:
PARTITION KEY ((day)) -- сегодняшний день = все на одном узле!Решение: composite partition key — (day, hour) или hash bucketing.
8.18 ⚠️ Замена БД в running системе
Заголовок раздела «8.18 ⚠️ Замена БД в running системе»Очень болезненно. Лучше выбрать правильно с начала.
Иногда возможно через dual-write phase:
- Сейчас все читают из Postgres.
- Начинаем писать в Postgres + Mongo.
- Backfill старых данных.
- Постепенно переключаем reads на Mongo.
- Останавливаем write в Postgres.
9. Production-практики
Заголовок раздела «9. Production-практики»9.1 Backups
Заголовок раздела «9.1 Backups»- MongoDB:
mongodump/ Ops Manager. Continuous backup через oplog tailing. - Cassandra:
nodetool snapshot+ filesystem copy. Опять snapshots + incremental. - ClickHouse:
BACKUP TABLE(с 22.4+) или filesystem snapshots. - Elasticsearch: snapshots в S3 / GCS через
snapshot API. - Tarantool: snapshots (.snap files) + xlogs.
9.2 Monitoring
Заголовок раздела «9.2 Monitoring»| БД | Tool |
|---|---|
| MongoDB | mongo-exporter + Prometheus, Atlas (managed) |
| Cassandra | jmx_exporter + Prometheus, Datastax tools |
| ClickHouse | clickhouse_exporter, system.metrics |
| Elasticsearch | elasticsearch_exporter, X-Pack |
| Tarantool | metrics module |
9.3 Capacity planning
Заголовок раздела «9.3 Capacity planning»Cassandra: RF=3, 70% disk usage — пора добавлять узлы.
ClickHouse: партиция > 1 TB — пора шардировать.
MongoDB: working set должен помещаться в RAM (для hot data).
Elasticsearch: 20-40 GB на shard. Heap = 50% RAM, max 32 GB.
9.4 Multi-region
Заголовок раздела «9.4 Multi-region»- Cassandra: NetworkTopologyStrategy с DC-aware. LOCAL_QUORUM для writes.
- MongoDB: Replica set across DCs + read preferences. Atlas — managed cross-region.
- ClickHouse: replicated tables + ZooKeeper.
- YDB: cross-region — natively (если регионы поддерживают).
9.5 Securing NoSQL
Заголовок раздела «9.5 Securing NoSQL»- TLS in transit (ВСЕГДА в production).
- Authentication: SCRAM, x509, LDAP/Kerberos integration.
- Authorization: roles, fine-grained permissions.
- Encryption at rest (FDE, или встроенный TDE в Enterprise).
- Network: VPC, IP whitelist, firewall.
- Audit log.
⚠️ Известный антипаттерн: MongoDB/Elastic без auth на публичном IP → ransomware. Регулярно появляется в новостях.
9.6 Choosing between alternatives
Заголовок раздела «9.6 Choosing between alternatives»| Pair | When A | When B |
|---|---|---|
| MongoDB vs Postgres JSONB | Heavy denormalized docs, high write QPS | Нужен ACID, joins, mixed workload |
| Cassandra vs ScyllaDB | Существующий ecosystem, дешевле hosting | Latency critical, throughput |
| Elasticsearch vs OpenSearch | Enterprise features, ML | Open source без vendor lock-in (Amazon) |
| ClickHouse vs Druid/Pinot | Realtime + ad-hoc query | OLAP-only realtime |
| Tarantool vs Redis | Стейтфул логика + persistence | Pure cache |
| YDB vs CockroachDB | Yandex Cloud | Global ecosystem, AWS/GCP |
9.7 Migration strategy (Postgres → NoSQL)
Заголовок раздела «9.7 Migration strategy (Postgres → NoSQL)»- Identify use case fit (use right tool).
- Dual-write phase: писать в обе.
- Backfill historic data.
- Verify consistency.
- Switch reads gradually (canary).
- Switch writes.
- Decommission old.
Не делать “big bang” миграцию.
9.8 Cost optimization
Заголовок раздела «9.8 Cost optimization»- Cold storage tiering: старые данные в S3 / archive.
- Compression: ZSTD в ClickHouse — экономия места.
- TTL: автоматическое удаление (MongoDB TTL index, Cassandra TTL).
- Right-sizing: managed services часто overprovision.
9.9 Connection pooling NoSQL
Заголовок раздела «9.9 Connection pooling NoSQL»В Go все драйверы имеют встроенный пул:
mongo-go-driver—maxPoolSizeв URI.gocql—NumConns.clickhouse-go—MaxOpenConns.elastic— http client pool.
Не создавать клиента на запрос! Создавать раз и переиспользовать.
9.10 Schema migrations NoSQL
Заголовок раздела «9.10 Schema migrations NoSQL»MongoDB: documents эволюционируют в коде. Версионирование через schemaVersion field. При load: если старая версия → upgrade in-place.
Cassandra: ALTER TABLE ADD COLUMN. Не удалять колонки! (старые tombstones).
Elasticsearch: re-index с alias swap.
ClickHouse: ALTER TABLE + mutations (slow).
10. Вопросы для собеседования
Заголовок раздела «10. Вопросы для собеседования»-
Когда NoSQL лучше SQL? Schema flexibility, scale-out требуется, specific workloads (analytics, search, time-series). НЕ для transactional CRUD с ACID.
-
Чем MongoDB отличается от Postgres JSONB? MongoDB: native document store, sharding. Postgres JSONB: ACID, joins возможны, mixed workload. Postgres JSONB достаточен для большинства случаев.
-
Что такое sharding key в MongoDB? Поле(я), по которому документы распределяются по shards. Выбор раз и навсегда. Bad choice = hot shard / scatter-gather queries.
-
Какие индексы в MongoDB? Single field, compound, multikey (для arrays), text, geo (2d, 2dsphere), partial, TTL.
-
Чем bson.M отличается от bson.D в Go? bson.M — unordered map. bson.D — ordered slice (нужно когда порядок важен: aggregation pipeline, sort).
-
Что такое replica set в MongoDB? Кластер из primary + secondaries. Writes → primary, reads → primary или secondaries (с read preference). Auto failover.
-
Что такое Cassandra и где её использовать? Wide-column distributed DB. Write-heavy workloads, time-series, IoT, global apps. Linear scaling.
-
Чем ScyllaDB отличается от Cassandra? Rewrite на C++ (vs Java). 5-10x faster, lower latency (no GC). Drop-in replacement (CQL compatible).
-
Что такое tunable consistency в Cassandra? Уровни consistency per query: ONE, QUORUM, LOCAL_QUORUM, ALL. R+W > RF → strong.
-
Что такое partition key в Cassandra? Часть primary key, по которой строки распределяются по nodes. Все запросы должны включать partition key.
-
Что такое tombstones и в чём проблема? DELETE = записать tombstone. При большом числе tombstones — SELECT замедляется (нужно фильтровать). Антипаттерн — Cassandra как queue.
-
Что такое ClickHouse и где использовать? Column-oriented analytical OLAP DB. Аналитика, observability, time-series. Не OLTP.
-
Почему ClickHouse быстрый для analytics? Column storage, compression, vector engine (SIMD), parallel execution. Aggregations миллиардов строк за секунды.
-
Что такое MergeTree? Engine family в ClickHouse. Данные в parts, merge в background (LSM-like). PARTITION BY (обычно по дате), ORDER BY (primary key).
-
Почему нельзя одиночные INSERT в ClickHouse? Каждый INSERT — новый part. Merge не успевает → деградация. Батчить (10K+ rows).
-
Что такое Materialized View в ClickHouse? Pre-computed aggregation, обновляется на каждый INSERT. Запросы к ней — мгновенные.
-
Что такое Elasticsearch? Full-text search engine на Lucene. Indices, shards, mappings, query DSL.
-
Чем OpenSearch отличается от Elasticsearch? Fork от Amazon. API-compatible. Open source без vendor lock-in (после ES license change).
-
Какие типы полей в Elasticsearch? text (full-text indexed), keyword (exact match), date, integer, double, geo_point, nested.
-
Какие use cases для Elasticsearch? Site search, logs (ELK), APM, geo search, vector search. Не primary storage.
-
Что такое Tarantool? In-memory DB + app server + queue, российская разработка. Lua scripting, fiber-based. Использует VK, Tinkoff, Mail.ru.
-
Зачем нужен Tarantool, если есть Redis? Tarantool — full DB (с indexes, transactions, durability). Lua stored procedures. Persistent. Redis — больше cache.
-
Что такое YDB? Distributed SQL от Яндекса. Strong consistency, auto-sharding. Yandex Cloud стандарт.
-
Сравните YDB и CockroachDB. Оба — distributed SQL. CockroachDB — PG-compatible, global ecosystem. YDB — Yandex Cloud, российская экосистема.
-
Как выбрать NoSQL под use case? По matrix: документы → MongoDB; write-heavy → Cassandra; analytics → ClickHouse; search → Elasticsearch; in-memory → Tarantool; Russian cloud → YDB.
11. Practice
Заголовок раздела «11. Practice»Задача 1: MongoDB catalog service
Заголовок раздела «Задача 1: MongoDB catalog service»Создать каталог товаров (Mongo). Endpoints: создать товар, search по name/category/price range, aggregation by category. Индексы.
Задача 2: ClickHouse for analytics
Заголовок раздела «Задача 2: ClickHouse for analytics»ETL pipeline: events из Kafka → batched insert в ClickHouse. Materialized view: events_per_hour. Dashboard SQL queries.
Задача 3: Elasticsearch full-text search
Заголовок раздела «Задача 3: Elasticsearch full-text search»Index 1M статей. Реализовать full-text search с русским анализатором. Highlight matching terms. Pagination.
Задача 4: Cassandra time-series
Заголовок раздела «Задача 4: Cassandra time-series»Метрики (server_id, metric_name, ts, value). Partition by (server_id, day), clustering by ts. Запросы: latest N points, по диапазону.
Задача 5: Tarantool session storage
Заголовок раздела «Задача 5: Tarantool session storage»Хранение сессий: key=session_id, value=user_data, TTL=24h. CRUD через Go.
Задача 6: Compare MongoDB transaction vs Postgres
Заголовок раздела «Задача 6: Compare MongoDB transaction vs Postgres»Реализовать money transfer в MongoDB transaction. Сравнить latency с Postgres. Понять trade-offs.
Задача 7: Hot partition simulation
Заголовок раздела «Задача 7: Hot partition simulation»В Cassandra сделать partition key = current_day. Заметить, что все writes идут на один node. Перепроектировать с composite key.
Задача 8: ClickHouse vs Postgres for analytics
Заголовок раздела «Задача 8: ClickHouse vs Postgres for analytics»Залить 100M event’ов. Запустить SELECT COUNT, GROUP BY на обеих. Сравнить.
12. Источники
Заголовок раздела «12. Источники»- MongoDB Documentation — https://www.mongodb.com/docs/ (актуально для 7.0+ в 2026).
- Designing Data-Intensive Applications by Martin Kleppmann — главы 2-3 (data models, storage).
- Cassandra: The Definitive Guide by Jeff Carpenter, Eben Hewitt (3rd edition).
- ScyllaDB Documentation — https://docs.scylladb.com/
- ClickHouse Documentation — https://clickhouse.com/docs (русская версия отличная).
- Elasticsearch: The Definitive Guide + новые версии docs.
- Tarantool Documentation — https://www.tarantool.io/en/doc/latest/ (есть на русском).
- YDB Documentation — https://ydb.tech/docs/ru/
- Habr / Postgres Pro / Yandex blog — реальные production opyt на русском.
- Use The Index, Luke + специфичные NoSQL гайды по индексированию.