Создание роллапа на основе материализованных представлений для быстрой аналитики временных рядов
В этом руководстве показано, как поддерживать предварительно агрегированные сводки (rollup-таблицы) для высоконагруженной таблицы событий с помощью материализованных представлений. Вы создадите три объекта: «сырую» таблицу, rollup-таблицу и материализованное представление, которое будет автоматически записывать данные в rollup-таблицу.
Когда использовать этот шаблон
Используйте этот шаблон, если:
- У вас есть поток событий с только добавлением (клики, просмотры страниц, IoT, логи).
- Большинство запросов — это агрегации по временным диапазонам (по минутам/часам/дням).
- Вам нужны стабильные чтения с задержкой менее секунды без повторного сканирования всех исходных строк.
Создание таблицы необработанных событий
Примечания
PARTITION BY toYYYYMM(event_time)позволяет создавать небольшие партиции, которые легко удалять.ORDER BY (event_time, user_id)поддерживает ограниченные по времени запросы + вторичную фильтрацию.LowCardinality(String)позволяет экономить память для категориальных измерений.TTLудаляет сырые данные по истечении 90 дней (настройте в соответствии с вашими требованиями к сроку хранения данных).
Проектирование таблицы свёртки (агрегированной)
Мы будем предварительно агрегировать данные с почасовой гранулярностью. Выберите гранулярность в соответствии с наиболее распространённым окном анализа.
Мы храним агрегатные состояния (например, AggregateFunction(sum, ...)), которые компактно представляют частичные агрегаты и могут быть объединены или завершены позже.
Создайте материализованное представление для заполнения сводной таблицы
Это материализованное представление срабатывает автоматически при вставке в events_raw и записывает агрегатные состояния в таблицу свёртки.
Запрос агрегированных данных
Вы можете либо объединять состояния при чтении, либо финализировать их:
- Слияние при чтении
- Финализация с FINAL
Если вы ожидаете, что запросы всегда будут обращаться к агрегированным данным, можно создать второе материализованное представление, которое записывает финализированные значения в «обычную» таблицу MergeTree с той же детализацией в 1 час.
Состояния обеспечивают большую гибкость, тогда как финализированные значения упрощают чтение.
Фильтруйте по полям первичного ключа для оптимальной производительности
Используйте команду EXPLAIN, чтобы увидеть, как индекс применяется для отсечения данных:
В приведенном выше плане выполнения запроса используются три типа индексов:
индекс MinMax, индекс партиций и индекс первичного ключа.
Каждый индекс использует поля, указанные в первичном ключе: (bucket_start, country, event_type).
Для достижения максимальной производительности фильтрации убедитесь, что ваши запросы используют поля первичного ключа для отсечения данных.
Распространённые варианты
- Разные уровни агрегирования: добавьте дневной роллап:
Затем второе материализованное представление:
- Сжатие: применяйте кодеки к крупным столбцам (например,
Codec(ZSTD(3))) в raw-таблице. - Контроль затрат: перенесите основную длительность хранения в сырую таблицу и поддерживайте долгоживущие агрегаты.
- Заполнение задним числом (backfilling): при загрузке исторических данных вставляйте данные в
events_raw, материализованное представление автоматически построит агрегаты (roll-ups). Для существующих строк используйтеPOPULATEпри создании материализованного представления, если это уместно, илиINSERT SELECT.
Очистка и хранение данных
- Увеличьте TTL сырых данных (например, до 30/90 дней), но храните агрегированные данные дольше (например, до 1 года).
- Вы также можете использовать TTL для перемещения старых частей данных в более дешёвое хранилище, если включено многоуровневое хранение.
Устранение неполадок
- Материализованное представление не обновляется? Проверьте, что вставки выполняются в таблицу events_raw (а не в roll-up-таблицу) и что целевая таблица в определении материализованного представления указана корректно (
TO events_rollup_1h). - Медленные запросы? Убедитесь, что они выполняются по rollup-таблице (выполните запрос напрямую к таблице rollup) и что временные фильтры соответствуют шагу агрегации этой rollup-таблицы.
- Несоответствия при бэкфилле? Используйте
SYSTEM FLUSH LOGSи проверьтеsystem.query_log/system.parts, чтобы подтвердить, что вставки и слияния выполнены.