ДЕМО Что сделано, замеры 500 тыс. и 10 млн, архитектура Открыть отчёт →
+7 (831) 414-29-83 Пн–Сб 9:00–18:00
инженерный кейс · 10 млн SKU

Когда PostgreSQL
перестаёт успевать

Каталог автозапчастей на 10 млн позиций: замерили, нашли узкое место, вынесли поиск в Meilisearch. Ниже — цифры, реализация и честные ограничения.

12,5 мсp50 на 10 млн
против 4,35 с у PostgreSQL
p95, 10 клиентов40 мс
Индекс13,2 ГБ
Индексация1 ч 23 мин
задача

Что было на входе

Данные

100 поставщиков, разные CSV/XLSX/XML, цены, фото на чужих CDN, опечатки и разные названия одной детали.

Объём

Целевые 500 тыс. позиций. Крайний случай — 10 млн. Демо жило на 2 vCPU и 1,4 ГБ, поэтому 10 млн проверялись в отдельной лаборатории.

Ограничения

Без Bitrix и платных CMS. Go, PostgreSQL, бесплатный поиск. SEO, быстрый каталог, заказы и админка.

Что нельзя

Нельзя хранить оригиналы фото на сервере. Нельзя отдавать 10 млн с одной базы без деградации поиска.

диагностика

Где именно болит

Сначала измерили каждый тип запроса, а не «оптимизировали всё».

Запрос на 10 млнp50Что происходит
Страница марки, 24 товара0,20 мсиндекс brand_id, price
Страница модели0,67 мсиндекс brand_id, model_id
Keyset-пагинация0,21 мсвместо OFFSET
OFFSET 100000146 мсперебирает и выбрасывает строки
Фасеты марок462 мсагрегат по всей таблице
Поиск «бампер»: count2,68 с1 млн совпадений
Поиск «бампер»: топ-246,55 сts_rank_cd по миллионам строк

Вывод: каталог по марке/модели/группе масштабируется на PostgreSQL. Болит только полнотекстовый поиск: rank и count по миллионам совпадений.

решение

Разделили ответственность

БраузерHTML + CSSSEO, без SPA
→
GoПриложениекаталог, импорт, заказы
→
PostgreSQLИсточник истиныцена, наличие, заказы
MeilisearchПоисковый индекстекст, префиксы, опечатки
S3 + CDNФотоWebP, без оригиналов на сервере
PostgreSQL — истина.Цена и остаток всегда из базы. Индекс не транзакционный.
Meili — только поиск.Возвращает ID и total. Карточки собираются из PostgreSQL.
Fallback.Meili упал → поиск идёт в PostgreSQL, сайт не отдаёт 500.
реализация

Что написано, а не нарисовано

01HTTP-клиент Meili

Индекс, настройки полей, батчи, ожидание задач, поиск с фильтрами и сортировкой. Без SDK.

02Индексатор

cmd/searchindex: полная переиндексация PostgreSQL → Meili батчами, restartable.

03Интеграция

SEARCH_BACKEND, CardsByIDs, сохранение порядка, fallback на PostgreSQL.

04Дельта после импорта

IndexRun(runID): индексируются только изменённые строки, архивные удаляются.

05Метрики

/metrics: p50/p95 HTTP, поиск, ошибки, пул БД, кэш, количество товаров.

06Лаборатория

cmd/bench + cmd/searchlab: 10 млн строк, одинаковый запрос, p50/p95/QPS.

замеры

10 млн: до и после

Один и тот же запрос «бампер» с 1 млн совпадений. Не среднее по редким словам.

PostgreSQL было

count2,68 с
топ-24 + rank6,55 с
count + top в одном тесте4,35 с

Rank по 1 млн совпадений. Индексы не спасают: нужно читать и сортировать совпадения.

Meilisearch стало

p50, 1 клиент7,3 мс
p50, 20 клиентов12,5 мс
p95, 10 клиентов40,0 мс
QPS, 10 клиентов764
QPS, 20 клиентов599
Клиентовp50p95QPSКомментарий
17,3 мс7,8 мс136чистая latency
1010,2 мс40,0 мс764рабочий режим
2012,5 мс114,3 мс599p95 начинает расти
50148,4 мс398,4 мс286перегруз на этом железе

Честно: 50 клиентов на одном поисковом узле — уже деградация. Для SLA нужен отдельный узел, запас CPU и, при росте, шардирование. Кейс показывает не «магию», а точку перегруза.

эксплуатация

Что происходит при сбоях

Meili упал

Приложение логирует ошибку и отдаёт поиск из PostgreSQL. Проверено: 200 за 3,0 с вместо 500.

Импорт идёт

Импорт пишет в PostgreSQL; после завершения вызывается дельта-индексация. Поиск не блокируется импортом.

Индекс отстал

Полная переиндексация — отдельная команда. В продакшене нужна очередь changed IDs и мониторинг расхождения.

Индекс большой

10 млн = 13,2 ГБ, 1 ч 23 мин на macOS x86_64 через Rosetta. Это отдельное окно и ресурсы, не «нажал кнопку».

границы

Что мы не делали и почему

Не тащили KubernetesОдин Go-сервис + PostgreSQL + Meili проще в эксплуатации и дешевле.
Не партиционировали «для галочки»Каталог уже 0,2–0,7 мс. Партиции нужны для обслуживания/архива, а не для этих запросов.
Не хранили оригиналы фотоТолько ссылка и WebP-превью в кэше; в продакшене S3 + CDN.
Не обещали 10 млн на 2 vCPU10 млн — lab. Демо — 500 тыс. Продакшен — отдельное железо.
стоимость

Сколько это требует

Компонент500 тыс.10 млн
Приложение2 vCPU / 1–2 ГБ4–8 vCPU / 8–16 ГБ
PostgreSQL2 vCPU / 4 ГБ8 vCPU / 32 ГБ, отдельный диск
Поискне обязателен8 vCPU / 32 ГБ, индекс 13+ ГБ
Фотодиск + кэшS3 + CDN
БэкапыежедневноPITR + бэкап индекса/переиндексация

Ориентир, не коммерческое предложение: зависит от провайдера, трафика и требований к SLA. Точная смета — после замера на целевом железе.

воспроизводимость

Как проверить

make lab-up go run ./cmd/bench -dsn "$BENCH_DSN" -seed 10000000 go run ./cmd/searchlab -dsn "$BENCH_DSN" -index -bench SEARCH_BACKEND=meilisearch go run ./cmd/server

Подробно: lab/README.md, docs/MEILI.md, docs/CASE-JOURNAL.md. Код и датасет — синтетика, без прайсов заказчика.

подбор детали

Не нашли нужную запчасть?

Оставьте заявку: подберём деталь по VIN, артикулу или названию и вернёмся с ценой и фото.

Проверка перед отправкойОтвет в течение рабочего дняОтправка по России
Нажимая кнопку, вы соглашаетесь с политикой конфиденциальности.