Справочник DBA: базы данных изнутри читается по порядку

Статья 14.7 · раздел 14 «Консалтинг: аудит, оптимизация, миграции» · 108 из 109

Выбор СУБД под задачу: PostgreSQL по умолчанию, пока измерение не докажет обратное

опубликовано
читать
≈ 11 мин
автор
среда и версия
PostgreSQL 18.6 (Debian 18.6-1.pgdg13+2) в контейнере postgres:18 (--memory 1g --cpus 2); собственный опыт один: полнотекстовый поиск на 500 000 строк (таблица 123 МБ, GIN 22 МБ), общая машина; остальные числа взяты из опубликованных статей справочника с указанием статьи; лицензии — по LICENSE на релизных тегах и страницам сервисов на 28 октября 2026; опыт выполнен 28 октября 2026
содержание решать задачи SQL на собеседовании

Команда просит поставить ClickHouse, Redis или Elasticsearch «потому что так делают», и консультанту нужен ответ, который переживёт возражение. Каждая лишняя система — это вторая загрузка данных, вторые бэкапы, вторые доступы и ещё одна ночь дежурства, поэтому ответ строится от обратного: PostgreSQL остаётся, пока измерение на своём запросе и своём объёме не покажет, что он не вытягивает. Эта статья собирает границы, уже измеренные в справочнике, в одну матрицу и отдельно помечает те, которых никто не измерял. Метод измерения — в честном нагрузочном тестировании, снимок состояния чужой базы — в методе аудита, а как восемь находок аудита OLTP-базы дали измеримый эффект только у индексов под внешние ключи — в кейсе OLTP.

Что значит «по умолчанию»

PostgreSQL по умолчанию — не утверждение «он лучше всех», а порядок проверки: сначала возможности самой СУБД и её расширений, затем специализированная система. Дерево решений ниже задаёт три вопроса после измерения: объём, согласованность и команда. Измерение — условие входа: оценки «обычно быстрее» и «рекомендуют на конференциях» в матрицу не попадают, а клетка без публикованного числа честно называется «не измерено».

Матрица: восемь нагрузок

Строка — тип нагрузки; столбцы — вариант на PostgreSQL, специализированная система, граница (число из статьи справочника либо «не измерено») и лицензия по LICENSE на релизном теге. Даты проверки лицензий: 28.10.2026. Толкование условий — за юристом, не за таблицей.

НагрузкаPostgreSQLСпециальная системаГраницаЛицензия на 28.10.2026
OLTP, несколько регионовPostgreSQL 18, репликиCockroachDBна одной машине 3 узла дали 340 TPS против 3 301 у PostgreSQL (12.8); в нескольких зонах не измереноPostgreSQL: лицензия PostgreSQL (COPYRIGHT, REL_18_6); CockroachDB: CockroachDB Software License (LICENSE, v26.2.9)
Аналитикасекции, BRIN, matviewClickHouseQ2 и Q4 без предагрегата на 20 млн строк быстрее в 23 и 21 раз; Q2 закрывает матвью за 1,5 мс, Q4 не закрывает ничего (14.5)ClickHouse: Apache 2.0 (LICENSE, v25.3.14.14-lts)
Кэш и очередиUNLOGGED-таблица, FOR UPDATE SKIP LOCKEDRedis, ValkeyRedis и Valkey — 285–313 тыс. запросов в секунду на одном потоке (12.5); PostgreSQL на той же нагрузке не измереноRedis 8.x: на выбор RSALv2, SSPLv1 или AGPLv3 (README.md, 8.10.3); Valkey: BSD-3-Clause (12.5)
Документыjsonb + GINMongoDBпоиск по вложенному полю: 5,2–5,7 мс с GIN против 36,1–37,6 мс по таблицам (7.2); граница с MongoDB на одних данных не измеренаMongoDB: SSPL (12.6)
Временные рядыTimescaleDBInfluxDB, ClickHouseне измереноTimescaleDB: Apache 2.0 вне каталога tsl, Timescale License внутри (LICENSE, 2.31.0); InfluxDB: MIT или Apache 2.0 (v3.12.1)
Полнотекстовый поискtsvector + GINElasticsearch, OpenSearchна 500 тыс. строк слово находится за 0,13–0,30 мс против 101–108 мс у LIKE (опыт ниже); граница с Elasticsearch не измеренаElasticsearch: по умолчанию AGPLv3, SSPLv1 и Elastic License 2.0 на выбор (LICENSE.txt, v9.5.5)
Графырекурсивный CTENeo4jне измереноNeo4j: GPL версии 3 (LICENSE.txt, 2026.10.1)
ВекторыpgvectorQdrantне измереноpgvector: текст лицензии PostgreSQL (LICENSE, v0.8.7); Qdrant: Apache 2.0 (v1.20.0)

Три строки с «не измерено» — не пробел в таблице, а результат: у справочника нет опыта, который проводил бы границу для временных рядов, графов и векторов. Консультант в этом случае не называет порог, а ставит замер на данные клиента. PostGIS (геоданные) в матрицу не вошёл, потому что специализированной альтернативы здесь нет: он распространяется под GPL версии 2 (COPYING, 3.6.4).

Опыт: где у полнотекстового поиска граница с LIKE

Для одной строки матрицы границу можно получить за минуту. Таблица на 500 тыс. строк по 12 «слов» из словаря в 5 000 (распределение перекошено, как у реального текста); i % 1 равен нулю и нужен только затем, чтобы подзапрос считался для каждой строки, а не один раз.

docker run -d --name dba-choose --memory 1g --cpus 2 -e POSTGRES_PASSWORD=pg postgres:18
docker exec dba-choose psql -U postgres -Atc "select version()"
вывод
PostgreSQL 18.6 (Debian 18.6-1.pgdg13+2) on x86_64-pc-linux-gnu, compiled by gcc (Debian 14.2.0-19) 14.2.0, 64-bit
CREATE TABLE docs (id int, body text);
INSERT INTO docs
SELECT i, (SELECT string_agg('w' || (1 + (power(random(), 3) * 5000)::int), ' ') FROM generate_series(1, 12 + i % 1))
FROM generate_series(1, 500000) i;
ALTER TABLE docs ADD COLUMN tsv tsvector GENERATED ALWAYS AS (to_tsvector('simple', body)) STORED;
CREATE INDEX docs_tsv ON docs USING gin (tsv);
VACUUM ANALYZE docs;
SELECT pg_size_pretty(pg_relation_size('docs')) AS table, pg_size_pretty(pg_relation_size('docs_tsv')) AS gin;
SELECT body FROM docs WHERE id = 1;
SELECT count(*) FROM docs WHERE body LIKE '%w4321%';
SELECT count(*) FROM docs WHERE tsv @@ to_tsquery('simple', 'w4321');
вывод
123 MB|22 MB
w1479 w9 w4927 w2 w5 w1537 w2643 w1240 w1393 w561 w87 w3525
452
452

Таблица 123 МБ вместе со столбцом tsvector, индекс GIN — 22 МБ. Оба запроса нашли по 452 строки; у тебя числа будут другими, словарь случайный. Теперь план с буферами, параллельные воркеры выключены (max_parallel_workers_per_gather = 0), чтобы сравнить один процесс с одним. Каждый запрос выполнен три раза:

SET max_parallel_workers_per_gather = 0;
EXPLAIN (ANALYZE, BUFFERS, COSTS OFF, TIMING OFF) SELECT count(*) FROM docs WHERE body LIKE '%w4321%';
EXPLAIN (ANALYZE, BUFFERS, COSTS OFF, TIMING OFF) SELECT count(*) FROM docs WHERE tsv @@ to_tsquery('simple','w4321');
вывод (первый запуск каждого запроса, строки Buffers, Scan и Execution Time)
  Buffers: shared hit=3322 read=12422
  ->  Seq Scan on docs (actual rows=452.00 loops=1)
        Buffers: shared hit=3322 read=12422
  Buffers: shared hit=85
Execution Time: 103.209 ms

  Buffers: shared hit=446
  ->  Bitmap Heap Scan on docs (actual rows=452.00 loops=1)
        Buffers: shared hit=446
        ->  Bitmap Index Scan on docs_tsv (actual rows=452.00 loops=1)
              Buffers: shared hit=4
  Buffers: shared hit=31
Execution Time: 0.301 ms

Три запуска LIKE: 103,2 · 100,9 · 107,6 мс; три запуска через GIN: 0,301 · 0,174 · 0,133 мс. LIKE прошёл всю таблицу: 3322 + 12422 = 15 744 страниц; индекс открыл 4 страницы и отправил в кучу 446. Это ответ на вопрос «нужен ли для поиска отдельный сервис»: до 500 тыс. строк и поиска слова целиком — нет, хватает индекса в той же базе. Что опыт не говорит: конфигурация simple не знает морфологии, русский текст потребует словаря; ранжирование, подсказки и фасеты не проверялись; граница с Elasticsearch не измерена, поэтому в матрице она так и названа.

Три разбора выбора

1. Отчёты по событиям: переносить ли в ClickHouse. В 14.5 одна таблица на 20 млн строк прошла пять запросов в обеих системах. Запрос по суткам: 8,2 мс в PostgreSQL (секции и BRIN) против 4 мс в ClickHouse. С предагрегатом в PostgreSQL запросы по заранее известным измерениям шли за 0,11–5,5 мс. Два запроса на полном проходе без предагрегата оказались быстрее в ClickHouse: 480 против 21 мс и 3837 против 186 мс (в 23 и 21 раз). Но Q2 в PostgreSQL закрывает материализованное представление за 1,5 мс, а Q4 с миллионом групп не закрывает ничто. Точка по user_id в ClickHouse в 78 раз медленнее, чем B-tree. Решение: ClickHouse оправдан, если в списке есть запрос вроде Q4, который нельзя закрыть предагрегатом; цена — вторая загрузка (8,5 с для разового переноса, лаг CDC в статье не измерен).

2. «Нужна распределённая база». В 12.8 три узла CockroachDB и один PostgreSQL получили одни и те же скрипты pgbench. Облегчённый скрипт simple.sql (без двух горячих таблиц): 7 531 TPS у PostgreSQL против 2 530 у CockroachDB (в 2,98 раза). Скрипт tpcb.sql с двумя горячими таблицами: 3 301 против 340 TPS (в 9,7 раза), у CockroachDB 397 транзакций (3,88 %) пришлось повторять. Опыт шёл на одной машине, где сетевые раунды между узлами почти бесплатны, поэтому для кластера в нескольких зонах эти числа верхняя оценка для CockroachDB, а не сравнение. Решение: пока данные помещаются на один сервер с репликами и нет требования переживать отказ региона, распределённая SQL приносит цену без выигрыша; когда требование появляется, это уже вопрос согласованности, а не TPS.

3. Документы: jsonb или MongoDB. В 7.2 миллион событий с атрибутами в jsonb: поиск по вложенным полям с GIN-индексом 5,2–5,7 мс против 36,1–37,6 мс по таблицам, цена — 94 МБ индекса и 3,3 с построения. В 7.6 миллион заказов в MongoDB: агрегат по вложенным документам 95–102 мс, по ссылкам через $lookup 652–689 мс. Оба опыта про моделирование внутри одной системы, поэтому прямого ответа «что быстрее на одних данных» у них нет. Решение: документная модель сама по себе не повод менять СУБД; повод — требования к шардированию и лицензии, которые разбираются в 12.6 (SSPL).

Согласованность и потеря данных

Вопрос «что можно потерять» задаёт настройки раньше, чем выбор системы. В 14.2 pgbench на одной базе дал 432 TPS (разброс запусков 371–3236) при обычном COMMIT, который ждёт сброса WAL, и 14 204 TPS при synchronous_commit = off: разница в десятки раз получена настройкой, не заменой СУБД, и платит за неё потеря последних транзакций при сбое. Если потеря последних секунд недопустима, смотри синхронную репликацию, а не «систему с другой моделью согласованности». Требования по потере данных и времени восстановления задают RPO и RTO, и они одинаковы для всех систем в матрице.

Лицензии и доступность в России

Лицензии меняются: Redis прошёл путь BSD, затем RSALv2 или SSPLv1, затем с 8.0 добавил AGPLv3 (12.5); CockroachDB v26.2.9 поставляется под CockroachDB Software License, и без файла лицензии кластер пишет в каждое подключение NOTICE: No license is installed. Throttling will begin after 2026-11-04 11:41:30 +0000 UTC (12.8). Перед выбором открой LICENSE на теге той версии, которую поставишь, и запиши дату проверки.

Доступность в России проверена только по официальным страницам, 28.10.2026:

  • Yandex Cloud: страницы сервисов открываются для Managed Service for ClickHouse, Managed Service for Valkey (на странице — «опенсорс-форк Redis под лицензией BSD»), Yandex StoreDoc («полная совместимость с MongoDB») и Managed Service for OpenSearch.
  • Расширения управляемого PostgreSQL: по странице Yandex Cloud в списке есть postgis и упомянут timescaledb; провайдеры и расширения сравнены в 13.1 (pgvector у Yandex Cloud, TimescaleDB и PostGIS у VK Cloud).
  • Elasticsearch, Neo4j, Qdrant, InfluxDB и CockroachDB как сервисы российских облаков: не проверено.
  • Что лицензия или санкционные ограничения означают для конкретного клиента: не проверено, это вопрос юриста.

Команда и стоимость

Навыки команды в матрицу не вошли: числа для найма и обучения в справочнике не измерялись. Измеримая часть — стоимость сопровождения. В 13.1 управляемый PostgreSQL 8 vCPU и 32 ГБ с репликой в VK Cloud стоил 51 387,92 ₽ за 30 дней, а своя установка выгоднее, пока h · r < M − 2 · C: часы DBA в месяц на обновления, бэкапы и failover, умноженные на цену часа, меньше цены кластера за вычетом двух машин. Вторая система добавляет в h свои обновления, свой failover и свои восстановительные учения. Пока никто из команды не умеет её восстанавливать, это не выбор системы, а риск.

DBA / 01

Дерево решений: когда PostgreSQL перестаёт быть ответом

Дерево решений при выборе СУБД: объём, согласованность, команда Три вопроса идут сверху вниз. Объём: если PostgreSQL вытягивает по измерению, остаться в PostgreSQL с расширением; полнотекстовый поиск через GIN занял 0,13–0,30 мс против 101–108 мс у LIKE. Согласованность: если потеря последних секунд недопустима, PostgreSQL с синхронной репликой; коммит с ожиданием WAL дал 432 TPS, synchronous_commit off дал 14 204. Команда: если дежурить по второй системе некому, остаться в PostgreSQL и добавить материализованное представление: 0,11–5,5 мс на трёх запросах. Если ответ на все три вопроса да, выбирается специализированная система, например ClickHouse, который на запросе с миллионом групп быстрее в 21 раз. 1. Объём PostgreSQL не вытягивает по измерению? 2. Согласованность можно потерять последние секунды? 3. Команда есть кому дежурить по второй системе? Специализированная система ClickHouse: Q4 в 21 раз быстрее PostgreSQL + расширение GIN: 0,13–0,30 мс, LIKE: 101–108 мс PostgreSQL, синхронная реплика ожидание WAL: 432 против 14 204 TPS Остаться в PostgreSQL ещё один matview: 0,11–5,5 мс на Q1–Q3 нет нет нет да да да Ответ «да» ведёт вниз, «нет» — вправо; каждое число из 14.2, 14.5 и опыта выше.
Специализированная система остаётся последней веткой: до неё запрос должен не вытянуться по измерению, допускать потерю последних секунд и иметь команду, которая сможет восстановить вторую систему.

Что из этого следует

  • Граница между PostgreSQL и специализированной системой проходит по запросу, а не по названию нагрузки: в 14.5 четыре запроса из пяти PostgreSQL закрывает сам (Q1–Q3 с предагрегатом, Q5 через B-tree), и только Q4 с миллионом групп требует полного прохода.
  • Настройка и расширение дешевле второй системы: разница 432 и 14 204 TPS в 14.2 получена параметром, а tsvector + GIN сократил поиск по 500 тыс. строк со 101–108 до 0,13–0,30 мс.
  • Клетка «не измерено» в матрице означает задачу для замера на данных клиента, а не приговор в пользу какой-либо стороны.
  • Лицензия и доступность проверяются на теге нужной версии и фиксируются датой; сменить их может любой релиз.

Чек-лист

  • Выпиши нагрузки клиента и отнеси каждую к строке матрицы; для строк «не измерено» поставь замер на копии его данных (метод — честный бенчмарк).
  • Для каждой границы сравни время и объём чтения по EXPLAIN (ANALYZE, BUFFERS), а не только миллисекунды.
  • Реши, что допустимо потерять, до выбора системы: RPO и RTO задают настройку репликации раньше, чем марку.
  • Открой LICENSE на теге версии, которую будешь ставить, и запиши дату; доступность у облачного провайдера проверяй по его официальной странице.
  • Убери стенд вместе с томом: docker rm -f -v dba-choose.

Источники

Выжимки статей в Telegram: одна ловушка, код с выводом и ссылка на полный разбор. t.me/koddo_ru