# База данных — это память системы

Экспресс-курс по базам данных и хранилищам. 20 понятий — Relational, SQL, Transactions, Document, Key-value, Wide-column, Graph, Time-series, Search, Vector, Object storage, ACID, BASE, CAP, Index, Replication, Sharding, Normalization, Polyglot persistence — у каждого английское название, схема, карточка с примером и ценой. В конце — шпаргалка по всем понятиям на одной странице.

- **Author:** Фёдор Чищин
- **Level:** intro
- **Reading time:** ~35 минут чтения
- **Published:** 2026-06-02
- **Source:** https://fedorthinks.com/ru/learn/databases-express

---

## 1. Зачем нужна база данных

База данных — это долговременная память твоей системы. Код переписывают; данные и форма, которую ты для них выбрал, обычно переживают всё вокруг.

### Данные переживают код

> Дом за десятилетия перекрашивают, переставляют мебель, даже перекрывают крышу — но фундамент, залитый в первый день, остаётся, потому что заменить его значит поднять весь дом.

Языки меняются, фреймворки подменяют, интерфейс перерисовывают десять раз. **Данные и их структура обычно остаются**, потому что менять их на живой системе, полной реальных записей, медленно и рискованно. Поэтому база — первое, о чём надо подумать всерьёз, и последнее, что стоит делать впопыхах.

### База отвечает на два вопроса

> Выбор, где хранить вещи, задаёт два вопроса: какой они формы — книги на полках или странные инструменты на крючках? — и насколько надёжно их надо беречь — сейф или удобный ящик?

Любое хранилище определяется двумя вещами. Первая — **модель данных (data model)**: как оно придаёт данным форму — жёсткие таблицы, гибкие документы, граф связей. Вторая — **гарантии (guarantees)**: строгая корректность или скорость и доступность с более слабыми обещаниями. Почти любой выбор базы — это разный ответ на эти два вопроса, и остальной курс — это меню.

### Access pattern — выбирай по тому, как читаешь и пишешь

> Картотечный шкаф, каталог карточек и конвейер — всё это «хранилище», но выбираешь ты по тому, как будешь доставать вещи, а не по тому, что выглядит новее.

Лучшей базы не бывает. Хранилище, идеальное под быстрые поиски по ключу, плохо для глубоких запросов по связям; построенное под огромные записи плохо для строгих банковских транзакций. Правильный вопрос — твой **паттерн доступа (access pattern)**: что ты читаешь, что пишешь, как часто и как это должно вести себя, когда что-то идёт не так.

### Карта: 20 понятий в шести группах

> Карта метро: сначала видишь линии целиком, потом едешь по станциям.

Понятия курса делятся на шесть групп: реляционная основа, семейство NoSQL, специализированные хранилища, гарантии, приёмы масштабирования и то, как всё это сочетать.

_Diagram: Шесть групп понятий — по ним идут главы 2–7._

У каждого понятия дальше одно и то же: **английское название** (это термин, по которому его ищут в документации), образ из жизни, **схема** и **карточка** с полями: _проблема_, _идея_, _пример_, _цена_, _когда брать_ и _связи_. В конце — шпаргалка на одной странице.

> **Прежде чем выбрать хранилище:** «Какой формы мои данные, как я буду их запрашивать и насколько строгими должны быть гарантии?»

> Код легко поменять. Данные — нет. Выбирай хранилище как решение, с которым проживёшь годы, — потому что так и будет.

## 2. Реляционный дефолт

Для большинства систем дефолт — реляционная база, и этот дефолт она заслуживает. Начинай отсюда и уходи только с причиной.

### Relational Database — таблицы, строки и связи

> Правильно сделанная таблица: каждый лист — один вид сущности (клиенты, заказы), каждая строка — одна запись, и заказы ссылаются назад на клиента, которому принадлежат.

Это понятие **реляционная база данных (relational database)**. Она хранит данные в **таблицах (tables)** с фиксированной **схемой (schema)** и связывает таблицы по ссылке — строка заказа держит id своего клиента. Такая ссылка называется **внешний ключ (foreign key)**. Схема — это обещание о форме: у каждого заказа есть нужные колонки, проверенные на входе. **Структура заранее в обмен на здравомыслие потом.**

_Diagram: Две таблицы: заказ хранит customer_id — внешний ключ, указывающий на клиента._

#### Relational Database (реляционная БД, RDBMS)

- **Проблема:** Данные со структурой и связями, к которым нужно задавать разные вопросы.
- **Идея:** Таблицы с фиксированной схемой; связи между таблицами через внешние ключи.
- **Пример:** Postgres, MySQL: таблицы `customers` и `orders`, у заказа есть `customer_id`.
- **Цена:** Схему надо продумать заранее; менять её на живых данных — работа.
- **Когда брать:** **Почти всегда как стартовая точка.** Уходить — только с причиной.
- **Связи:** Говорит на **SQL**, даёт **Transactions** и **ACID**.

### SQL — проси что хочешь, а не как достать

> Ты говоришь библиотекарю «все романы французских авторов, изданные после 1950-го, по году» — и он сам соображает, как их найти. Ты описываешь результат, а не поиск.

**SQL (Structured Query Language)** — это **декларативный язык запросов (declarative query language)**: ты заявляешь нужный ответ, а база сама строит план, как его достать, включая соединение таблиц (**JOIN**). Этот план строит **планировщик запросов (query planner)**. `SELECT ... JOIN ... WHERE ...` может ответить на вопросы, которых ты не предвидел при проектировании, — одна из тихих суперсил реляционной модели.

_Diagram: Ты пишешь, ЧТО нужно; планировщик базы решает, КАК это достать._

#### SQL (язык запросов)

- **Проблема:** Нужно задавать данным вопросы, не программируя каждый поиск вручную.
- **Идея:** Описываешь результат; база сама выбирает план (индексы, соединения, сортировку).
- **Пример:** `SELECT title FROM books JOIN authors ... WHERE country = 'FR' AND year > 1950 ORDER BY year`.
- **Цена:** Сложные запросы надо уметь читать и проверять, как именно база их выполняет.
- **Когда брать:** Везде, где есть реляционная база, — это её родной язык.
- **Связи:** Язык **Relational Database**; быстрым его делают **индексы (Index)**.

### Transactions — всё или ничего

> Денежный перевод — это две записи: минус здесь, плюс там, — но одно намерение. Либо обе случились, либо ни одна; наполовину сделанный перевод — катастрофа.

Это понятие **транзакция (transaction)**: группа изменений, которая либо целиком **фиксируется (commit)**, либо целиком **откатывается (rollback)**, изолированно от других, и переживает падение, как только подтверждена. Набор этих обещаний называют **ACID** (подробно — в главе 5). Поэтому банки, заказы и склад живут в SQL — **когда корректность важнее голой скорости, тебе нужно именно это обещание.**

_Diagram: Перевод: списание и зачисление — одна транзакция. Либо COMMIT обоих, либо ROLLBACK обоих._

#### Transaction (транзакция)

- **Проблема:** Одно действие — это несколько записей; сбой посередине оставит мусор.
- **Идея:** Обернуть записи в `BEGIN … COMMIT`: либо все сохранились, либо ни одна.
- **Пример:** Перевод: `A − 100` и `B + 100` в одной транзакции.
- **Цена:** Немного скорости и более тяжёлое масштабирование по машинам.
- **Когда брать:** Деньги, заказы, склад — всё, где неверное число недопустимо.
- **Связи:** Свойства транзакции и есть **ACID**.

### Почему это правильный дефолт

> Хороший универсальный инструмент: не самый быстрый в каком-то одном деле, но тот, за которым тянешься, пока задача специально не потребует иного.

Структурированные данные, реальные связи, произвольные запросы, сильные гарантии — реляционная база делает всё это хорошо, а современный Postgres ещё и тянет JSON, полнотекстовый поиск и даже векторы. **Начинай каждый проект здесь;** тянись за чем-то другим, только когда конкретная боль — масштаб, форма или паттерн доступа — выталкивает тебя.

> **Реляционный вопрос:** «Есть ли у моих данных ясная структура и связи, которые я буду запрашивать способами, что пока не могу полностью предугадать?»

> Начинай с Postgres. Вопрос не «зачем SQL» — а есть ли у тебя реальная причина его не брать.

## 3. Когда таблица — не та форма

Иногда жёсткая таблица — неправильная форма. «NoSQL» — это на самом деле семейство хранилищ, и каждое гнёт правила в свою сторону ради своей задачи.

### Document Store — по JSON-документу на сущность

> Папка заполненных анкет, где у каждой могут быть слегка разные поля — нет центрального шаблона, заставляющего все совпадать.

Это **документное хранилище (document store)**. Оно хранит записи как гибкие **JSON-подобные документы**, вложенные и почти бессхемные (**schema-less**), так что две записи в одной **коллекции (collection)** могут различаться. Хорошо, когда формы разнятся или быстро меняются, и целая сущность — товар, профиль — живёт как один документ, который ты достаёшь за одно чтение.

_Diagram: Два товара в одной коллекции с разными полями; весь товар достаётся одним чтением._

#### Document Store (документная БД)

- **Проблема:** Формы записей разные или быстро меняются; таблица мешает.
- **Идея:** Каждая сущность — один вложенный JSON-документ, без общей жёсткой схемы.
- **Пример:** MongoDB: каталог товаров, профили пользователей.
- **Цена:** Меньше гарантий и сложнее запросы между документами, чем в SQL.
- **Когда брать:** Сущность читается целиком, а поля у разных записей разные.
- **Связи:** Postgres тоже умеет документы (**JSONB**); часто живёт по правилам **BASE**.

### Key-Value Store — гигантская хеш-таблица

> Гардероб — отдаёшь номерок, получаешь ровно своё пальто. Никаких поисков, никаких вопросов, мгновенно.

Это **хранилище ключ-значение (key-value store)** — простейшая форма: **ключ на входе, значение на выходе**, ослепительно быстро. Идеально для кэша, сессий, счётчиков ограничения запросов (**rate limit**) и всего, что ты достаёшь по известному id.

_Diagram: Знаешь ключ — получаешь значение мгновенно. Спросить «по значениям» нельзя._

#### Key-Value Store (ключ-значение)

- **Проблема:** Нужно очень быстро доставать данные по известному id.
- **Идея:** Ключ → значение, как гигантская хеш-таблица.
- **Пример:** Redis, DynamoDB: `session:42`, кэш страницы, счётчик rate limit.
- **Цена:** Искать можно только по ключу — богатых запросов по самим значениям нет.
- **Когда брать:** Кэш, сессии, счётчики — всё, где id известен заранее.
- **Связи:** Типичный «быстрый помощник» рядом с основной базой в **Polyglot persistence**.

### Wide-Column Store — под огромные потоки записей

> Огромный склад с бесконечными одинаковыми проходами, устроенный так, чтобы тысяча погрузчиков расставляла товар одновременно, не сталкиваясь.

Это **хранилище широких колонок (wide-column store)**. Оно размазывает данные по множеству машин по **ключу партиции (partition key)** ради **огромной пропускной способности записи и масштаба**, меняя богатые запросы и строгую согласованность на способность принять брандспойт данных. Мощно — и больше работы по эксплуатации.

_Diagram: Ключ партиции раскидывает записи по многим машинам; строки длинные, соединений нет._

#### Wide-Column Store (широкие колонки)

- **Проблема:** Записей так много, что одна SQL-машина прогнётся.
- **Идея:** Разложить данные по многим узлам по ключу партиции; писать параллельно.
- **Пример:** Cassandra: логи событий, телеметрия, ленты на большом масштабе.
- **Цена:** Нет богатых запросов и строгой согласованности; сложнее эксплуатировать.
- **Когда брать:** Брандспойт записей, а запросы заранее известны и простые.
- **Связи:** По сути встроенный **Sharding**; обычно живёт по **BASE**.

### Graph Database — когда суть в связях

> Доска детектива — фото, соединённые нитями. Ценность не в отдельном фото, а в паутине того, кто с кем связан.

Это **графовая база данных (graph database)**. Она моделирует данные как **узлы (nodes)** и **рёбра (edges)** между ними, делая запросы по связям — «друзья друзей, которым это нравится», «путь между этими двумя аккаунтами» — естественными и быстрыми там, где SQL понадобились бы болезненные многосторонние соединения. **Тянись за этим, когда связи важнее самих записей.**

_Diagram: «Друзья друзей, которым нравится X»: просто идём по рёбрам Ann → Bob → Cara → Jazz club._

#### Graph Database (графовая БД)

- **Проблема:** Главные вопросы — про связи, и в SQL это многоэтажные JOIN.
- **Идея:** Узлы и рёбра — первоклассные данные; запрос идёт по рёбрам.
- **Пример:** Neo4j: социальный граф, рекомендации, выявление мошенничества.
- **Цена:** Узкая ниша; для обычных записей и отчётов неудобна.
- **Когда брать:** Связи важнее самих записей.
- **Связи:** Альтернатива **Relational Database**, когда JOIN становится болью.

> **Вопрос про NoSQL:** «Реляционная форма правда мне мешает — разношёрстные документы, чистые поиски по ключу, брандспойт записей или паутина связей?»

> NoSQL — это не «новее SQL». Каждое хранилище сбрасывает какую-то гарантию ради чего-то конкретного — знай, чем именно ты жертвуешь.

## 4. Специализированные хранилища

За универсальными хранилищами стоят базы, сделанные под одну задачу, где специализированный движок обходит универсальный на голову.

### Time-Series Database — данные со штампом времени

> Лента кардиомонитора — бесконечная полоса показаний, каждое привязано к моменту, и ты почти всегда спрашиваешь «что было между этими двумя временами?»

Это **база временных рядов (time-series database)**. Она заточена под **данные, которые приходят по порядку времени и запрашиваются по времени** — метрики, показания датчиков, цены. Принимает огромные потоки и быстро отвечает «среднее за минуту за прошлую неделю», со встроенным удалением старых данных (**retention**).

_Diagram: Точки идут по времени; запрос — окно от t1 до t2; старое удаляется по retention._

#### Time-Series Database (временные ряды, TSDB)

- **Проблема:** Огромный поток показаний, и все вопросы — «что было за этот период».
- **Идея:** Хранить и индексировать по времени; быстро агрегировать окна.
- **Пример:** InfluxDB, TimescaleDB: метрики серверов, датчики, цены.
- **Цена:** Ещё одно хранилище; для обычных бизнес-данных не подходит.
- **Когда брать:** У всего есть метка времени, и время — главная ось.
- **Связи:** TimescaleDB — расширение Postgres, см. **Postgres first**.

### Search Engine — полный текст и релевантность

> Строка поиска, которая прощает опечатки, ставит лучшие совпадения первыми и находит «running», когда ты набрал «run», — как библиотекарь, понимающий, что ты имел в виду.

Это **поисковый движок (search engine)**. Он строит **обратный индекс (inverted index)** — «слово → документы» — ради **быстрого полнотекстового поиска (full-text search) с ранжированием, нечётким совпадением и фильтрами** — того, что `LIKE '%word%'` в SQL делает медленно и плохо. Обычно работает рядом с основной базой, держится в синхроне и питает строку поиска и разбор логов.

_Diagram: Данные копируются из Postgres в поисковый индекс; запрос с опечаткой даёт ранжированный список._

#### Search Engine (поисковый движок)

- **Проблема:** `LIKE '%word%'` медленный, не прощает опечаток и не ранжирует.
- **Идея:** Обратный индекс слов + оценка релевантности.
- **Пример:** Elasticsearch, OpenSearch: строка поиска на сайте, разбор логов.
- **Цена:** Надо держать в синхроне с основной базой.
- **Когда брать:** Пользователи ищут по тексту и ждут «умного» поиска.
- **Связи:** **Не источник правды** — быстрая линза на него; часть **Polyglot persistence**.

### Vector Database — поиск по смыслу

> Вместо совпадения точных слов ты спрашиваешь «найди то, что похоже на это» — и получаешь соседей по сходству, а не по ключевому слову.

Это **векторная база данных (vector database)**. Она хранит **embeddings** — числовые отпечатки смысла — и находит ближайшие (**nearest neighbours**), так что ты ищешь по сходству, а не по точному тексту. Это движок под **семантическим поиском (semantic search)** и **RAG**, где AI-приложение достаёт самые релевантные куски, чтобы из них ответить.

_Diagram: Текст превращается в вектор; ближайшие точки — это тексты, близкие по смыслу._

#### Vector Database (векторная БД)

- **Проблема:** Нужно найти похожее по смыслу, а слова могут не совпадать.
- **Идея:** Хранить embeddings и искать ближайших соседей.
- **Пример:** pgvector, Pinecone: по запросу «refund policy» найти «returns» и «money back».
- **Цена:** Нужна модель embeddings и синхронизация с исходными данными.
- **Когда брать:** Семантический поиск и RAG — новое обязательное хранилище эпохи AI.
- **Связи:** pgvector — расширение Postgres; копия данных, как **Search Engine**.

### Object Storage — куда кладут большие файлы

> Бокс на складе самообслуживания — не для карточек, а для мебели: громоздкие вещи, которые держишь по ярлыку и вывозишь целиком.

Это **объектное хранилище (object storage)**. Оно не база данных, но именно туда кладут **большие блобы (blobs)** — картинки, видео, бэкапы, документы — адресуемые **ключом (key)** внутри **бакета (bucket)** и отдаваемые дёшево на масштабе. Паттерн почти универсален: файл — в объектном хранилище, а строку, указывающую на него, — в базе. **Записи в БД, нагрузка в бакете.**

_Diagram: Строка в Postgres хранит только ключ; сам файл лежит в S3-бакете._

#### Object Storage (объектное хранилище, S3)

- **Проблема:** Большие файлы в базе раздувают её и дорого отдаются.
- **Идея:** Файл — в бакете по ключу; в базе — только строка с этим ключом.
- **Пример:** S3 и его собратья: фото, видео, бэкапы, документы.
- **Цена:** Не база: запросов по содержимому нет, только «положить/достать по ключу».
- **Когда брать:** Любые большие файлы.
- **Связи:** По форме похоже на **Key-Value Store**, только для громоздких вещей.

> **Вопрос про специализацию:** «Это задача — время, текст, сходство, большие файлы, — где специализированное хранилище размазывает универсальное?»

> Не выгибай основную базу в поисковый движок или файловый сервер. Специализированную задачу клади в хранилище, построенное под неё.

## 5. Гарантии

Самый глубокий выбор базы — не её форма, а то, что она обещает, когда что-то идёт не так. Небольшой алфавитный суп называет этот размен.

### ACID — пессимистично, корректно, осторожно

> Кассир в банке, который перепроверяет каждую запись и не двинется дальше, пока книги не сойдутся, — медленнее, зато никогда не ошибается.

**ACID** — это **строгое обещание** реляционных баз о транзакциях: каждая транзакция оставляет данные корректными и полными или не происходит вовсе. Четыре буквы: **Atomicity** (всё или ничего), **Consistency** (правила данных всегда соблюдены), **Isolation** (параллельные транзакции не мешают друг другу), **Durability** (подтверждённое переживает падение). **Для денег, заказов и всего, где неверное число недопустимо, это тот размен, что тебе нужен.**

_Diagram: Четыре буквы ACID и что каждая обещает._

#### ACID (Atomicity, Consistency, Isolation, Durability)

- **Что обещает:** Каждая транзакция целиком корректна или не случилась вовсе.
- **Пример:** Банковский перевод, оформление заказа, списание со склада.
- **Цена:** Некоторая скорость и более тяжёлое масштабирование по машинам.
- **Когда брать:** Неверное число недопустимо.
- **Связи:** Свойства **Transaction**; противоположная позиция — **BASE**.

### BASE — оптимистично, доступно, в итоге верно

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

Многие NoSQL-системы занимают противоположную позицию: **BASE** (Basically Available, Soft state, Eventually consistent). Они остаются **быстрыми и доступными** и принимают, что копии данных могут ненадолго расходиться, прежде чем сойтись. Это называется **согласованность в конечном счёте (eventual consistency)**.

_Diagram: Запись попала на узел A сразу, на B и C — чуть позже. Миг расхождения, потом все согласны._

#### BASE (Basically Available, Soft state, Eventually consistent)

- **Что обещает:** Система всегда отвечает; копии сходятся «в итоге».
- **Пример:** Счётчик лайков, лента, счётчик просмотров.
- **Цена:** Можно ненадолго увидеть устаревшее значение.
- **Когда брать:** Доступность важнее мгновенной точности, и секунда устаревания ничего не стоит.
- **Связи:** Противоположность **ACID**; выбор «A» в **CAP**.

### CAP Theorem — при обрыве сети выбирай

> Команда, разрезанная упавшей телефонной линией: каждая половина может работать сама (но они разойдутся) или сложить инструменты до восстановления связи (но ты встаёшь). И то, и другое сразу — нельзя.

**Теорема CAP (CAP theorem)** говорит: когда **сетевой раздел (network partition)** разрезает машины твоей базы, ты должен выбрать **Consistency** (отказывать в записи, оставаться корректным) или **Availability** (продолжать обслуживать, рискуя расхождением) — оба сразу во время раздела невозможны. Любая распределённая база делает этот выбор. **Знать, что выбирает твоё хранилище, — значит знать, как оно ведёт себя в худший день.**

_Diagram: Сеть порвалась: либо CP (отказываем, но корректны), либо AP (работаем, но можем разойтись)._

#### CAP Theorem (Consistency, Availability, Partition tolerance)

- **Что говорит:** При разрыве сети нельзя одновременно быть и корректным, и доступным.
- **Пример:** Два узла потеряли связь: отказать в записи (CP) или принять её на обоих и потом мирить (AP).
- **Цена:** CP — простой; AP — устаревшие или конфликтующие данные.
- **Когда думать:** Как только данные живут больше чем на одной машине.
- **Связи:** Встаёт из-за **Replication** и **Sharding**; AP-сторона — это **BASE**.

> **Вопрос про гарантии:** «Когда две копии могут разойтись, мне нужна корректность даже ценой простоя — или аптайм даже ценой устаревания?»

> ACID для денег, BASE для лайков. Нужная гарантия задаётся ценой того, чтобы ненадолго ошибиться.

## 6. Быстро и надёжно на масштабе

Горстка приёмов превращает базу из одной хрупкой коробки в нечто быстрое и живучее. Со всеми ты столкнёшься.

### Index — предметный указатель для данных

> Чтобы найти слово в книге на 900 страниц, ты не читаешь каждую страницу — ты открываешь указатель в конце и прыгаешь прямо к нему.

**Индекс (index)** — это побочная структура, превращающая **полный перебор таблицы (full table scan)** в прямой поиск, **самый большой рычаг скорости чтения.** Подвох: каждый индекс стоит места и замедляет записи (каждая вставка должна обновить и его).

_Diagram: Без индекса база читает все строки; с индексом — прыгает по отсортированному списку прямо к нужной._

#### Index (индекс)

- **Проблема:** Запрос читает всю таблицу, и с ростом данных становится медленным.
- **Идея:** Отсортированная побочная структура по колонке → прямой поиск строки.
- **Пример:** Индекс по `email`: найти пользователя — без перебора всей таблицы.
- **Цена:** Место на диске и медленнее каждая запись.
- **Когда брать:** На колонки, по которым реально фильтруешь и сортируешь, — не на все подряд.
- **Связи:** Ускоряет **SQL**; поисковый движок — это особый индекс по тексту.

### Replication — держи копии, переживай отказ

> Документ, хранимый в трёх сейфах в трёх зданиях — если одно сгорит, другие всё равно держат его, и трое могут читать одновременно.

**Репликация (replication)** держит **копии данных на нескольких машинах**. Все записи идут в **лидера (leader)**, а **реплики (followers, replicas)** получают копию и обслуживают чтения. Если лидер умирает, его место занимает реплика (**failover**). Подвох — **лаг репликации (replication lag):** реплика может отставать на миг, так что только что записанное значение может ещё не показаться на ней.

_Diagram: Лидер принимает записи и рассылает копии; реплики отдают чтения и подхватывают, если лидер упал._

#### Replication (репликация, leader–follower)

- **Проблема:** Одна машина — одна точка отказа и предел по чтениям.
- **Идея:** Те же данные на нескольких машинах: лидер пишет, реплики читают.
- **Пример:** Postgres с двумя репликами для чтения; при падении лидера реплика становится лидером.
- **Цена:** Лаг: свежая запись может ещё не дойти до реплики.
- **Когда брать:** Нужна надёжность или чтений больше, чем тянет одна машина.
- **Связи:** Копирует **одни и те же** данные; **Sharding** делит **разные**. Обе ведут к **CAP**.

### Sharding — дели, когда одной машины мало

> Один переполненный картотечный шкаф становится A–M в одном шкафу и N–Z в другом. Больше места — но теперь надо знать, в каком шкафу живёт имя.

Когда данные или нагрузка на запись перерастают одну машину, **шардинг (sharding)** **делит данные по многим** — пользователи A–M здесь, N–Z там. Каждая часть называется **шард (shard)**, а поле, по которому делим, — **ключ шардирования (shard key)**. Это открывает почти безграничный масштаб, но **ключ шардирования — тяжёлый, почти необратимый выбор:** ошибёшься — получишь **горячие точки (hotspots)** или запросы, которым приходится бить по всем шардам.

_Diagram: Роутер смотрит на ключ: «Ann» — в шард 1, «Zoe» — в шард 2. На каждом шарде разные данные._

#### Sharding (шардинг, горизонтальное партиционирование)

- **Проблема:** Данные или поток записей не помещаются в одну машину.
- **Идея:** Разделить данные по ключу между многими машинами.
- **Пример:** Пользователи A–M на шарде 1, N–Z на шарде 2.
- **Цена:** Ключ почти не поменять; плохой ключ → горячие точки или запросы по всем шардам.
- **Когда брать:** Последним: после индексов и реплик.
- **Связи:** Встроен в **Wide-Column Store**; вместе с **Replication** ведёт к **CAP**.

### Normalization / Denormalization — один факт в одном месте или копии ради скорости

> Один общий список адресов, на который все ссылаются (без противоречий), против пришитой копии адреса к каждому заказу (читать быстрее, но теперь есть копии, которые надо держать честными).

**Нормализация (normalization)** держит каждый факт ровно в одном месте — чисто, без противоречий, реляционный дефолт. **Денормализация (denormalization)** намеренно дублирует данные, чтобы чтения были быстрее и требовали меньше соединений. Оба подхода годятся; правило — делать это **осознанно** и всегда знать, какая копия **источник правды (source of truth)**, а какая просто быстрый дубль. (Тот же урок, что с кэшем.)

_Diagram: Слева все заказы ссылаются на один адрес; справа адрес скопирован в каждый заказ._

#### Normalization / Denormalization (нормализация / денормализация)

- **Проблема:** Один и тот же факт в нескольких местах расходится; а соединения замедляют чтения.
- **Идея:** Нормализуй по умолчанию; денормализуй осознанно там, где чтения важнее.
- **Пример:** Адрес в одной таблице `addresses` против копии адреса в каждом заказе.
- **Цена:** Нормализация — больше JOIN; денормализация — копии, которые надо синхронизировать.
- **Когда брать:** Денормализацию — когда измеренно медленные чтения требуют этого.
- **Связи:** Та же идея «источник правды + быстрые копии», что в **Polyglot persistence**.

> **Вопрос про масштаб:** «Моя реальная проблема — скорость чтения (индекс, реплики), выживание (репликация) или сам размер (шардинг), — и решаю ли я ту, что у меня правда есть?»

> Сначала индекс, потом сервер; сначала реплика, потом шард. Самое дешёвое масштабирование — то, которое не приходится эксплуатировать.

## 7. Как выбирать

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

### Polyglot Persistence — несколько хранилищ осознанно

> На кухне есть холодильник, морозилка и кладовка — а не одна коробка, заставленная делать всё три. Каждая держит то, что ей подходит.

Это паттерн **Polyglot persistence** — «многоязычное хранение». Зрелые системы обычно используют несколько хранилищ: Postgres как источник правды, Redis для кэша и сессий, поисковый индекс для строки поиска, может быть, векторное хранилище для AI-фич и объектное хранилище для файлов. Каждое делает то, в чём хорошо. Дисциплина в том, чтобы ясно держать, **какое из них хранит правду**, а остальные — быстрые копии, держащиеся в синхроне.

_Diagram: Пять хранилищ, каждое под свою задачу. Правда — в Postgres, остальное — копии._

#### Polyglot Persistence (несколько хранилищ в одной системе)

- **Проблема:** Одна база не может быть лучшей во всём сразу.
- **Идея:** Каждой задаче — своё хранилище; один явный источник правды.
- **Пример:** Postgres + Redis + поисковый индекс + векторное хранилище + S3.
- **Цена:** Каждое хранилище надо эксплуатировать, бэкапить и держать согласованным.
- **Когда брать:** Когда реальная боль требует ещё одного хранилища, а не «на всякий случай».
- **Связи:** Начинается с **Postgres first**; объединяет почти все хранилища курса.

### Postgres First — начинай с Postgres, вырастай осознанно

> Швейцарский нож тянет поразительно много, прежде чем тебе вообще понадобится полный набор инструментов.

Это практика **Postgres first**. Современный Postgres поразительно способен — реляционное ядро, JSON-документы, полнотекстовый поиск, векторы, даже рабочая очередь задач. **Большинство продуктов должны начинать просто с Postgres** и добавлять специализированное хранилище, только когда реальная, измеренная боль этого требует. Каждая лишняя база — это ещё одно, что надо эксплуатировать, бэкапить и держать согласованным. Самое дешёвое хранилище — то, которое ты не добавил.

_Diagram: Один Postgres закрывает таблицы, документы, поиск, векторы и очередь. Новое хранилище — только по реальной боли._

#### Postgres First (начинай с Postgres)

- **Проблема:** Команда добавляет базы раньше, чем появляется боль.
- **Идея:** Один Postgres, пока измеренная проблема не потребует большего.
- **Пример:** JSONB вместо отдельной документной базы, pgvector вместо отдельной векторной.
- **Цена:** На очень большом масштабе специализированный движок всё же обгонит.
- **Когда брать:** В начале почти любого продукта.
- **Связи:** Первый шаг к **Polyglot persistence** — когда «пока что» закончилось.

### Как всё связано

> Карта района после прогулки: теперь видно, какие улицы куда ведут.

Понятия курса не живут поодиночке. Реляционная база — **источник правды** с гарантиями **ACID**; **индекс** ускоряет её чтения; поисковый движок и векторная база — **копии**, которые от неё питаются; в объектном хранилище лежат файлы, на которые указывают её строки. Когда одной машины мало, приходят **репликация и шардинг** — и вместе с ними выбор **CAP**. NoSQL-семейство часто выбирает **BASE** — противоположность ACID.

_Diagram: Главные связи между понятиями курса: в центре — источник правды._

#### Прежде чем выбрать базу

- **Какой формы данные** — таблицы, документы, ключ-значение, граф?
- **Как я буду их запрашивать** — по id, по связям, по тексту, по сходству, по времени?
- **Какие гарантии мне нужны** — строгий ACID или сойдёт eventually consistent?
- **Каков баланс чтений/записей** и примерный масштаб?
- **Не смог бы Postgres уже это сделать**, прежде чем я добавлю ещё хранилище?
- **Какое хранилище держит источник правды**, а какие — просто копии?

#### Признаки, что ты выбрал не то

- Ты гоняешь `LIKE '%...%'` для поиска вместо поискового движка.
- **Хранилище ключ-значение** там, где постоянно нужно запрашивать по значениям.
- **Пять баз** для продукта, который поддерживают два человека.
- Ты взял **MongoDB, чтобы избежать схем**, а потом всё равно руками собрал валидацию.
- **Нет ясного источника правды** — два хранилища оба претендуют на авторитет.

#### Признаки, что ты выбрал хорошо

- **Запрос, который ты гоняешь чаще всего**, — тот, в котором хранилище быстрее всего.
- **Гарантия соответствует ставкам** — строгая для денег, расслабленная для лайков.
- Ты можешь назвать **источник правды** для каждого важного факта.
- Ты добавил каждое хранилище, чтобы решить **реальную, прочувствованную боль**, а не гипотетическую.
- Ты мог бы **объяснить модель данных** новому инженеру за несколько минут.

## Шпаргалка: все 20 понятий

### Реляционная основа

- **Relational Database** (реляционная БД): Таблицы с фиксированной схемой, связи через внешние ключи. Дефолт.
- **SQL** (язык запросов): Описываешь, что нужно; база сама решает, как достать.
- **Transaction** (транзакция): Несколько записей как одно целое: COMMIT всех или ROLLBACK всех.

### Семейство NoSQL

- **Document Store** (документная БД): Гибкие JSON-документы; сущность читается целиком.
- **Key-Value Store** (ключ-значение): Ключ → значение, мгновенно. Кэш, сессии, счётчики.
- **Wide-Column Store** (широкие колонки): Данные по многим машинам ради брандспойта записей.
- **Graph Database** (графовая БД): Узлы и рёбра; запросы по связям без мучительных JOIN.

### Специализированные хранилища

- **Time-Series Database** (временные ряды): Показания по времени; запросы окнами, старое удаляется.
- **Search Engine** (поисковый движок): Полнотекстовый поиск с ранжированием и опечатками. Копия, не правда.
- **Vector Database** (векторная БД): Embeddings и поиск ближайших по смыслу. Основа RAG.
- **Object Storage** (S3): Большие файлы по ключу в бакете; в базе — только ссылка.

### Гарантии

- **ACID** (строгое обещание): Транзакция целиком корректна или не случилась. Для денег.
- **BASE** (eventual consistency): Всегда доступно, копии сходятся в итоге. Для лайков.
- **CAP Theorem** (теорема CAP): При разрыве сети — либо корректность, либо доступность.

### Масштаб

- **Index** (индекс): Указатель для быстрого чтения; цена — место и медленные записи.
- **Replication** (репликация): Те же данные на многих машинах: надёжность и чтения. Есть лаг.
- **Sharding** (шардинг): Разные данные на разных машинах по ключу. Ключ почти не поменять.
- **Normalization / Denormalization** (нормализация): Один факт в одном месте — или осознанные копии ради скорости.

### Как выбирать

- **Polyglot Persistence** (несколько хранилищ): Каждой задаче своё хранилище, один источник правды.
- **Postgres First** (начинай с Postgres): Один Postgres, пока реальная боль не потребует большего.

> Лучшее решение по базе обычно — «Postgres, пока что» — плюс мудрость понять ровно тот момент, когда «пока что» закончилось.

Конец экспресс-курса · 7 глав · 20 понятий

Дальше — глубина: «Designing Data-Intensive Applications» Мартина Клеппманна — та самая книга, которую стоит прочесть, плюс документация Postgres и руководство твоего хранилища. Но прежде глубокой воды — представь, как твои данные будут читать, писать и кому доверять. База — это место, где система помнит; выбери, как она помнит, прежде чем выбирать, на чём она построена.
