> Как хранить историю изменений цен акций в базе данных (Go)

Уровень: senior · Роль: backend · Язык: Go · Категория: Технические вопросы

Компании: BrightPattern

Стек: Go

> Пример ответа

Короткий ответ

Для хранения истории цен акций оптимальна таблица с колонками ticker, price, timestamp и первичным ключом (ticker, timestamp). Это обеспечивает уникальность записей и эффективные запросы по временным диапазонам. Для высоконагруженных систем стоит рассмотреть партиционирование по времени и агрегацию старых данных. Индексы на timestamp и ticker обязательны. В Go используйте time.Time с часовым поясом UTC и decimal для цены.

Подробное объяснение

Основной trade-off - между гранулярностью данных и объемом хранения. Полная история тиков может генерировать миллионы записей в день на один инструмент. Поэтому архитектура обычно включает:

  • Таблица тиков (raw data) - для последних N дней, с retention policy.
  • Таблица свечей (OHLCV) - агрегированные данные за минуту/час/день для долгосрочного хранения.
  • Таблица снапшотов - ежедневные снимки состояния (опционально).

Ключевые решения:

  • Тип цены: NUMERIC(18, 6) или DECIMAL - никогда не используйте float/double из-за ошибок округления.
  • Временная метка: TIMESTAMPTZ в PostgreSQL или DATETIME(6) в MySQL, всегда UTC.
  • Первичный ключ: композитный (ticker, timestamp) - предотвращает дубликаты и ускоряет запросы по конкретному инструменту.
  • Партиционирование: по месяцам или неделям для облегчения удаления старых данных и ускорения сканирования.
  • Сжатие: для старых данных можно использовать columnar storage (ClickHouse) или TimescaleDB.

На практике

Для Go-приложения с PostgreSQL:

  1. Используйте pgx или database/sql с пулом соединений.
  2. Вставку делайте батчами (например, 1000 записей) через COPY или multi-row INSERT.
  3. Для агрегации используйте материализованные представления или фоновые job'ы (например, через pg_cron).
  4. Настройте retention policy: удаление данных старше 90 дней из таблицы тиков, перенос в архив.

Пример схемы:

SQL
CREATE TABLE price_ticks (
ticker VARCHAR(10) NOT NULL,
price NUMERIC(18, 6) NOT NULL,
ts TIMESTAMPTZ NOT NULL,
PRIMARY KEY (ticker, ts)
) PARTITION BY RANGE (ts);
CREATE TABLE price_ticks_2025_01 PARTITION OF price_ticks
FOR VALUES FROM ('2025-01-01') TO ('2025-02-01');

Пример кода

Вставка батчем в Go:

GO
func InsertPrices(ctx context.Context, db *sql.DB, ticker string, prices []PricePoint) error {
tx, err := db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer tx.Rollback()
stmt, err := tx.PrepareContext(ctx,
`INSERT INTO price_ticks (ticker, price, ts) VALUES ($1, $2, $3)`)
if err != nil {
return err
}
defer stmt.Close()
for _, p := range prices {
if _, err := stmt.ExecContext(ctx, ticker, p.Price, p.Timestamp); err != nil {
return err
}
}
return tx.Commit()
}

Запрос последней цены:

GO
SELECT price FROM price_ticks
WHERE ticker = $1 AND ts <= $2
ORDER BY ts DESC LIMIT 1;

Как отвечать на собеседовании

Начните с базовой схемы, затем уточните требования: частота обновления, объем данных, типы запросов. Покажите понимание trade-off между точностью и производительностью. Упомяните партиционирование, индексы и агрегацию. Если спросят про high-load - предложите TimescaleDB или ClickHouse. Подчеркните важность decimal для денежных значений и UTC для времени.

Что проверяет интервьюер

  • Понимание нормализации и денормализации.
  • Умение проектировать схему под нагрузку.
  • Знание особенностей временных рядов (time-series).
  • Владение SQL (индексы, партиции, оконные функции).
  • Практический опыт с Go и БД.

Типичные ошибки

  • Использование float для цен - потеря точности.
  • Отсутствие композитного PK - дубликаты.
  • Индекс только на timestamp без ticker - медленные запросы по конкретному инструменту.
  • Хранение всех данных без агрегации - быстрый рост БД.
  • Игнорирование часовых поясов - смещение времени при анализе.
  • Вставка по одной записи в цикле - низкая производительность.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью