Перейти к основному содержимому
Перейти к основному содержимому

Управление Данных

Развертывания ClickHouse для Наблюдаемости неизбежно включают большие объемы данных, которые необходимо управлять. ClickHouse предлагает ряд возможностей для содействия в управлении данными.

Разделы

Разделение в ClickHouse позволяет логически разделить данные на диске в соответствии со столбцом или SQL выражением. Логически разделяя данные, каждый раздел может обрабатываться независимо, например, удаляться. Это позволяет пользователям перемещать разделы, а следовательно, и подмножества между уровнями хранения эффективно по времени или истекания данных/эффективно удалять из кластера.

Разделение указывается в таблице, когда она изначально определяется с помощью оператора PARTITION BY. Этот оператор может содержать SQL выражение по любым столбцам, результаты которого определяют, в какой раздел будет отправлена строка.

Части данных логически связаны (с помощью общего префикса имен папок) с каждым разделом на диске и могут запрашиваться изолированно. Для примера ниже схема по умолчанию otel_logs разделяет по дням, используя выражение toDate(Timestamp). Когда строки вставляются в ClickHouse, это выражение будет оцениваться для каждой строки и направляться в соответствующий раздел, если он существует (если строка является первой для дня, раздел будет создан).

Несколько операций могут выполняться на разделах, включая резервные копии, манипуляции со столбцами, мутации изменения/удаления данных по строкам и очистки индекса (например, вторичных индексов).

В качестве примера допустим, что наша таблица otel_logs разделена по дням. Если она заполнена структурированным набором данных логов, это будет содержать несколько дней данных:

Текущие разделы можно найти с помощью простого запроса к системной таблице:

У нас может быть другая таблица, otel_logs_archive, которую мы используем для хранения старых данных. Данные могут быть эффективно перемещены в эту таблицу по разделам (это просто изменение метаданных).

Это в отличие от других техник, которые потребовали бы использования INSERT INTO SELECT и переписывания данных в новую целевую таблицу.

Перемещение разделов

Перемещение разделов между таблицами требует выполнения нескольких условий, в частности, таблицы должны иметь одинаковую структуру, ключ раздела, первичный ключ и индексы/проекции. Подробные примечания о том, как задавать разделы в DDL ALTER можно найти здесь.

Кроме того, данные могут быть эффективно удалены по разделам. Это гораздо более ресурсно-эффективно, чем альтернативные техники (мутации или легковесные удаления) и должно быть предпочтительным.

примечание

Эта функция используется с TTL, когда используется настройка ttl_only_drop_parts=1. См. Управление данными с помощью TTL для получения более подробной информации.

Применения

Выше описывается, как данные могут быть эффективно перемещены и изменены по разделам. На практике пользователи, вероятно, наиболее часто будут использовать операции с разделами в сценариях Наблюдаемости для двух случаев:

  • Многоуровневые архитектуры - Перемещение данных между уровнями хранения (см. Уровни хранения), тем самым позволяя построить архитектуры горячего-холодного хранения.
  • Эффективное удаление - когда данные достигли установленного TTL (см. Управление данными с помощью TTL)

Мы подробно рассмотрим оба этих случая ниже.

Производительность Запросов

Хотя разделы могут помочь с производительностью запросов, это сильно зависит от моделей доступа. Если запросы нацелены только на несколько разделов (желательно один), производительность может потенциально улучшиться. Это обычно полезно только в том случае, если ключ раздела не входит в первичный ключ, и вы фильтруете по нему. Тем не менее, запросы, которым необходимо охватить много разделов, могут иметь худшую производительность, чем если бы разделения не использовались (поскольку может быть больше частей). Преимущество нацеливания на один раздел будет еще менее выраженным, если ключ раздела уже является ранним элементом в первичном ключе. Разделы также могут быть использованы для оптимизации запросов GROUP BY, если значения в каждом разделе уникальны. Тем не менее, в общем, пользователи должны убедиться, что первичный ключ оптимизирован и рассматривать разделение как технику оптимизации запросов только в исключительных случаях, когда модели доступа обращаются к конкретному предсказуемому подмножеству данных, например, разделив по дням, когда большинство запросов идет за последний день. См. здесь для примера такого поведения.

Управление данными с помощью TTL (Время жизни)

Время жизни (TTL) является ключевой функцией в решениях по наблюдаемости, поддерживаемых ClickHouse, для эффективного хранения и управления данными, особенно учитывая, что огромные объемы данных непрерывно генерируются. Реализация TTL в ClickHouse позволяет автоматически истекать и удалять старые данные, обеспечивая оптимальное использование хранилища и поддержание производительности без ручного вмешательства. Эта возможность необходима для поддержания базы данных в чистоте, снижения затрат на хранение и обеспечения быстроты и эффективности запросов, сосредоточенных на наиболее актуальных и свежих данных. Более того, это помогает в соблюдении политик хранения данных путем систематического управления жизненным циклом данных, тем самым увеличивая общую устойчивость и масштабируемость решения по наблюдаемости.

TTL можно указать как на уровне таблицы, так и на уровне столбца в ClickHouse.

TTL на уровне таблицы

Схема по умолчанию как для логов, так и для трейсов включает TTL для истечения данных после определенного периода. Это указывается в экспортере ClickHouse под ключом ttl, например:

Этот синтаксис в настоящее время поддерживает синтаксис длительности Golang. Мы рекомендуем пользователям использовать h и убедиться, что это соответствует периоду разделения. Например, если вы разделяете по дням, убедитесь, что это кратно дням, например, 24h, 48h, 72h. Это автоматически обеспечит добавление условия TTL в таблицу, например, если ttl: 96h.

По умолчанию данные с истекшим TTL удаляются, когда ClickHouse сливает части данных. Когда ClickHouse обнаружит, что данные истекли, он выполняет слияние вне расписания.

Запланированные TTL

TTL не применяются немедленно, а по расписанию, как упоминалось выше. Настройка таблицы MergeTree merge_with_ttl_timeout устанавливает минимальную задержку в секундах перед повторным слиянием с удалением TTL. Значение по умолчанию составляет 14400 секунд (4 часа). Но это только минимальная задержка, выполнение слияния TTL может занять больше времени. Если значение слишком низкое, это приведет к многочисленным слияниям вне расписания, которые могут потреблять много ресурсов. Истечение TTL можно принудительно выполнить с помощью команды ALTER TABLE my_table MATERIALIZE TTL.

Важно: Мы рекомендуем использовать настройку ttl_only_drop_parts=1 (применяется схемой по умолчанию). Когда эта настройка включена, ClickHouse удаляет целую часть, когда все строки в ней истекли. Удаление целых частей вместо частичной очистки устаревших строк TTL (достигается через ресурсоемкие мутации, когда ttl_only_drop_parts=0) позволяет иметь более короткие времена merge_with_ttl_timeout и снижает влияние на производительность системы. Если данные разделены по тому же единице, по которому вы выполняете истечение TTL, например, по дням, части будут содержать только данные из определенного интервала. Это обеспечит эффективное применение ttl_only_drop_parts=1.

TTL на уровне столбца

Приведенный выше пример истекает данные на уровне таблицы. Пользователи также могут истекать данные на уровне столбца. По мере старения данных это можно использовать для удаления столбцов, чьи значения в расследованиях не оправдывают их накладные расходы на хранение. Например, мы рекомендуем сохранить столбец Body на случай, если будет добавлен новый динамический метаданные, которое не было извлечено во время вставки, например, новая метка Kubernetes. После определенного периода, например, 1 месяц, может стать очевидным, что эти дополнительные метаданные не полезны - тем самым ограничивая значение сохранения столбца Body.

Ниже мы показываем, как столбец Body может быть удален через 30 дней.

примечание

Указание TTL на уровне столбца требует от пользователей указать свою собственную схему. Этого нельзя указать в сборщике OTel.

Рекомпрессия данных

Хотя мы обычно рекомендуем ZSTD(1) для наборов данных наблюдаемости, пользователи могут экспериментировать с различными алгоритмами сжатия или более высокими уровнями сжатия, например, ZSTD(3). Кроме того, помимо возможности указания этого при создании схемы, сжатие можно настраивать для изменения после определенного периода. Это может быть уместно, если кодек или алгоритм сжатия улучшает сжатие, но ухудшает производительность запросов. Этот компромисс может быть приемлемым для старых данных, которые запрашиваются реже, но не для недавних данных, которые подвержены более частому использованию в расследованиях.

Пример этого показан ниже, где мы сжимаем данные, используя ZSTD(3) после 4 дней, вместо того чтобы удалять их.

Оценка производительности

Мы рекомендуем пользователям всегда оценивать как влияние на производительность вставки, так и производительности запросов различных уровней сжатия и алгоритмов. Например, дельта кодеки могут быть полезны для сжатия временных меток. Однако, если это часть первичного ключа, производительность фильтрации может пострадать.

Дополнительные детали и примеры по настройке TTL можно найти здесь. Примеры того, как TTL могут быть добавлены и изменены для таблиц и столбцов, можно найти здесь. Для того, как TTL позволяют создавать иерархии хранения, такие как горячее-теплое хранения, смотрите Уровни хранения.

Уровни хранения

В ClickHouse пользователи могут создавать уровни хранения на разных дисках, например, горячие/недавние данные на SSD и старые данные на S3. Эта архитектура позволяет использовать менее дорогие хранилища для старых данных, которые имеют более высокие SLA на запросы из-за их редкого использования в расследованиях.

Не относится к ClickHouse Cloud

ClickHouse Cloud использует одну копию данных, которая хранится на S3, с кэшами узлов на SSD. Уровни хранения в ClickHouse Cloud, таким образом, не требуются.

Создание уровней хранения требует от пользователей создания дисков, которые затем используются для формирования политик хранения, с объемами, которые могут быть указаны при создании таблицы. Данные могут автоматически перемещаться между дисками на основе уровня заполнения, размеров частей и приоритетов объемов. Дополнительные детали можно найти здесь.

Хотя данные могут быть вручную перемещены между дисками с помощью команды ALTER TABLE MOVE PARTITION, перемещение данных между объемами также может контролироваться с использованием TTL. Полный пример можно найти здесь.

Управление изменениями в схеме

Схемы логов и трейсов неизбежно изменятся в течение жизненного цикла системы, например, когда пользователи мониторят новые системы, которые имеют разные метаданные или метки пода. Создавая данные с использованием схемы OTel и захватывая исходные данные событий в структурированном формате, схемы ClickHouse будут устойчивы к этим изменениям. Тем не менее, по мере появления новых метаданных и изменения моделей доступа к запросам пользователи захотят обновить схемы, чтобы отразить эти изменения.

Чтобы избежать простоя во время изменений схемы, у пользователей есть несколько вариантов, которые мы представляем ниже.

Используйте значения по умолчанию

Столбцы могут быть добавлены в схему с использованием DEFAULT значений. Указанное значение по умолчанию будет использоваться, если оно не указано во время INSERT.

Изменения схемы можно произвести до изменения любой логики трансформации материализованного представления или настройки сборщика OTel, что вызывает отправку этих новых столбцов.

После изменения схемы пользователи могут перенастроить сборщики OTel. Предполагая, что пользователи используют рекомендованный процесс, описанный в "Извлечение структуры с помощью SQL", где сборщики OTel отправляют свои данные в Null таблицу с материализованным представлением, отвечающим за извлечение целевой схемы и отправку результатов в целевую таблицу для хранения, представление можно изменить с помощью синтаксиса ALTER TABLE ... MODIFY QUERY. Предположим, у нас есть целевая таблица ниже с соответствующим материализованным представлением (аналогичное тому, что использовалось в "Извлечение структуры с помощью SQL") для извлечения целевой схемы из структурированных логов OTel:

Предположим, мы хотим извлечь новый столбец Size из LogAttributes. Мы можем добавить это в нашу схему с помощью ALTER TABLE, указав значение по умолчанию:

В приведенном выше примере мы указываем значение по умолчанию как ключ size в LogAttributes (это будет 0, если он не существует). Это означает, что запросы, которые обращаются к этому столбцу для строк, у которых значение не было вставлено, должны получить доступ к Map и, следовательно, будут медленнее. Мы также могли бы легко указать это как постоянное значение, например, 0, что снижает стоимость последующих запросов к строкам, у которых нет значения. Запрос к этой таблице показывает, что значение заполняется, как ожидалось, из Map:

Чтобы гарантировать, что это значение будет вставлено для всех будущих данных, мы можем изменить наше материализованное представление, используя синтаксис ALTER TABLE, как показано ниже:

Последующие строки будут иметь заполненный столбец Size во время вставки.

Создать новые таблицы

В качестве альтернативы вышеописанному процессу пользователи могут просто создать новую целевую таблицу с новой схемой. Любые материализованные представления могут затем быть изменены для использования новой таблицы с помощью вышеуказанного ALTER TABLE MODIFY QUERY. С этим подходом, пользователи могут версионировать свои таблицы, например, otel_logs_v3.

Этот подход оставляет пользователям несколько таблиц для запроса. Чтобы выполнять запросы по всем таблицам, пользователи могут использовать merge функцию, которая принимает шаблоны с подстановочными знаками для имени таблицы. Мы демонстрируем это ниже, запрашивая v2 и v3 таблицы otel_logs:

Если пользователи хотят избежать использования функции merge и предоставить пользователю таблицу, которая объединяет несколько таблиц, можно использовать Merge таблицы. Мы демонстрируем это ниже:

Это можно обновить всякий раз при добавлении новой таблицы, используя синтаксис EXCHANGE таблицы. Например, чтобы добавить таблицу v4, мы можем создать новую таблицу и атомарно обменяться ею с предыдущей версией.