13 авг. 2025 г.·7 мин чтения

ClickHouse vs Postgres для аналитики продуктовых событий

ClickHouse и Postgres оба могут подойти для аналитики событий продукта. Узнайте, когда Postgres хватает, когда помогает ClickHouse и что переносить в первую очередь.

ClickHouse vs Postgres для аналитики продуктовых событий

Почему команды слишком рано копируют события

Многие команды копируют каждое продуктовое событие во вторую базу данных задолго до того, как это действительно нужно. Это кажется безопаснее. Если трафик вырастет позже, они думают, что будут готовы.

На бумаге это звучит разумно. На практике это часто означает больше движущихся частей ещё до того, как появилась настоящая проблема.

Обычно всё начинается с одного медленного дашборда, пары постов про колоночные хранилища и быстрого вывода: каждый клик, просмотр и вызов API нужно с самого начала отправлять в отдельный аналитический стек.

На деле движет этим чаще страх, а не нагрузка. Команды боятся, что однажды упрётся в потолок, и строят вокруг этого будущего потолка уже сейчас.

Цена этого проявляется быстро. Вторая система данных означает вторую задачу по очистке. Кому-то нужно синхронизировать схемы, разбираться с опоздавшими и дубль-ивентами, следить за загрузкой и объяснять, почему один дашборд не совпадает с другим.

Сбои тут скучные, но они отнимают время:

  • названия событий расходятся между сервисами
  • ID пользователей приходят раньше, чем синхронизируются записи аккаунтов
  • повторные попытки создают дубликаты
  • догрузки меняют старые итоги
  • дашборды отстают от актуальных данных продукта

Ничего из этого не помогает команде быстрее отвечать на продуктовые вопросы. Это просто создаёт больше мест, где данные могут сломаться.

Команды ещё и слишком много строят заранее, потому что боль пока кажется абстрактной. Они представляют миллионы событий и сложную аналитику, хотя реальные вопросы обычно куда проще: какой функцией пользуются платящие клиенты? Где отваливаются пользователи на пробном тарифе? Что изменилось после релиза на прошлой неделе?

На такие вопросы редко отвечают только события. Обычно нужны ещё пользователи, аккаунты, тарифы, эксперименты, история обращений в поддержку или статус подписки. Postgres хорошо справляется с таким набором, потому что данные уже лежат там и остаются актуальными.

Поэтому копировать все события по умолчанию — часто не лучший первый шаг. Если Postgres всё ещё отвечает на те вопросы, которые команда действительно задаёт, вторая система в основном добавляет накладные расходы. Переносите нагрузку только тогда, когда она действительно перерастает реляционные запросы, а не потому, что рост может случиться когда-нибудь потом.

Что Postgres уже умеет хорошо

Postgres может покрывать аналитику продуктовых событий гораздо дольше, чем многие ожидают. Если события лежат рядом с пользователями, аккаунтами, подписками и биллингом, можно ответить на многое одним запросом и из одного источника истины.

Это важно, потому что продуктовые вопросы редко остаются простыми. Продакт-менеджер начинает с вопроса: "Кто пользовался функцией X на прошлой неделе?" Потом появляется продолжение: какие аккаунты платящие, какие пользователи пришли с пробного периода и изменилось ли использование после неудачного счёта.

В Postgres такие соединения делать просто, потому что бизнес-данные уже там.

Небольшие воронки тоже хорошо подходят. Если вы отслеживаете ограниченный набор событий вроде регистрации, создания первого проекта, приглашения коллеги и завершения апгрейда, Postgres без лишней сложности покажет, где люди отваливаются. Для SaaS на ранней стадии этого обычно достаточно, чтобы заметить реальные проблемы с онбордингом или ценами.

Ежедневная и еженедельная отчётность тоже нормально работает в Postgres. Большинству команд не нужны дашборды в секунду на каждое событие. Им нужен надёжный взгляд на активных пользователей, конверсию, использование функций по тарифам и несколько трендов для еженедельной встречи. Эту задачу часто закрывают пара сводных таблиц или запланированных запросов.

Срезы по удержанию и использованию функций тоже могут оставаться управляемыми. Если вы хотите сравнить удержание на первой неделе по месяцу регистрации или посмотреть, какие тарифы чаще используют новую функцию, Postgres обычно справляется, пока объёмы разумные, а вопросы знакомые.

Для маленькой команды меньше движущихся частей — это важнее, чем принято говорить вслух. Если аналитика живёт в Postgres, значит, одна база для бэкапа, один набор прав и меньше синхронизаций, которые ломаются в 2 часа ночи.

Это экономит реальное время. Стартапу с тремя инженерами обычно больше пользы приносят понятные названия событий, полезные индексы и несколько надёжных отчётов, чем слишком раннее копирование каждого клика в другую систему. Одна база, которую команда понимает, часто лучше, чем более сложная схема, которой почти никто не пользуется.

Где начинает помогать ClickHouse

ClickHouse оправдывает себя, когда данные событий перестают вести себя как данные приложения и начинают вести себя как длинный журнал действий пользователей.

Если вы храните сырой поток событий месяцами или годами, строите отчёты на больших промежутках времени и постоянно задаёте одной и той же таблице много разных вопросов, Postgres может начать ощущаться тяжёлым.

Этот переход обычно становится заметным, когда количество строк растёт с миллионов до сотен миллионов, а потом до миллиардов. Посчитать регистрации по источнику за последние семь дней в Postgres ещё может быть нормально. А вот разбить 12 месяцев событий по стране, устройству, тарифу, версии приложения, кампании и флагу функции — это уже совсем другая нагрузка.

ClickHouse особенно помогает, когда поток событий простой. События приходят, вы их сохраняете, а потом в основном читаете. Старые строки вы не обновляете постоянно. Вы просто добавляете записи и быстро сканируете их.

Он также полезен, когда много людей одновременно читают одни и те же сырые события. Продукт, маркетинг, поддержка и основатели могут все искать ответы в одной и той же таблице, но с разными фильтрами. Широкие таблицы событий с колонками вроде user_id, team_id, event_name, timestamp, device, region и properties становятся дорогими для сканирования в Postgres, когда вырастают слишком сильно.

Обычно в эту сторону указывают несколько признаков:

  • дашборды много раз в день сканируют большие временные диапазоны
  • запросы по сырым событиям начинают замедлять обычные запросы приложения
  • аналитикам нужна быстрая фильтрация по многим колонкам событий
  • большинство событий хранятся для истории, а не для точечных обновлений строк
  • схема событий остаётся довольно стабильной, даже если таблица широкая

Самый тревожный сигнал — когда аналитическая нагрузка начинает вредить самому продукту. Если отчёт за вчерашние события замедляет checkout, login или API-запросы, у вас проблема с разделением нагрузок. ClickHouse даёт этим тяжёлым сканам свой собственный дом.

Но это всё равно не значит, что каждое событие нужно переносить туда в первый же день. Это значит, что ClickHouse начинает иметь смысл тогда, когда анализ сырых событий становится постоянной тяжёлой задачей на чтение, а ваша основная база за это платит.

Простой способ принять решение

Большинство споров о ClickHouse vs Postgres идут не туда, потому что команды сравнивают базы до того, как измерят реальную нагрузку.

Начните с двух простых чисел: количество событий и окно хранения. Если ваш продукт создаёт 300 000 событий в день, а вы храните их 60 дней, Postgres может оставаться комфортным решением. Если у вас 50 миллионов событий в день и хранение на 18 месяцев, объём и форма запросов гораздо раньше подтолкнут вас к колоночному хранилищу.

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

Если большинство отчётов смотрят на недавние данные с понятными фильтрами, Postgres может выдержать больше, чем многие ожидают. Если люди постоянно сканируют огромные временные диапазоны и группируют миллиарды строк, ClickHouse начинает выглядеть более разумно.

Не полагайтесь на интуицию. Измерьте самые медленные запросы в Postgres на реальных данных. График, который загружается за три секунды, раздражает, но терпим. Запрос, который выполняется 70 секунд, требует особого обращения и при этом замедляет другие чтения, — уже гораздо более веский повод что-то менять.

Скрытая стоимость — это поддержка. Вторая система аналитики означает задания на загрузку, сопоставление схем, догрузки, контроль доступа, алерты и человека, который чинит сломанные пайплайны. Хранилище может быть дешёвым. Время команды — обычно нет.

Простой чек-лист помогает не расплыться:

  • посчитайте ежедневные события и срок их хранения
  • перечислите отчёты, которые люди запускают каждую неделю
  • измерьте самые медленные запросы в Postgres
  • оцените затраты на пайплайны, хранение и поддержку
  • перенесите сначала одну болезненную нагрузку

Последний шаг экономит много лишней работы. Не копируйте все события по умолчанию. Перенесите одну задачу, которая явно мешает, например длинный отчёт по удержанию или тяжёлый дашборд по использованию. Если это сокращает время запроса с минут до секунд и не создаёт еженедельной уборки, продолжайте. Если нет, значит, Postgres, скорее всего, всё ещё был правильным местом для остального.

Что переносить первым

Сначала настройте Postgres
Проверьте индексы, партиции и сводные таблицы, прежде чем добавлять новую базу данных.

Начинайте с потока событий, а не со всей схемы приложения.

В большинстве случаев первым стоит переносить то, что быстро растёт, запрашивается крупными пачками и не требует связей с кучей бизнес-таблиц. Сырые клики, просмотры страниц и сессионные события обычно подходят под это правило. Они только добавляются, растут каждый час, а аналитические запросы по ним часто читают миллионы строк, чтобы ответить на базовые вопросы вроде ежедневной активности, самых популярных страниц или удержания по неделям.

Хорошие первые кандидаты — это высоконагруженные сырые таблицы событий, трендовые отчёты по длинным диапазонам дат и сводки для дашбордов, которые люди открывают весь день.

После этого переносите повторяющуюся работу по подсчётам. Если один и тот же дашборд пересчитывает значения при каждой загрузке страницы, храните эти сводки в ClickHouse как предагрегированные таблицы. Это ускорит чтение графиков и снимет нагрузку с Postgres.

Пользователей, аккаунты, подписки, счета и другие бизнес-записи оставляйте в Postgres. Эти таблицы нужны для входа в систему, биллинга, прав доступа и логики приложения. Они обновляются на месте, требуют точных записей и опираются на обычные реляционные правила.

Если графику нужен тариф аккаунта или название компании, скопируйте небольшую справочную таблицу или простой снимок данных. Не зеркальте всю модель биллинга только потому, что одному аналитическому запросу нужны дополнительные подписи. Такое решение потом сильно упрощает жизнь.

Низкообъёмные отчёты оставляйте там, где они уже работают. Экран поддержки, где проверяют одного клиента, или внутренний отчёт, который раз в неделю трогает несколько тысяч строк, не нуждается во второй базе данных.

Для первого шага достаточно чёткого разделения: операционные данные остаются в Postgres, тяжёлые сканы событий и сводки для дашбордов переезжают в ClickHouse, и на этом всё — пока не появится реальное узкое место.

Пример: SaaS-команда с растущим объёмом событий

Маленькая SaaS-команда обычно начинает с одной базы, и чаще всего это Postgres. В нём уже лежат пользователи, аккаунты, подписки и продуктовые события, поэтому первые дашборды собрать легко. Ежедневная активность, воронки регистрации и использование функций по аккаунтам находятся рядом с данными приложения.

Такое решение работает какое-то время. Проблема начинается тогда, когда объём событий растёт намного быстрее, чем потребности в отчётности, под которые строили исходную схему.

Представьте продукт, который вырос с 3 миллионов событий в месяц до 60 миллионов после добавления большего количества in-app-аналитики. Продакт-менеджеры теперь хотят смотреть не только прошлую неделю, но и 90-дневные и 180-дневные окна. Они просят удержание по когортам, тренды использования по функциям и длинные разрезы для встреч с советом.

Данные всё ещё лежат в Postgres, но сырые запросы к событиям начинают тормозить, конкурировать с обычной нагрузкой приложения и просто надоедают в поддержке.

В этот момент команде обычно не нужна полная миграция. Именно здесь многие создают себе лишнюю работу.

Частичного разделения часто достаточно. Postgres оставляет у себя транзакционные данные и бизнес-логику на уровне аккаунта. ClickHouse получает поток только добавляемых событий и берёт на себя тяжёлые чтения по длинным диапазонам времени.

На практике это часто значит следующее:

  • ClickHouse отвечает на вопросы вроде ежедневного количества событий, использования функций за шесть месяцев и анализа пути пользователя на сотнях миллионов строк.
  • Postgres по-прежнему отвечает на вопросы, связанные с бизнес-записью: какие платящие аккаунты апгрейдились, оттекли или относятся к определённому тарифу.
  • Когда кому-то нужны оба типа данных, команда соединяет их на более высоком уровне, обычно по account ID или через подготовленную сводную таблицу, вместо того чтобы сканировать всё в одном месте.

Так изменение остаётся небольшим. Приложению не нужен новый источник истины для клиентов или биллинга. Аналитическая часть получает более быстрые запросы по длинным периодам. Инженерам не приходится копировать все таблицы только потому, что аналитика событий выросла.

Обычно это и есть более чистый ход: перенести ту нагрузку, которая переросла Postgres, и оставить остальное как есть.

Ошибки, которые создают лишнюю работу

Исправьте медленные запросы аналитики
Разберитесь, что лучше подходит: индексы, сводные таблицы или отдельное хранилище событий.

Большинство команд сначала упираются не в лимит базы данных. Сначала они упираются в ошибку планирования.

Одна частая ошибка — копировать все события, а иногда и все связанные таблицы, во вторую систему ещё до того, как кто-то определил, какие отчёты действительно важны. Это удваивает загрузку, изменения схем, мониторинг и отладку. Если команде нужны только ежедневная активность, провал воронки и несколько срезов по удержанию, широкий пайплайн копирования часто оказывается напрасной работой.

Ещё одна дорогая привычка — пытаться восстановить текущее состояние продукта только из сырых событий. События рассказывают, что произошло. Но они не всегда показывают, что истинно прямо сейчас. Если вам нужен текущий тариф пользователя, размер команды или статус аккаунта, храните это состояние в базе приложения и соединяйте при необходимости. Попытка собрать его заново из месяцев событий обычно превращает простые вопросы в хрупкую логику.

Свои проблемы создаёт и качество событий. В реальных системах бывают дубликаты. Мобильные клиенты отправляют данные с задержкой. Фоновые задачи падают и пропускают события. Если никто не продумал, что делать с дубликатами, опоздавшими или пропавшими данными, каждый график превращается в спор.

Худший вариант — когда Postgres и ClickHouse считают одну и ту же метрику по-разному. Один запрос считает созданные аккаунты. Другой — активированные аккаунты. Третий исключает внутренних пользователей, но только в одной базе. После этого команда перестаёт спорить о поведении продукта и начинает спорить о SQL.

Прежде чем добавлять ещё один движок, разберитесь с базой:

  • определите небольшой набор бизнес-метрик простым языком
  • решите, как обрабатывать дубликаты и поздние события
  • храните актуальное состояние бизнеса отдельно от сырой истории событий
  • оптимизируйте медленные запросы Postgres и добавьте нужные индексы

У Postgres часто больше запаса, чем команды думают. Более удачная схема, сводки, партиционирование или несколько материализованных представлений могут убрать ту самую боль, из-за которой команда и смотрела в сторону ClickHouse.

Когда вторая система всё-таки нужна, переносите самую болезненную нагрузку. Не переносите всё только потому, что можете. Так аналитический стек остаётся меньше, цифры — согласованнее, а команда — вне режима постоянной уборки.

Быстрые проверки перед тем, как что-то переносить

Спланируйте аналитику событий правильно
Определите, что оставить в Postgres, а что перенести позже.

Начните с медленного отчёта, а не с новой базы данных. Один недостающий индекс, одна материализованная сводка или один ночной rollup могут превратить болезненный запрос в приемлемый.

Команды часто обвиняют Postgres, когда настоящая проблема — это широкий скан таблицы, слишком много join-ов или дашборд, который просит намного больше деталей, чем кто-то реально читает.

Также проверьте, нужны ли вообще сырые события. Если команда в основном смотрит ежедневные регистрации, использование функций по дням или еженедельное удержание, возможно, достаточно ежедневных итогов. Сырые события нужны тогда, когда людям нужны глубокие провалы в детали, повтор воспроизведения, отладка или свободный анализ. Если никто не использует такой уровень детализации, копировать каждое событие во вторую систему — это лишняя работа без заметной отдачи.

Важна и ответственность. Кто-то должен запускать загрузку, обрабатывать повторы, чинить плохие пакеты и догружать пропущенные данные после бага. На бумаге это кажется мелочью. Но это быстро надоедает, когда мобильные клиенты пересылают события повторно, timestamps приходят в неправильной временной зоне, или новое поле появляется посреди недели и ломает отчёт.

Самое сложное — не первый импорт. Самое сложное — сохранить две системы согласованными через месяц. Маленькие команды обычно чувствуют эту цену раньше, чем получают ощутимую выгоду.

Задайте себе пять простых вопросов:

  • можно ли сначала исправить медленный отчёт одним индексом, изменением партиционирования или сводкой?
  • люди запрашивают сырые события или в основном читают дневные и недельные итоги?
  • кто отвечает за загрузку, повторы, дедупликацию и догрузки, если данные ломаются?
  • сэкономит ли вторая система достаточно времени инженеров каждый месяц, чтобы окупить поддержку?
  • может ли команда чётко объяснить, что остаётся в Postgres и почему?

Если последний ответ расплывчатый, остановитесь. Postgres по-прежнему должен хранить транзакционные данные, свежие продуктовые просмотры и отчёты, которые зависят от актуальных реляционных join-ов. Граница важнее, чем числа бенчмарков.

Что делать маленькой команде дальше

Маленькие команды выигрывают, когда относятся к аналитике как к любому другому продуктовому решению: сначала решают одну реальную проблему, потом расширяются.

Если вы застряли на выборе ClickHouse vs Postgres, не начинайте с построения полноценного двухбазового пайплайна. Начните с одного отчёта или запроса, который уже кажется медленным, дорогим или неудобным в поддержке.

Назначьте одного владельца этого теста. Один человек должен решать, что измеряем, кто проверяет результаты и когда тест заканчивается. Без такого владельца команды расползаются в настройках, и никто не может сказать, помог ли переход.

До того как трогать поток данных, задайте одну метрику успеха. Сделайте её простой. Например, запрос по недельному удержанию должен выполняться меньше чем за 10 секунд. Или команда должна перестать ждать 15 минут обновления дашборда по использованию продукта. Чёткая цель не даёт проекту превратиться в «перенесём всё на всякий случай».

Короткий чек-лист помогает держаться земли:

  • выберите одну нагрузку, например анализ воронки или удержания по месяцу регистрации
  • назначьте одного владельца теста
  • запишите одну метрику успеха с числом и сроком
  • зафиксируйте названия метрик и определения событий до начала теста
  • проверьте результаты через несколько недель

Последовательность важнее, чем многие думают. Если на одном дашборде "активный пользователь" означает одно, а на другом — немного другое, никакая база данных не уберёт путаницу. Запишите, что значит каждое событие, что значит каждое свойство и когда метрика считается, а когда нет.

Через несколько недель посмотрите на три вещи: скорость запросов, общую стоимость и объём поддержки. Скорость заметить легко. Стоимость включает хранение, вычисления и время инженеров. Поддержка — скрытый фактор. Если новая схема экономит 20 секунд на запросе, но добавляет два часа поддержки в неделю, это обычно плохая сделка.

Если выбор всё ещё кажется неочевидным, короткий архитектурный разбор может сэкономить много лишней работы. Oleg Sotnikov через oleg.is консультирует стартапы и небольшие команды по лёгкой технической архитектуре, в том числе по тому, когда лучше оставить аналитику в Postgres, а когда отделить более тяжёлые нагрузки. Иногда лучший ответ — ещё немного пожить с более простым стеком.