Экспресс-курс · No. 06
Языки и фреймворки приходят и уходят. Данные, которые ты хранишь, и форма, в которой ты их хранишь, могут пережить их все — поэтому выбрать базу значит выбрать, как твоя система помнит и насколько тяжело ей передумать.
Только суть · Один образ на хранилище · Примеры важнее бенчмарков
База данных — это долговременная память твоей системы. Код переписывают; данные и форма, которую ты для них выбрал, обычно переживают всё вокруг.
Данные переживают код
Дом за десятилетия перекрашивают, переставляют мебель, даже перекрывают крышу — но фундамент, залитый в первый день, остаётся, потому что заменить его значит поднять весь дом.
Языки меняются, фреймворки подменяют, интерфейс перерисовывают десять раз. Данные и их структура обычно остаются, потому что менять их на живой системе, полной реальных записей, медленно и рискованно. Поэтому база — первое, о чём надо подумать всерьёз, и последнее, что стоит делать впопыхах.
База отвечает на два вопроса
Выбор, где хранить вещи, задаёт два вопроса: какой они формы — книги на полках или странные инструменты на крючках? — и насколько надёжно их надо беречь — сейф или удобный ящик?
Любое хранилище определяется тем, как оно придаёт данным форму (жёсткие таблицы, гибкие документы, граф связей) и что оно гарантирует (строгую корректность или скорость и доступность с более слабыми обещаниями). Почти любой выбор базы — это разный ответ на эти два вопроса, и остальной курс — это меню.
Выбирай по тому, как читаешь и пишешь, а не по хайпу
Картотечный шкаф, каталог карточек и конвейер — всё это «хранилище», но выбираешь ты по тому, как будешь доставать вещи, а не по тому, что выглядит новее.
Лучшей базы не бывает. Хранилище, идеальное под быстрые поиски по ключу, плохо для глубоких запросов по связям; построенное под огромные записи плохо для строгих банковских транзакций. Правильный вопрос — твой паттерн доступа: что ты читаешь, что пишешь, как часто и как это должно вести себя, когда что-то идёт не так.
Код легко поменять. Данные — нет. Выбирай хранилище как решение, с которым проживёшь годы, — потому что так и будет.
Для большинства систем дефолт — реляционная база, и этот дефолт она заслуживает. Начинай отсюда и уходи только с причиной.
Таблицы, строки и связи
Правильно сделанная таблица: каждый лист — один вид сущности (клиенты, заказы), каждая строка — одна запись, и заказы ссылаются назад на клиента, которому принадлежат.
Реляционные базы (Postgres, MySQL) хранят данные в таблицах с фиксированной схемой и связывают таблицы по ссылке — строка заказа держит id своего клиента. Схема — это обещание о форме: у каждого заказа есть нужные колонки, проверенные на входе. Структура заранее в обмен на здравомыслие потом.
ACID: корректность по принципу «всё или ничего»
Денежный перевод — это две записи: минус здесь, плюс там, — но одно намерение. Либо обе случились, либо ни одна; наполовину сделанный перевод — катастрофа.
Реляционные базы дают транзакции с гарантиями ACID: группа изменений либо целиком фиксируется, либо целиком откатывается, изолированно от других, и переживает падение, как только подтверждена. Поэтому банки, заказы и склад живут в SQL — когда корректность важнее голой скорости, тебе нужно именно это обещание.
SQL: проси что хочешь, а не как достать
Ты говоришь библиотекарю «все романы французских авторов, изданные после 1950-го, по году» — и он сам соображает, как их найти. Ты описываешь результат, а не поиск.
SQL — это декларативный язык запросов: ты заявляешь нужный ответ, а база планирует, как его достать, включая соединение таблиц. SELECT ... JOIN ... WHERE ... может ответить на вопросы, которых ты не предвидел при проектировании, — одна из тихих суперсил реляционной модели.
Почему это правильный дефолт
Хороший универсальный инструмент: не самый быстрый в каком-то одном деле, но тот, за которым тянешься, пока задача специально не потребует иного.
Структурированные данные, реальные связи, произвольные запросы, сильные гарантии — реляционная база делает всё это хорошо, а современный Postgres ещё и тянет JSON, полнотекстовый поиск и даже векторы. Начинай каждый проект здесь; тянись за чем-то другим, только когда конкретная боль — масштаб, форма или паттерн доступа — выталкивает тебя.
Начинай с Postgres. Вопрос не «зачем SQL» — а есть ли у тебя реальная причина его не брать.
Иногда жёсткая таблица — неправильная форма. «NoSQL» — это на самом деле семейство хранилищ, и каждое гнёт правила в свою сторону ради своей задачи.
Документные: по JSON-блобу на сущность
Папка заполненных анкет, где у каждой могут быть слегка разные поля — нет центрального шаблона, заставляющего все совпадать.
Документные базы (MongoDB) хранят записи как гибкие JSON-подобные документы, вложенные и почти бессхемные, так что две записи в одной коллекции могут различаться. Хорошо, когда формы разнятся или быстро меняются, и целая сущность — товар, профиль — живёт как один документ, который ты достаёшь за одно чтение. Цена: меньше гарантий и сложнее запросы между документами, чем в SQL.
Ключ-значение: гигантская хеш-таблица
Гардероб — отдаёшь номерок, получаешь ровно своё пальто. Никаких поисков, никаких вопросов, мгновенно.
Хранилища ключ-значение (Redis, DynamoDB) — простейшая форма: ключ на входе, значение на выходе, ослепительно быстро. Идеально для кэша, сессий, счётчиков rate-limit и всего, что ты достаёшь по известному id. Размен: искать можно только по ключу — богатых запросов по самим значениям нет.
Широкие колонки: под огромные записи
Огромный склад с бесконечными одинаковыми проходами, устроенный так, чтобы тысяча погрузчиков расставляла товар одновременно, не сталкиваясь.
Хранилища широких колонок (Cassandra) размазывают данные по множеству машин ради огромной пропускной способности записи и масштаба, меняя богатые запросы и строгую согласованность на способность принять брандспойт данных. Используются для логов событий, телеметрии и лент на масштабе, где одна SQL-машина прогнулась бы. Мощно — и больше работы по эксплуатации.
Графовые: когда суть в связях
Доска детектива — фото, соединённые нитями. Ценность не в отдельном фото, а в паутине того, кто с кем связан.
Графовые базы (Neo4j) моделируют данные как узлы и рёбра между ними, делая запросы по связям — «друзья друзей, которым это нравится», «путь между этими двумя аккаунтами» — естественными и быстрыми там, где SQL понадобились бы болезненные многосторонние соединения. Дом социальных графов, рекомендаций и выявления мошенничества. Тянись за этим, когда связи важнее самих записей.
NoSQL — это не «новее SQL». Каждое хранилище сбрасывает какую-то гарантию ради чего-то конкретного — знай, чем именно ты жертвуешь.
За универсальными хранилищами стоят базы, сделанные под одну задачу, где специализированный движок обходит универсальный на голову.
Временные ряды: данные со штампом времени
Лента кардиомонитора — бесконечная полоса показаний, каждое привязано к моменту, и ты почти всегда спрашиваешь «что было между этими двумя временами?»
Базы временных рядов (InfluxDB, TimescaleDB) заточены под данные, которые приходят по порядку времени и запрашиваются по времени — метрики, показания датчиков, цены. Они принимают огромные потоки и быстро отвечают «среднее за минуту за прошлую неделю», со встроенным истечением старых данных. Когда у всего, что ты хранишь, есть метка времени и время — главная ось, это твой выбор.
Поисковые движки: полный текст и релевантность
Строка поиска, которая прощает опечатки, ставит лучшие совпадения первыми и находит «running», когда ты набрал «run», — как библиотекарь, понимающий, что ты имел в виду.
Поисковые движки (Elasticsearch, OpenSearch) индексируют текст ради быстрого полнотекстового поиска с ранжированием, нечётким совпадением и фильтрами — того, что LIKE '%word%' в SQL делает медленно и плохо. Обычно работают рядом с основной базой, держатся в синхроне и питают строку поиска и разбор логов. Не источник правды — быстрая линза на него.
Векторные базы: поиск по смыслу
Вместо совпадения точных слов ты спрашиваешь «найди то, что похоже на это» — и получаешь соседей по сходству, а не по ключевому слову.
Векторные базы (pgvector, Pinecone) хранят embeddings — числовые отпечатки смысла — и находят ближайшие, так что ты ищешь по сходству, а не по точному тексту. Это движок под семантическим поиском и RAG, где AI-приложение достаёт самые релевантные куски, чтобы из них ответить. Новое обязательное хранилище эпохи AI.
Объектное хранилище: куда кладут большие файлы
Бокс на складе самообслуживания — не для карточек, а для мебели: громоздкие вещи, которые держишь по ярлыку и вывозишь целиком.
Объектное хранилище (S3 и его собратья) — не база данных, но именно туда кладут большие блобы — картинки, видео, бэкапы, документы — адресуемые ключом и отдаваемые дёшево на масштабе. Паттерн почти универсален: файл — в объектном хранилище, а строку, указывающую на него, — в базе. Записи в БД, нагрузка в бакете.
Не выгибай основную базу в поисковый движок или файловый сервер. Специализированную задачу клади в хранилище, построенное под неё.
Самый глубокий выбор базы — не её форма, а то, что она обещает, когда что-то идёт не так. Небольшой алфавитный суп называет этот размен.
ACID: пессимистично, корректно, осторожно
Кассир в банке, который перепроверяет каждую запись и не двинется дальше, пока книги не сойдутся, — медленнее, зато никогда не ошибается.
ACID (Atomicity, Consistency, Isolation, Durability) — строгое обещание реляционных баз: каждая транзакция оставляет данные корректными и полными или не происходит вовсе. Ты платишь некоторой скоростью и более тяжёлым масштабированием по машинам. Для денег, заказов и всего, где неверное число недопустимо, это тот размен, что тебе нужен.
BASE: оптимистично, доступно, в итоге верно
Оживлённый магазин, где всем дают покупать, даже пока обновляют ценники, — числа догонят через миг, и это нормально.
Многие NoSQL-системы занимают противоположную позицию: BASE (Basically Available, Soft-state, Eventually consistent). Они остаются быстрыми и доступными и принимают, что копии данных могут ненадолго расходиться, прежде чем сойтись. Идеально, когда доступность важнее мгновенной точности — счётчик лайков, лента, счётчик просмотров, — и секунда устаревания ничего не стоит.
CAP: при обрыве сети выбери два из трёх
Команда, разрезанная упавшей телефонной линией: каждая половина может работать сама (но они разойдутся) или сложить инструменты до восстановления связи (но ты встаёшь). И то, и другое сразу — нельзя.
Теорема CAP говорит: когда сетевой раздел разрезает машины твоей базы, ты должен выбрать Consistency (отказывать в записи, оставаться корректным) или Availability (продолжать обслуживать, рискуя расхождением) — оба сразу во время раздела невозможны. Любая распределённая база делает этот выбор. Знать, что выбирает твоё хранилище, — значит знать, как оно ведёт себя в худший день.
ACID для денег, BASE для лайков. Нужная гарантия задаётся ценой того, чтобы ненадолго ошибиться.
Горстка приёмов превращает базу из одной хрупкой коробки в нечто быстрое и живучее. Со всеми ты столкнёшься.
Индексы: предметный указатель для данных
Чтобы найти слово в книге на 900 страниц, ты не читаешь каждую страницу — ты открываешь указатель в конце и прыгаешь прямо к нему.
Индекс — это побочная структура, превращающая полный перебор таблицы в прямой поиск, самый большой рычаг скорости чтения. Подвох: каждый индекс стоит места и замедляет записи (каждая вставка должна обновить и его). Поэтому индексируешь колонки, по которым реально фильтруешь и сортируешь, — не все подряд на всякий случай.
Репликация: держи копии, переживай отказ
Документ, хранимый в трёх сейфах в трёх зданиях — если одно сгорит, другие всё равно держат его, и трое могут читать одновременно.
Репликация держит копии данных на нескольких машинах: если лидер умирает, его место занимает реплика, а чтения можно размазать по копиям. Это покупает надёжность и масштабирование чтений. Подвох — лаг репликации: реплика может отставать на миг, так что только что записанное значение может ещё не показаться на ней.
Шардинг: дели, когда одной машины мало
Один переполненный картотечный шкаф становится A–M в одном шкафу и N–Z в другом. Больше места — но теперь надо знать, в каком шкафу живёт имя.
Когда данные или нагрузка на запись перерастают одну машину, шардинг делит данные по многим — пользователи A–M здесь, N–Z там. Это открывает почти безграничный масштаб, но ключ шардирования — тяжёлый, почти необратимый выбор: ошибёшься — получишь горячие точки или запросы, которым приходится бить по всем шардам. Репликация копирует одни и те же данные; шардинг делит разные.
Нормализуй, потом денормализуй осознанно
Один общий список адресов, на который все ссылаются (без противоречий), против пришитой копии адреса к каждому заказу (читать быстрее, но теперь есть копии, которые надо держать честными).
Нормализация держит каждый факт ровно в одном месте — чисто, без противоречий, реляционный дефолт. Денормализация намеренно дублирует данные, чтобы чтения были быстрее и требовали меньше соединений. Оба подхода годятся; правило — делать это осознанно и всегда знать, какая копия источник правды, а какая просто быстрый дубль. (Тот же урок, что с кэшем.)
Сначала индекс, потом сервер; сначала реплика, потом шард. Самое дешёвое масштабирование — то, которое не приходится эксплуатировать.
Ты не выбираешь одну базу и не запихиваешь в неё всё. Реальные системы используют несколько, каждую под задачу, в которой она лучше всего.
Polyglot persistence: несколько хранилищ осознанно
На кухне есть холодильник, морозилка и кладовка — а не одна коробка, заставленная делать всё три. Каждая держит то, что ей подходит.
Зрелые системы обычно полиглотны: Postgres как источник правды, Redis для кэша и сессий, поисковый индекс для строки поиска, может быть, векторное хранилище для AI-фич и объектное хранилище для файлов. Каждое делает то, в чём хорошо. Дисциплина в том, чтобы ясно держать, какое из них хранит правду, а остальные — быстрые копии, держащиеся в синхроне.
Начинай с Postgres; вырастай из него осознанно
Швейцарский нож тянет поразительно много, прежде чем тебе вообще понадобится полный набор инструментов.
Современный Postgres поразительно способен — реляционное ядро, JSON-документы, полнотекстовый поиск, векторы, даже рабочая очередь задач. Большинство продуктов должны начинать просто с Postgres и добавлять специализированное хранилище, только когда реальная, измеренная боль этого требует. Каждая лишняя база — это ещё одно, что надо эксплуатировать, бэкапить и держать согласованным. Самое дешёвое хранилище — то, которое ты не добавил.
- Какой формы данные — таблицы, документы, ключ-значение, граф? - Как я буду их запрашивать — по id, по связям, по тексту, по сходству, по времени? - Какие гарантии мне нужны — строгий ACID или сойдёт eventually consistent? - Каков баланс чтений/записей и примерный масштаб? - Не смог бы Postgres уже это сделать, прежде чем я добавлю ещё хранилище?
- Какое хранилище держит источник правды, а какие — просто копии?
- Ты гоняешь
LIKE '%...%'для поиска вместо поискового движка. - Хранилище ключ-значение там, где постоянно нужно запрашивать по значениям. - Пять баз для продукта, который поддерживают два человека. - Ты взял MongoDB, чтобы избежать схем, а потом всё равно руками собрал валидацию. - Нет ясного источника правды — два хранилища оба претендуют на авторитет.
- Запрос, который ты гоняешь чаще всего, — тот, в котором хранилище быстрее всего. - Гарантия соответствует ставкам — строгая для денег, расслабленная для лайков. - Ты можешь назвать источник правды для каждого важного факта. - Ты добавил каждое хранилище, чтобы решить реальную, прочувствованную боль, а не гипотетическую. - Ты мог бы объяснить модель данных новому инженеру за несколько минут.
Лучшее решение по базе обычно — «Postgres, пока что» — плюс мудрость понять ровно тот момент, когда «пока что» закончилось.