Хранение истории цен конкурентов: схемы БД и временные ряды
Как хранить историю цен конкурентов в PostgreSQL: схемы таблиц, сжатие с TimescaleDB и построение аналитики для мониторинга рынка.Мониторинг цен конкурентов — критически важная задача для любого бизнеса, работающего в e-commerce, розничной торговле или на маркетплейсах. Объёмы собираемых данных могут достигать миллионов строк в месяц, и их эффективное хранение, сжатие и анализ напрямую влияют на скорость принятия решений. При построении собственного решения или использовании готового сервиса парсинга цен важно заранее продумать архитектуру базы данных, способную справляться с высокой интенсивностью записи и обеспечивать быстрые аналитические запросы по историческим данным.
Особенности хранения ценовых данных
Цены, собранные с сайтов конкурентов, представляют собой классический пример временного ряда. Каждая запись имеет временную метку, идентификатор товара и числовое значение цены. Помимо цены могут фиксироваться: наличие товара, валюта, акционная цена, регион доставки. Ключевые характеристики такого потока данных:
- Высокая частота поступления — парсеры могут обновлять цены десятки раз в сутки для одного товара;
- Преобладание операций вставки — данные постоянно дописываются, удаления и обновления редки;
- Аналитика «по окнам» — чаще всего запрашиваются изменения за день, неделю, месяц;
- Необходимость сжатия старых данных — для экономии места и ускорения запросов.
Игнорирование этих особенностей приводит к раздуванию базы, падению производительности и высоким затратам на хранение. Поэтому инженеры ESK Solutions при разработке SaaS-решений для мониторинга цен всегда опираются на реляционные СУБД с поддержкой временных рядов и применяют облачные технологии для гибкого масштабирования.
Схемы базы данных на PostgreSQL
Классический подход — использование нормализованной структуры с отдельными таблицами для товаров, конкурентов и непосредственно цен. Примерная схема:
Таблица products
- product_id (UUID или BIGINT) — уникальный идентификатор;
- sku, название, категория, url на сайте конкурента;
- metadata (JSONB) — дополнительные атрибуты.
Таблица competitors
- competitor_id, название, домен, признак активности.
Таблица price_snapshots — основная таблица временного ряда
- snapshot_id (BIGINT, первичный ключ), timestamp (TIMESTAMPTZ), product_id, competitor_id;
- price (NUMERIC), currency (VARCHAR), in_stock (BOOLEAN), promo_flag (BOOLEAN);
- raw_data (JSONB) — полный ответ парсера (может пригодиться для аудита).
Для высоконагруженных систем такую таблицу необходимо секционировать по времени (например, по месяцам или неделям). PostgreSQL с версии 10 предлагает декларативное партиционирование, что упрощает управление и ускоряет запросы за счёт обрезания секций (Partition Pruning).
Индексация и оптимизация
Нельзя создать один индекс на все поля: это замедлит вставку. Рекомендуется минимальный набор:
- Составной индекс (product_id, timestamp) — покрывает типовые запросы «цена товара за период»;
- Индекс на timestamp для очистки старых данных и временных срезов;
- Частичный индекс на in_stock для фильтрации товаров в наличии.
При использовании JSONB стоит избегать сканирования всего документа — лучше вынести часто запрашиваемые поля, как показано выше. Система веб-аналитики сможет использовать эти индексы для построения дэшбордов без лишних задержек.
Сжатие и временные ряды: TimescaleDB и нативная компрессия
Даже при партиционировании объём данных растёт линейно. На помощь приходит расширение TimescaleDB — надстройка над PostgreSQL, превращающая его в полноценную time-series базу данных. Ключевые возможности:
- Автоматическое разбиение на chunks (временные сегменты) вместо ручного партиционирования;
- Встроенные гипертаблицы с понятным синтаксисом;
- Нативная компрессия сжатием сегментов (до 90-98% экономии места);
- Непрерывные агрегаты (Continuous Aggregates) — материализованные представления, обновляемые инкрементально;
- Политика автоматического сжатия старых данных и удаления (Retention Policy).
Пример создания гипертаблицы из price_snapshots:
SELECT create_hypertable('price_snapshots', 'timestamp');
Затем включается сжатие для сегментов старше 7 дней:
ALTER TABLE price_snapshots SET (timescaledb.compress);
SELECT add_compression_policy('price_snapshots', INTERVAL '7 days');
При этом запросы к сжатым данным выполняются прозрачно. Такая архитектура отлично работает в составе SaaS-решений для мониторинга цен, где важно обеспечить быстрый отклик при любом окне выборки.
Пример оценки сжатия
Допустим, в день загружается 5 млн записей о ценах, что даёт 150 млн строк в месяц. Без сжатия каждая запись может занимать ~100 байт, итого 15 ГБ в месяц. После применения TimescaleDB-компрессии объём снижается до 0.3–0.5 ГБ. Экономия на дисковом пространстве и ускорение full-scan запросов делают такое решение экономически оправданным.
Аналитика на основе истории цен
Собранные данные — это лишь сырьё. Бизнес-ценность появляется после применения аналитических инструментов. Основные сценарии:
- Вычисление средневзвешенной цены конкурента за период;
- Выявление частоты и глубины скидок;
- Сравнение собственных цен с рынком для динамического ценообразования;
- Построение трендов и прогнозов.
В PostgreSQL это реализуется оконными функциями и агрегатами. Например, запрос, показывающий изменение цены относительно предыдущего снимка:
SELECT price - LAG(price) OVER (PARTITION BY product_id ORDER BY timestamp) AS price_diff ...
Если база развёрнута в облаке с автоскейлингом, нагрузка от сложных аналитических запросов не мешает оперативной обработке — опыт облачных сервисов ESK подтверждает, что отделение вычислительных ресурсов под аналитику с помощью read-реплик предотвращает конфликты.
Часто задаваемые вопросы
Можно ли хранить историю цен без специализированных расширений, используя обычный PostgreSQL?
Да, для небольших объёмов (до нескольких десятков миллионов записей) достаточно встроенных средств: партиционирование, правильные индексы и агрессивный VACUUM. Однако при росте данных вы неизбежно столкнётесь с деградацией производительности, и добавление TimescaleDB станет логичным шагом.
Как часто нужно запускать сжатие временных рядов?
Оптимально настраивать политику автоматического сжатия сегментов, например, через 7 дней после закрытия временного отрезка. В активных системах можно сжимать ежедневно по расписанию. Главное — не сжимать сегменты, в которые ещё может идти запись: TimescaleDB блокирует это автоматически.
Влияет ли сжатие на точность аналитики?
Нет. Компрессия TimescaleDB применяет lossless-алгоритмы, а непрерывные агрегаты пересчитывают точные значения. Ни одно числовое значение цены не округляется и не теряется.
Cтоит ли выносить горячие данные в оперативную память?
Да, для часто запрашиваемых периодов имеет смысл держать последние недели в таблицах без сжатия, а всё, что старше — в сжатом виде. Так вы получаете баланс между скоростью и стоимостью хранения. Мы помогаем проектировать такие решения в рамках услуг по комплексному парсингу и хранению цен.


