Базы данных

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

1980-е. Банки хранят транзакции в CSV-файлах. Два процесса читают баланс одновременно: оба видят $1000, оба списывают $700. Итог: 300 вместо правильного отказа. Этот баг называется lost update - и он стал первой задачей, которую решили базы данных. Эдгар Кодд сформулировал реляционную модель в 1970 году, работая в IBM. Менеджеры считали идею непрактичной. Сегодня Oracle - 50 миллиардов долларов выручки в год.

  • **MySQL** под капотом WordPress и Shopify - миллионы магазинов, десятки тысяч запросов в секунду на обычном серваке
  • **PostgreSQL** в Instagram и Heroku: строгие типы, JSONB, расширения - реляционная модель, которая не мешает расти
  • **Oracle** в банках и авиалиниях - там, где ACID-гарантии буквально означают что деньги не исчезают
  • **SQLite** на каждом iPhone и в браузере Chrome: 4 миллиарда устройств, ноль серверов
  • **Polyglot persistence**: Shopify использует MySQL + Redis + ClickHouse одновременно. Каждая БД делает то, в чём сильна

Файлы vs базы данных

1980-е. Банк. Два кассира одновременно открывают файл с балансами. Оба читают: счёт 1000 долларов. Оба списывают 700. Оба сохраняют. Баланс: 300 долларов вместо правильного отказа второй операции. 700 долларов испарились. Это **lost update** - и он случается каждый раз, когда два процесса одновременно читают и пишут один файл.

Lost update - только верхушка. Файлы создают целый класс проблем, которые инженеры 1970-х решали костылями, пока Эдгар Кодд не написал свою статью.

КритерийФайлы (CSV/JSON)База данных (СУБД)
Конкурентный доступПерезаписывают друг другаТранзакции и блокировки
Поиск по полюO(n) - перебор всего файлаO(log n) - индексы (B-tree)
Целостность данныхНет гарантийConstraints, типы, FK
Сбой при записиФайл может повредитьсяТранзакции: всё или ничего
Связи между даннымиВручную по ID в кодеJOIN, FK - встроено
МасштабДо ~100 МБ нормальноТерабайты без проблем

**Файлы не враги.** Конфиги, логи, статика - всё это нормально хранить в файлах. Но как только данные нужно **искать, обновлять конкурентно и защищать от потерь** - нужна СУБД. Граница чёткая: файл не имеет понятия о транзакции.

База данных - не "файл с интерфейсом". Это **специализированная система**, которая решает конкретные задачи: атомарность операций (всё или ничего), конкурентный доступ без потерь, индексирование O(log n) вместо O(n), гарантии сохранности при сбое. Именно за это Кодд получил премию Тьюринга в 1981 году.

У вас CSV-файл с 10 млн заказов. Два менеджера одновременно обновляют разных клиентов. Какая главная проблема?

Типы баз данных

Нет "лучшей" базы данных - есть правильный инструмент для задачи. Реляционные доминируют 50 лет, но за последние 15 появились десятки альтернатив. Каждая оптимизирована под конкретную физику данных.

**Реляционные (SQL)** - таблицы со строгой схемой, связи через ключи, стандартизированный SQL. PostgreSQL - самая продвинутая open-source СУБД (Instagram, Heroku). MySQL - самая распространённая в вебе (WordPress, Shopify). SQLite - встраиваемая, без сервера: каждый iPhone несёт её в кармане.

**Документные (NoSQL)** - данные как JSON. Нет жёсткой схемы: каждый документ со своей структурой. MongoDB - лидер. Хорошо когда структура меняется быстро или данные иерархичны. Плохо когда нужны сложные связи и транзакции.

**Key-Value** - ключ → значение, данные в RAM. Redis - эталон. Кеш, сессии, счётчики, pub/sub. Один get занимает микросекунды. Не для сложных запросов - только get/set.

**Графовые** - узлы и рёбра. Neo4j - лидер. Запрос "друзья друзей, которые покупали X" - Neo4j делает за миллисекунды, PostgreSQL с тройным JOIN - за секунды. LinkedIn строит на этом весь граф профессиональных связей.

**Колоночные** - хранят данные по столбцам, не по строкам. ClickHouse, Cassandra. Аналитика за год по одному полю читает один столбец из миллиарда строк, не все данные. Kafka в LinkedIn обрабатывает 7 триллионов сообщений в день - и под капотом у хранилища колоночная модель.

Тип БДПримерКогда использоватьКогда НЕ использовать
РеляционнаяPostgreSQL, MySQLФинансы, e-commerce, любые связанные данныеОгромные объёмы неструктурированных данных
ДокументнаяMongoDB, CouchDBКаталоги, CMS, прототипы с меняющейся схемойСложные связи между сущностями, транзакции
Key-ValueRedis, MemcachedКеш, сессии, очереди, счётчикиСложные запросы, связи между данными
ГрафоваяNeo4j, ArangoDBСоцсети, рекомендации, маршруты, fraud detectionПростые CRUD без связей
КолоночнаяClickHouse, CassandraАналитика, логи, time-series, агрегацииЧастые обновления отдельных строк

**Polyglot persistence** - один продукт, несколько БД. Shopify: MySQL для заказов, Redis для кеша, ClickHouse для аналитики. Airbnb: PostgreSQL для бронирований, Elasticsearch для поиска, Druid для метрик. Это не усложнение ради усложнения - это каждая БД на своей сильной стороне.

Вы строите систему рекомендаций: "пользователи, которые дружат с вашими друзьями и покупали похожие товары". Какой тип БД оптимален?

CRUD-операции

Любое приложение делает с данными четыре вещи: **создаёт, читает, обновляет, удаляет**. Четыре. Не пять, не двадцать. Эти операции получили аббревиатуру **CRUD** - Create, Read, Update, Delete. Каждый REST API, каждая форма, каждый ORM в любом языке - это CRUD поверх базы. Вся архитектура только маскирует четыре команды.

**UPDATE и DELETE без WHERE** - две самых опасных команды в SQL. Они затронут ВСЕ строки в таблице. Всегда начинайте с SELECT с теми же условиями, чтобы проверить, какие строки будут затронуты.

CRUDSQLHTTP (REST)Описание
CreateINSERTPOSTСоздание новой записи
ReadSELECTGETЧтение данных
UpdateUPDATEPUT / PATCHИзменение существующей записи
DeleteDELETEDELETEУдаление записи

**Soft delete** - вместо DELETE ставить флаг `deleted_at = NOW()`. Данные восстановимы, аудит-трейл сохранён. GitHub, Slack, Notion - все используют. Настоящий DELETE в продакшне - редкость. Данные дороже дискового места.

Вы хотите поменять email у пользователя с id = 42. Какой порядок действий безопаснее всего?

Клиент-серверная модель БД

База данных - это отдельный **сервер** (процесс), который слушает TCP-порт и принимает запросы от клиентов. Приложение не трогает файлы данных напрямую - оно **отправляет SQL через сеть**, а сервер выполняет и возвращает результат. Именно поэтому PostgreSQL можно поднять на другой машине, в Docker, в облаке - приложению всё равно.

**Драйвер** - библиотека-переводчик. Код пишет `db.query('SELECT ...')`, драйвер упаковывает это в бинарный пакет PostgreSQL Wire Protocol, отправляет через TCP-сокет, парсит ответ и отдаёт строки. pg для Node.js, psycopg2 для Python, JDBC для Java - все делают одно и то же.

**Connection pooling** - критическая оптимизация. TCP-handshake + TLS + аутентификация = 5-50 мс на каждое новое соединение. Пул держит N готовых соединений и выдаёт их мгновенно. Без пула при 500 RPS половина времени ответа уходит на подключение. PgBouncer, HikariCP, Prisma - все решают эту задачу.

**Типичная ошибка** - новое соединение на каждый HTTP-запрос. При 1000 RPS это 1000 одновременных TCP-соединений, каждое по 20-50 мс. PostgreSQL по умолчанию: max_connections = 100. 1000 > 100 = сервер отказывает в соединениях. Без пула приложение упадёт под нагрузкой.

SQLite - это не настоящая база данных, просто обёртка над файлом

SQLite - полноценная реляционная СУБД с SQL, транзакциями, индексами и ACID. Работает на миллиардах устройств: каждый iPhone, Android, браузер Chrome и Firefox

SQLite отличается только архитектурой: embedded - работает внутри процесса без отдельного сервера. Telegram хранит все сообщения в SQLite. Это не упрощённая версия - это другая модель деплоя. По количеству инстансов SQLite - самая используемая СУБД в мире.

Приложение обрабатывает 500 HTTP-запросов/сек. Каждый запрос делает 1 запрос к PostgreSQL. Соединение с БД создаётся за 20 мс. Без connection pool, сколько времени будет уходить только на подключения?

Ключевые идеи

  • **Файлы ломаются при конкурентном доступе**: lost update - два процесса читают одно значение и оба его перезаписывают. БД решает через транзакции и блокировки
  • **Нет универсальной БД**: реляционные (связанные данные, ACID), документные (гибкая схема), key-value (кеш, скорость), графовые (обход связей), колоночные (аналитика)
  • **CRUD** - четыре операции за которыми стоит весь бэкенд: INSERT, SELECT, UPDATE, DELETE в SQL; POST, GET, PUT, DELETE в REST
  • **Connection pool**: без него каждый запрос тратит 20-50 мс на TCP-handshake. С пулом - 0.1 мс. При 500 RPS разница ощутима физически
  • **Кодд 1970, IBM отвергла** - Oracle 50 млрд, каждый банк мира. Непрактичные идеи иногда побеждают

Связанные темы

Базы данных пересекаются со всем стеком:

  • Реляционная модель — Углубляет тему таблиц, ключей и связей между данными
  • Сетевая модель (TCP/IP) — Клиент-серверная модель БД работает поверх TCP/IP протокола
  • Алгоритмы поиска — Индексы в БД используют B-tree и хеш-таблицы для быстрого поиска

Вопросы для размышления

  • В каком сценарии CSV-файл всё же предпочтительнее базы данных? Назовите конкретный случай.
  • При проектировании мессенджера - какие типы БД и для каких данных? Сообщения, контакты, онлайн-статус, история поиска - у каждого свой тип.
  • Почему connection pool имеет верхнюю границу maxconn? Почему нельзя открыть 10 000 соединений одновременно?
  • Кодд предложил реляционную модель в 1970. IBM не верила в её практичность. Какие современные идеи в программировании сегодня считаются непрактичными, но могут победить?

Связанные уроки

  • db-02-relational-model — После понимания зачем нужны БД - реляционная модель как основной подход
  • os-01-intro — СУБД строится поверх операционной системы
  • ds-06-hash-intro — Хеш-таблицы - основа быстрых индексов в реляционных БД
  • net-01-intro — Клиент-серверная модель БД аналогична сетевому взаимодействию
  • alg-10-binary-search
Зачем нужны базы данных

0

1

Войти