Перейти к основному содержанию
В этом руководстве показано, как поддерживать предварительно агрегированные rollup-данные из высоконагруженной таблицы событий с помощью materialized views. Вы создадите три объекта: сырую таблицу, таблицу rollup и materialized view, которая автоматически записывает данные в rollup.

Когда использовать этот шаблон

Используйте этот шаблон, когда:
  • У вас есть поток событий, в который данные только добавляются (клики, просмотры страниц, IoT, журналы).
  • Большинство запросов — это агрегации по временным диапазонам (по минутам/часам/дням).
  • Вам нужно стабильное чтение данных менее чем за секунду без повторного сканирования всех необработанных строк.
1

Создайте таблицу сырых событий

Примечания
  • PARTITION BY toYYYYMM(event_time) разбивает данные на небольшие партиции, которые легко удалять.
  • ORDER BY (event_time, user_id) оптимизирует запросы с ограничением по времени и дополнительной фильтрацией.
  • LowCardinality(String) экономит память для категориальных измерений.
  • TTL удаляет необработанные данные через 90 дней (настройте в соответствии с вашими требованиями к сроку хранения).
2

Спроектируйте rollup-таблицу (агрегированную таблицу)

Мы будем выполнять предварительную агрегацию с часовой детализацией. Выбирайте уровень детализации под наиболее типичный временной диапазон анализа.
Мы храним состояния агрегатных функций (например, AggregateFunction(sum, ...)), которые компактно представляют частичные агрегаты и впоследствии могут быть объединены или финализированы.
3

Создайте materialized view, заполняющее rollup

Это materialized view автоматически срабатывает при вставке в events_raw и записывает состояния агрегатных функций в rollup.
4

Вставьте немного тестовых данных

Вставьте немного тестовых данных:
5

Выполнение запросов к rollup

Вы можете либо объединять состояния при чтении, либо финализировать их:

Если вы ожидаете, что данные всегда будут читаться из rollup, можно создать второе materialized view, которое записывает финализированные значения в «обычную» таблицу MergeTree с той же часовой гранулярностью. Состояния дают больше гибкости, а финализированные значения немного упрощают чтение.
6

Фильтруйте по полям первичного ключа для наилучшей производительности

Чтобы увидеть, как индекс отсекает данные, можно использовать команду EXPLAIN:
Query
Response
Показанный выше план выполнения запроса демонстрирует использование трех типов индексов: индекса MinMax, индекса партиции и индекса первичного ключа. Каждый индекс использует поля, входящие в наш первичный ключ: (bucket_start, country, event_type). Чтобы обеспечить максимальную эффективность фильтрации, убедитесь, что ваши запросы используют поля первичного ключа для отсечения данных.
7

Распространённые варианты

  • Разная гранулярность: добавьте ежедневную агрегацию:
Затем — второе materialized view:
  • Сжатие: применяйте кодеки к большим столбцам (например, Codec(ZSTD(3))) в raw-таблице.
  • Контроль затрат: основную нагрузку по хранению возложите на raw-таблицу, а roll-up’ы храните долго.
  • Дозагрузка: при загрузке исторических данных вставляйте их в events_raw, а materialized view автоматически построит roll-up’ы. Для существующих строк используйте POPULATE при создании materialized view, если это уместно, либо INSERT SELECT.
8

Очистка и сроки хранения

  • Увеличьте TTL для сырых данных (например, до 30/90 дней), но храните roll-up-данные дольше (например, 1 год).
  • Вы также можете использовать TTL для перемещения старых частей в более дешёвое хранилище, если включено распределение по уровням хранения.
9

Устранение неполадок

  • materialized view не обновляется? Проверьте, что вставка идет в events_raw (а не в таблицу rollup) и что целевая таблица materialized view указана правильно (TO events_rollup_1h).
  • Медленные запросы? Убедитесь, что они обращаются к rollup (выполните запрос к таблице rollup напрямую) и что временные фильтры соответствуют гранулярности rollup.
  • Несоответствия при дозагрузке? Используйте SYSTEM FLUSH LOGS и проверьте system.query_log / system.parts, чтобы подтвердить вставки и слияния.
Последнее изменение 2 июля 2026 г.