Что такое NoSQL: четыре типа нереляционных баз и когда SQL лучше

Что такое NoSQL: четыре типа нереляционных баз и когда SQL лучше Полезное

NoSQL — это общее название баз данных, которые хранят данные не в виде связанных таблиц реляционной модели, а как пары «ключ-значение», документы, широкие строки или графы. Расшифровывают его обычно как «Not only SQL»: это не запрет на SQL, а отказ от реляционной модели как единственного варианта. Ниже — четыре основных типа, чем они платят за масштабирование, что на самом деле говорят CAP и BASE и по каким признакам выбирать между NoSQL и обычной реляционной СУБД.

Примеры кода проверены 23.09.2026: Python 3.9 и 3.12, PostgreSQL 18.6 в Docker.

Мини-словарь

Термин Что значит С чем путают
Реляционная модель Данные в таблицах, связи через ключи, запросы на SQL «Любая база с таблицами»
NoSQL Семейство нереляционных моделей данных «База без SQL вообще» — многие NoSQL-системы имеют SQL-подобные языки
ACID Гарантии транзакции: атомарность, согласованность, изоляция, долговечность «Есть только в SQL»
BASE Базовая доступность, мягкое состояние, согласованность в конечном счете «Противоположность ACID во всех NoSQL»
CAP При сетевом разделе распределенная система выбирает между согласованностью и доступностью «Выберите любые 2 из 3 всегда»

Откуда взялся термин

В 1998 году словом NoSQL Карло Строцци назвал свою СУБД — при этом реляционную, просто без языка SQL. Нынешний смысл термин получил в 2009 году. Технический толчок дали публикации Google Bigtable (2006) и Amazon Dynamo (2007): как хранить огромные объемы на сотнях обычных серверов, добавляя машины, а не покупая одну все более мощную (горизонтальное масштабирование вместо вертикального).

Четыре типа NoSQL

Тип Как хранит Примеры систем Типичная задача Где неудобен
Ключ-значение Значение по уникальному ключу, содержимое для базы часто непрозрачно Redis, Valkey, Amazon DynamoDB, etcd Кеш, сессии, счетчики, очереди Поиск по полям внутри значения
Документный JSON-подобные документы с вложенностью MongoDB, Couchbase, Firestore Каталог товаров, профили, контент с разной структурой Много связей между сущностями
Wide-column (семейство столбцов) Строки по ключу, у каждой свой набор столбцов Apache Cassandra, ScyllaDB, HBase Поток записей: события, метрики, логи по ключу Произвольные запросы и JOIN
Графовый Вершины и ребра с атрибутами Neo4j, JanusGraph, Amazon Neptune Связи: соцграф, рекомендации, антифрод Массовая агрегация по всем данным

Одна граница важна сразу. Wide-column не то же самое, что колоночная аналитическая база. Cassandra хранит данные построчно по ключу и рассчитана на быструю запись и чтение по ключу. ClickHouse хранит каждый столбец отдельно ради аналитики по миллионам строк, работает на SQL и к NoSQL обычно не относится.

К NoSQL также причисляют поисковые движки, базы временных рядов и векторные базы, а многие продукты многомодельные (DynamoDB — и ключ-значение, и документы). Языки графовых баз — Cypher, Gremlin, SPARQL и стандарт ISO GQL (2024). GraphQL сюда не относится: это язык запросов к API, а не к графовой базе.

Одна и та же задача в трех моделях

Лучше всего разница видна на одних и тех же данных. Скрипт ниже работает на стандартной библиотеке Python: ключ-значение на словаре, документы на списке словарей, реляционная модель на встроенном SQLite.

import json
import sqlite3

# 1. Ключ-значение: значение непрозрачно, доступ только по ключу
kv = {}
kv["session:42"] = json.dumps({"user_id": 7, "cart": [101, 102]})
print("KV:", json.loads(kv["session:42"])["cart"])

# 2. Документ: заказ целиком, вместе с вложенными позициями
orders = [
    {"_id": 1, "customer": "Анна",
     "items": [{"sku": 101, "title": "Кружка", "qty": 2}]},
    {"_id": 2, "customer": "Борис",
     "items": [{"sku": 101, "title": "Кружка", "qty": 1},
               {"sku": 102, "title": "Чайник", "qty": 1}]},
]
print("Документ:", orders[1]["items"][1]["title"])

# Переименовали товар 101 - копий столько, сколько заказов с ним
copies = sum(1 for o in orders for i in o["items"] if i["sku"] == 101)
print("Копий названия товара 101 в документах:", copies)

# 3. Реляционная модель: товар хранится один раз, связь через ключи
db = sqlite3.connect(":memory:")
db.executescript("""
CREATE TABLE product (sku INTEGER PRIMARY KEY, title TEXT NOT NULL);
CREATE TABLE order_item (order_id INTEGER, sku INTEGER REFERENCES product,
                         qty INTEGER NOT NULL CHECK (qty > 0));
INSERT INTO product VALUES (101, 'Кружка'), (102, 'Чайник');
INSERT INTO order_item VALUES (1, 101, 2), (2, 101, 1), (2, 102, 1);
UPDATE product SET title = 'Кружка 300 мл' WHERE sku = 101;
""")
rows = db.execute("""
SELECT oi.order_id, p.title, oi.qty
FROM order_item oi JOIN product p ON p.sku = oi.sku
ORDER BY oi.order_id, p.sku""").fetchall()
print("SQL после одного UPDATE:", rows)

В консоли появится:

KV: [101, 102]
Документ: Чайник
Копий названия товара 101 в документах: 2
SQL после одного UPDATE: [(1, 'Кружка 300 мл', 2), (2, 'Кружка 300 мл', 1), (2, 'Чайник', 1)]

Что показывает пример:

  • Ключ-значение быстро отдает значение по ключу, но спросить «у кого в корзине товар 101» база не может — это пришлось бы делать приложению, перебирая значения.
  • Документ отдает заказ целиком одним чтением, без JOIN. Цена — дублирование: название товара лежит в каждом заказе, и при переименовании нужно обновить все копии (в реальной системе их тысячи).
  • Реляционная модель хранит товар один раз, и одного UPDATE хватает, чтобы все заказы показали новое название. Цена — JOIN при чтении и более жесткая схема.

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

ACID, BASE и CAP без мифов

ACID — свойства транзакции. Их дают не только PostgreSQL или MySQL (InnoDB), но и часть NoSQL-систем: в MongoDB многодокументные транзакции есть с версии 4.0, в Neo4j транзакции ACID. Вопрос не в «есть или нет», а в границах и цене транзакций в конкретной системе.

BASE описывает другой компромисс: система остается доступной, реплики какое-то время могут расходиться, но без новых записей со временем приходят к одному значению (eventual consistency). Для ленты новостей или счетчика просмотров это допустимо, для списания денег со счета — обычно нет.

CAP часто пересказывают как «выберите 2 из 3», и это главная ловушка. Точнее так: если сеть между узлами разорвалась (partition), система вынуждена выбрать, отвечать ли всем запросам (доступность) или отвечать только согласованными данными (согласованность). Когда сети ничего не мешает, выбор идет между задержкой и согласованностью — это уже модель PACELC.

C в CAP (все видят последнюю запись) и C в ACID (данные не нарушают ограничений) — разные свойства. А в Cassandra уровень согласованности задают на каждый запрос, так что ярлык «CP» или «AP» описывает настройку, а не базу целиком.

«Без схемы» — значит схема в приложении

NoSQL часто называют schemaless, но схема никуда не исчезает: ее проверяет код, который читает данные (schema-on-read). Ошибку в данных вы увидите не при записи, а когда кто-то попробует ее прочитать. Покажу это на гибридном варианте — документных полях внутри PostgreSQL 18 (тип jsonb):

CREATE TABLE product (
    sku   integer PRIMARY KEY,
    title text    NOT NULL,
    attrs jsonb   NOT NULL DEFAULT '{}'
);
INSERT INTO product VALUES
  (101, 'Кружка',  '{"volume_ml": 300, "color": "white"}'),
  (102, 'Чайник',  '{"volume_ml": 1700, "power_w": 2200}'),
  (103, 'Футболка','{"size": "M", "color": "white"}');
CREATE INDEX product_attrs_gin ON product USING gin (attrs);

SELECT sku, title FROM product WHERE attrs @> '{"color": "white"}' ORDER BY sku;

Запрос вернет Кружку (101) и Футболку (103): у товаров разный набор атрибутов, а поиск по ним поддерживается GIN-индексом. Теперь неверный ввод:

INSERT INTO product VALUES (104, 'Стакан', '{"volume_ml": "много"}');
SELECT title, (attrs->>'volume_ml')::int AS ml FROM product
WHERE attrs ? 'volume_ml' ORDER BY ml;

Вставка прошла, а упал уже отчет:

ERROR:  invalid input syntax for type integer: "много"

Исправление — вернуть проверку на запись там, где поле важно (строку 104 перед этим удаляем):

DELETE FROM product WHERE sku = 104;
ALTER TABLE product ADD CONSTRAINT volume_is_number
  CHECK (NOT attrs ? 'volume_ml' OR jsonb_typeof(attrs->'volume_ml') = 'number');
INSERT INTO product VALUES (104, 'Стакан', '{"volume_ml": "много"}');
ERROR:  new row for relation "product" violates check constraint "volume_is_number"
DETAIL:  Failing row contains (104, Стакан, {"volume_ml": "много"}).

В MongoDB аналог — валидация коллекции по JSON Schema. Гибкая схема ускоряет изменения, но проверки данных приходится проектировать сознательно.

Когда выбрать NoSQL, а когда SQL лучше

Задача Что обычно берут Когда иначе
Деньги, остатки, заказы с жесткими правилами Реляционную СУБД (PostgreSQL, MySQL, SQL Server) Если нагрузка превышает возможности одного кластера, смотрят распределенные SQL-базы
Кеш, сессии, лимиты запросов Ключ-значение (Redis, Valkey) Если данные нельзя терять, нужна настройка сохранения на диск или основная база
Каталог с разными атрибутами у товаров Документную базу или jsonb в PostgreSQL Если по атрибутам нужны сложные связи и отчеты — нормализованные таблицы
Миллионы событий в секунду с чтением по ключу Wide-column (Cassandra, ScyllaDB) Если нужна аналитика по всем событиям — колоночная OLAP-база
Запросы «друзья друзей», цепочки связей Графовую базу Если связей мало и глубина 1-2 шага — SQL с JOIN или рекурсивным CTE
Учебный или небольшой проект, требования не ясны Реляционную СУБД —

SQL обычно лучше, когда данные сильно связаны, важны ограничения целостности и транзакции через несколько сущностей, а запросы заранее неизвестны (отчеты, аналитика, ad hoc). NoSQL выигрывает, когда шаблон доступа известен заранее (почти всегда «по ключу»), данные удобно читать целиком одним куском, а объем или поток записи требует горизонтального масштабирования.

Частый вариант — гибрид: основная реляционная база плюс Redis для кеша. Но у каждой новой базы своя цена: эксплуатация, резервное копирование, синхронизация данных.

Недостатки NoSQL, о которых стоит знать заранее

  • Модель проектируют от запросов. В Cassandra таблицу создают под конкретный запрос, JOIN в CQL нет; новый вид запроса может потребовать новой таблицы и дублирования.
  • Привязка к продукту. API и языки запросов у систем разные, переезд почти всегда означает переписывание кода.
  • Лицензии. Не все популярные системы — классический open source: MongoDB под SSPL, Redis в 2024 году сменил лицензию, после чего появился форк Valkey.

Выводы

  • NoSQL — семейство нереляционных моделей: ключ-значение, документные, wide-column и графовые базы, а не «базы без SQL».
  • Каждая модель удобна для своего шаблона доступа: документ читается одним куском, но дублирование данных усложняет обновления.
  • CAP касается выбора при сетевом разделе, а не «двух из трех свойств всегда»; ACID есть и в части NoSQL-систем.
  • «Без схемы» означает, что схему и проверки держит приложение; ошибки данных всплывают при чтении.
  • Для связанных данных с транзакциями и неизвестными заранее запросами обычно лучше реляционная СУБД, для кеша, потоков событий и графов связей — подходящий тип NoSQL.

Где применяется / связь с практикой

Освойте тему на практике

Выбор базы дорого менять позже: от него зависят схема, код доступа и эксплуатация. На практике нужно уметь спроектировать модель под запросы в MongoDB, Cassandra или Redis, настроить репликацию и шардирование и понять, где реляционная база справится лучше. Этому посвящен курс «NoSQL» в Otus.

Освойте тему на практике

Чтобы оценить формат и разобрать отдельные темы по базам данных бесплатно, можно прийти на открытые уроки Otus.

FAQ

Можно ли писать SQL-запросы к NoSQL-базе?
Часто да, но не на стандартном SQL: у Cassandra есть CQL, похожий на SQL без JOIN, у Couchbase — SQL++, а у многих систем есть коннекторы для SQL-движков. Возможности таких языков уже, чем у реляционной СУБД.

NoSQL быстрее, чем SQL?
Не в общем случае. NoSQL-база быстра на том шаблоне доступа, под который спроектирована модель данных (обычно чтение по ключу), и может быть медленной или неудобной на произвольных запросах, которые реляционная СУБД выполнит индексами и JOIN.

С какой базы начинать изучение?
Обычно с реляционной (PostgreSQL или MySQL) и SQL: на ней проще понять ключи, транзакции и индексы. Затем имеет смысл освоить Redis и одну документную базу, чтобы видеть, от чего именно отказывается NoSQL.

OTUS Журнал
Бесплатные открытые уроки (поп-ап)