У Tiger Data (бывший Timescale) вышла статья о десяти проблемах Elasticsearch в проде и о том, как поиск в PostgreSQL их избегает. Главный аргумент в ней верный, и о нём ниже. Но это текст вендора, который продаёт свой вариант, и примерно половина претензий к Elasticsearch описывает его состояние пятилетней давности.
Раз уж статья попадается как аргумент в спорах «выносить поиск или нет», стоит пройти по ней пункт за пунктом.
Источник — статья Tiger Data (Raja Rao DV, 30 января 2026). Там продвигаются расширения
pg_textsearch,pgvector,vectorscaleиpgai. Проверка и оценки мои; ограниченияpg_textsearchвзяты из его собственного README .
Пункт за пунктом
1. Паузы сборщика мусора JVM
Претензия: stop-the-world паузы замораживают узлы на секунды, а паузы дольше 30 секунд запускают перевыборы и каскадные отказы. PostgreSQL написан на C, сборщика мусора там нет.
Вердикт: преувеличено. Длинные GC-паузы — классическая беда старых версий и неправильно выставленной кучи. Современный Elasticsearch с G1 и кучей не больше ~31 ГБ (чтобы сохранить сжатые указатели) такими паузами почти не страдает. А у PostgreSQL свои «заморозки»: всплески при чекпоинтах, конкуренция за блокировки, долгие транзакции, мешающие vacuum. Отсутствие GC не означает отсутствия пауз. Про чекпоинты подробнее — в разборе записи.
2. Взрыв маппинга
Претензия: автоматическое определение полей создаёт маппинг на каждый уникальный ключ, и состояние кластера раздувается. В PostgreSQL — колонка JSONB и индексы по нужным путям.
Вердикт: справедливо, но неполно. Проблема реальная и болезненная. Но в Elasticsearch давно есть лимит index.mapping.total_fields.limit (по умолчанию 1000), режим dynamic: strict и тип flattened, созданный ровно под этот случай. JSONB — хороший ответ, однако GIN-индекс по всему документу тоже не бесплатен ни по размеру, ни по записи.
3. Слишком много или слишком мало шардов
Претензия: число шардов задаётся при создании индекса и не меняется без переиндексации.
Вердикт: частично устарело. Есть API _split и _shrink, меняющие число шардов без полной переиндексации. А для временных рядов — основного источника этой боли — есть data streams с ролловером по ILM, где размер шарда задаётся политикой, а не угадывается заранее. Для полнотекстового поиска по постоянному корпусу проблема остаётся, но «не меняется никогда» — неправда.
4. Глубокая пагинация
Претензия: страница 500 требует выбрать и отранжировать все предыдущие результаты, а index.max_result_window по умолчанию ограничивает окно 10 000 записями. В PostgreSQL жёсткого потолка у LIMIT/OFFSET нет.
Вердикт: ничья, поданная как победа. Глубокий OFFSET в PostgreSQL точно так же читает и выбрасывает все пропущенные строки — потолка нет, но и скорости нет. Правильный ответ в обеих системах одинаковый: keyset-пагинация. В PostgreSQL это WHERE id > $last, в Elasticsearch — search_after вместе с Point-in-Time API. Статья сама признаёт, что для глубоких страниц нужен keyset.
5. Split-brain и потеря данных
Претензия: при сетевом разделении несколько групп выбирают своих мастеров, а при слиянии данные теряются. В PostgreSQL один primary, а выбор лидера делают Patroni или pg_auto_failover.
Вердикт: устарело — и неловко. В Elasticsearch 7.0 (2019) слой координации кластера переписали: ручной minimum_master_nodes убран, конфигурация голосования управляется автоматически, и при трёх master-узлах классический split-brain практически исключён.
Неловкость в другом: отказоустойчивость PostgreSQL от split-brain не застрахована. Она зависит от инструмента поверх — и сама бывает источником проблемы. Буквально на днях в CloudNativePG 1.30.1 закрыли окно, в котором primary начинал принимать запись до получения лизы. Про то, как защита от split-brain устроена без внешнего DCS, — разбор GFM.
6. Отложенная видимость записей
Претензия: записи становятся видны поиску после refresh (по умолчанию раз в секунду). В PostgreSQL данные видны сразу после коммита, и BM25-индекс pg_textsearch обновляется в той же транзакции.
Вердикт: справедливо. Настоящее преимущество поиска внутри базы: транзакционная видимость без оговорок. В Elasticsearch можно ждать через refresh=wait_for, но это компромисс по производительности. Одна поправка: если поиск идёт с реплики PostgreSQL, отставание репликации возвращает ту же проблему, пусть и в другом масштабе.
7. Безопасность
Претензия: годами не было аутентификации по умолчанию, порт 9200 торчал в интернет, утечка 2019 года раскрыла 1.2 млрд записей.
Вердикт: устарело. С Elasticsearch 8.0 (февраль 2022) аутентификация и TLS включены по умолчанию. История с открытыми кластерами — реальная и поучительная, но это эпоха до 8.0. Подавать её как текущее состояние в статье 2026 года некорректно.
8. Сложность мониторинга
Претензия: сотни метрик — куча JVM, GC, пулы потоков, circuit breakers, неназначенные шарды. У PostgreSQL проще: соединения, латентность, hit ratio, отставание репликации, pg_stat_statements.
Вердикт: однобоко. У PostgreSQL своя глубина, о которой статья умалчивает: vacuum и раздувание таблиц, защита от переполнения счётчика транзакций, слоты репликации, копящие WAL, чекпоинты. Любой, кто эксплуатировал PostgreSQL под нагрузкой, не назовёт его мониторинг простым. Проще — возможно, потому что система одна, а не две. Но это уже аргумент из пункта 9.
9. Синхронизация данных
Претензия: архитектура «PostgreSQL → Kafka/Debezium → Elasticsearch» — это отставание, ломающиеся при смене схемы коннекторы и отладка через три системы. Поиск в PostgreSQL убирает конвейер целиком.
Вердикт: главный аргумент, и он верный. Вот ради чего стоило читать статью. Двойная запись и CDC-конвейеры — один из самых дорогих источников проблем в эксплуатации: рассинхронизация, которую замечают пользователи, а не мониторинг; миграции схемы в трёх местах; инциденты, где непонятно, какая из систем врёт. Если поиск можно держать в той же базе, эта категория проблем исчезает целиком.
10. Стоимость инфраструктуры
Претензия: JVM требует 16–64 ГБ на узел, минимум три узла, выделенные master/coordinating/data-узлы. PostgreSQL хватает одного узла.
Вердикт: сравнение яблок с апельсинами. Сравнивается отказоустойчивый кластер Elasticsearch с одиночным PostgreSQL. Продовый PostgreSQL с HA — это тоже два-три узла плюс инструмент выбора лидера. Честное сравнение: HA против HA. При нём разница остаётся, но уже не драматическая.
Итог по списку
| # | Претензия | Вердикт |
|---|---|---|
| 1 | GC-паузы | преувеличено |
| 2 | Взрыв маппинга | справедливо, но есть решения |
| 3 | Шарды | частично устарело |
| 4 | Глубокая пагинация | ничья |
| 5 | Split-brain | устарело с 2019 года |
| 6 | Видимость записей | справедливо |
| 7 | Безопасность | устарело с 2022 года |
| 8 | Мониторинг | однобоко |
| 9 | Синхронизация | главный аргумент |
| 10 | Стоимость | некорректное сравнение |
Два пункта из десяти — настоящие преимущества. Ещё один справедлив с оговорками. Остальные описывают Elasticsearch, которого уже нет, или сравнивают неравные вещи.
О чём статья молчит
Самое интересное — в документации продвигаемого расширения. В разделе ограничений README pg_textsearch написано:
- Фразовых запросов нет. BM25-индекс хранит частоты термов, но не их позиции, поэтому искать фразу напрямую нельзя. Предлагаемый обход — выбрать с запасом кандидатов по рангу и отфильтровать через
ILIKE. Для поискового движка это базовая возможность, и в Elasticsearch она работает из коробки. - Статистика корпуса протекает через RLS. Статистика BM25 учитывает все строки, включая скрытые политиками row-level security. Тот, кто знает термин, может судить о его частоте в недоступных строках.
PREPARE TRANSACTIONне поддерживается после создания BM25-индекса.- Только PostgreSQL 17 и 18, девятнадцатая — в режиме «по возможности».
- Фоновое уплотнение индекса требует отдельного расширения
pg_durable.
Ничто из этого не делает pg_textsearch плохим. Но в тексте, доказывающем, что PostgreSQL «избегает» проблем Elasticsearch, отсутствие фразового поиска — умолчание существенное.
Для русскоязычного контента хорошая новость: text_config принимает любую конфигурацию полнотекстового поиска PostgreSQL, включая встроенную russian со стеммером Snowball. Для базовых случаев этого достаточно. Более тонкая морфология — отдельная задача, где у Elasticsearch с его анализаторами и плагинами выбор богаче.
Когда что выбирать
Держите поиск в PostgreSQL, если:
- данные для поиска уже лежат в PostgreSQL, и главная боль — синхронизация;
- нужна транзакционная видимость: записал — сразу нашёл;
- корпус помещается на один узел (а это очень много);
- поиск — функция приложения, а не отдельный продукт: поиск по заказам, товарам, тикетам.
Оставьте Elasticsearch или OpenSearch, если:
- корпус не помещается на один узел, и нужно горизонтальное масштабирование;
- поиск — ядро продукта: сложное ранжирование, фразы, синонимы, подсветка, тонкая настройка релевантности;
- нужны тяжёлые агрегации и фасеты по большим объёмам;
- речь о логах и наблюдаемости в больших масштабах — хотя там у Elasticsearch сильный конкурент, ClickHouse.
Какие ещё варианты есть для поиска внутри PostgreSQL и какие из них можно поставить себе, а какие живут только в облаке, — в разборе TIN.
Вывод
Статья права в главном: вторая система хранения — это цена, которую платят синхронизацией, и если без неё можно обойтись, это стоит сделать. Но доказывает она это списком, где половина пунктов устарела на несколько лет, а ограничения собственного решения не упомянуты.
Решать, выносить поиск или нет, стоит по двум честным вопросам: помещаются ли данные в один PostgreSQL и нужны ли вам возможности, которых у поиска в нём пока нет — фразы в первую очередь.