Как решать задачу суммирования revenue на большом объеме данных
Что проверяют
Понимаете ли вы, как масштабируется агрегация: где узкое место, в вычислениях, чтении с диска или сети, и как не потерять точность денег. Следом обычно спрашивают про перекос ключей и про пересчёт, когда приходят поздние данные.
Как отвечать
Сначала оцените объём. Десятки гигабайт в Parquet спокойно агрегируются на одной машине: DuckDB или Polars в ленивом режиме (scan_parquet) читают только нужные колонки и не загружают всё в память. В pandas пришлось бы читать по частям через chunksize и складывать частичные суммы.
Когда данных больше, чем тянет одна машина:
- Spark:
groupBy("store_id").agg(F.sum("revenue")). Сумма считается в два этапа: частичная агрегация внутри каждой партиции, затем по сети едут уже частичные суммы, а не сырые строки. Поэтому перекос ключей на сумме бьёт слабее, чем наjoin; - формат: колоночный Parquet или ORC, партиционирование по дате и фильтр по ней. Движок читает только нужные партиции и колонки и отбрасывает блоки по статистике min/max. Индексов в привычном смысле здесь нет;
- ClickHouse: если сумма нужна постоянно, её предагрегируют материализованным представлением в
SummingMergeTreeилиAggregatingMergeTree, и отчёт читает готовые итоги; - инкрементальность: пересчитывать только новые партиции, хранить дневные итоги и собирать общую сумму из них.
Про деньги скажите отдельно: float для выручки не годится, накапливается ошибка округления, а в распределённом расчёте результат ещё и зависит от порядка сложения. Берите Decimal или целые копейки. Уточните и бизнес-правила: возвраты, валюта по курсу на дату операции, дубли событий.
Где спотыкаются
Отвечают «загружу в pandas и сделаю groupby». На объёме больше оперативной памяти процесс просто упадёт.
Считают сумму во float и получают копеечные расхождения с бухгалтерией, которые потом долго ищут.
Не думают о дублях и поздних данных. Повторно доставленное событие удваивает выручку, если нет дедупликации по идентификатору транзакции, а опоздавшие записи не попадают в уже посчитанный день.