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

Структура и метрики Query Profile

Обзор

Query Profile — это подробный отчет, который предоставляет информацию о выполнении SQL-запроса в Selena. Он предлагает комплексное представление о производительности запроса, включая время, затраченное на каждую операцию, объем обработанных данных и другие соответствующие метрики. Эта информация неоценима для оптимизации производительности запросов, выявления узких мест и устранения неполадок.

Почему это важно

80% медленных запросов в реальных условиях решаются путем обнаружения одной из трех критических метрик. Эта шпаргалка поможет вам найти их, прежде чем вы утонете в цифрах.

Быстрый старт

Профилирование недавнего запроса:

1. Список недавних ID запросов

Для анализа профиля запроса необходим ID запроса. Используйте SHOW PROFILELIST;:

SHOW PROFILELIST;
подсказка

SHOW PROFILELIST подробно описан в Текстовый анализ Query Profile. Обратитесь к этой странице, если вы только начинаете.

2. Откройте профиль рядом с вашим SQL

Выполните ANALYZE PROFILE FOR <query_id>\G или нажмите Profile в веб-интерфейсе CelerData.

3. Просмотрите баннер "Execution Overview"

Изучите ключевые метрики для общей производительности выполнения:

  • QueryExecutionWallTime: Общее время выполнения запроса по настенным часам
  • QueryPeakMemoryUsagePerNode: Пиковое использование памяти на узел, значения превышающие 80% памяти BE указывают на потенциальные риски переполнения данных или ошибок нехватки памяти (OOM)
  • QueryCumulativeCpuTime / WallTime < 0.5 * num_cpu_cores означает, что CPU ожидает (вероятно, I/O или сеть)

Если ни одна из них не срабатывает, ваш запрос обычно в порядке — остановитесь здесь.

4. Углубитесь на один уровень

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

Раздел "Operator Metrics" предлагает множество рекомендаций для помощи в выявлении первопричины проблем с производительностью.

Основные концепции

Поток выполнения запроса

Комплексный поток выполнения SQL-запроса включает следующие этапы:

  1. Планирование: Запрос проходит парсинг, анализ и оптимизацию, завершающиеся генерацией плана запроса.
  2. Планирование: Планировщик и координатор работают вместе для распределения плана запроса по всем участвующим backend-узлам.
  3. Выполнение: План запроса выполняется с использованием движка конвейерного выполнения.

SQL Execution Flow

Структура плана запроса

Движок выполнения Selena разработан для выполнения запросов в распределенном режиме, и структура Query Profile отражает этот дизайн. Следующие компоненты составляют распределенный план запроса:

  • Fragment: Высший уровень дерева выполнения, представляющий логическую единицу работы. Запрос может быть разделен на один или несколько фрагментов.
  • FragmentInstance: Каждый фрагмент создается несколько раз, при этом каждый экземпляр (FragmentInstance) выполняется на разном вычислительном узле. Это позволяет параллельную обработку между узлами.
  • Pipeline: FragmentInstance далее разделяется на несколько конвейеров, которые являются последовательностями соединенных экземпляров Operator. Конвейеры определяют путь выполнения для FragmentInstance.
  • PipelineDriver: Для максимального использования вычислительных ресурсов каждый конвейер может иметь несколько экземпляров, известных как PipelineDrivers. Эти драйверы выполняют конвейер параллельно, используя несколько вычислительных ядер.
  • Operator: Основная единица выполнения, экземпляр Operator является частью PipelineDriver. Операторы реализуют специфические алгоритмы, такие как агрегация, соединение или сканирование, для обработки данных.

profile-3

Концепции движка конвейерного выполнения

Pipeline Engine является ключевым компонентом движка выполнения Selena. Он отвечает за выполнение плана запроса параллельным и эффективным способом. Pipeline Engine разработан для обработки сложных планов запросов и больших объемов данных, обеспечивая высокую производительность и масштабируемость.

Ключевые концепции в Pipeline Engine:

  • Operator: Основная единица выполнения, отвечающая за реализацию специфических алгоритмов (например, агрегация, соединение, сканирование)
  • Pipeline: Последовательность соединенных экземпляров Operator, представляющая путь выполнения
  • PipelineDriver: Несколько экземпляров конвейера для параллельного выполнения
  • Schedule: Неблокирующее планирование конвейеров с использованием пользовательского разделения времени

pipeline_opeartors

Стратегия объединения метрик

По умолчанию Selena объединяет слои FragmentInstance и PipelineDriver для уменьшения объема профиля, что приводит к упрощенной трехслойной структуре:

  • Fragment
  • Pipeline
  • Operator

Вы можете контролировать это поведение объединения через переменную сессии pipeline_profile_level:

  • 1 (По умолчанию): Объединенная трехслойная структура
  • 2: Оригинальная пятислойная структура
  • Другие значения: Обрабатываются как 1

При объединении метрик используются разные стратегии в зависимости от типа метрики:

  • Временные метрики: Берется среднее значение

    • Пример: OperatorTotalTime — это среднее время потребления
    • __MAX_OF_OperatorTotalTime и __MIN_OF_OperatorTotalTime записывают крайние значения
  • Не временные метрики: Суммируются значения

    • Пример: PullChunkNum — это сумма по всем экземплярам
    • __MAX_OF_PullChunkNum и __MIN_OF_PullChunkNum записывают крайние значения
  • Константные метрики: Одинаковое значение для всех экземпляров (например, DegreeOfParallelism)

Значительные различия между значениями MIN и MAX часто указывают на перекос данных, особенно в операциях агрегации и соединения.

Метрики Query Profile

Сводные метрики

Базовая информация о выполнении запроса:

МетрикаОписание
TotalОбщее время, потребленное запросом, включая длительность фаз Planning, Executing и Profiling.
Query StateСостояние запроса, возможные состояния включают Finished, Error и Running.
Query IDУникальный идентификатор запроса.
Start TimeВременная метка начала запроса.
End TimeВременная метка окончания запроса.
TotalОбщая длительность запроса.
Query TypeТип запроса.
Query StateТекущее состояние запроса.
Selena VersionВерсия используемой Selena.
UserПользователь, выполнивший запрос.
Default DbБаза данных по умолчанию, используемая для запроса.
Sql StatementВыполненный SQL-оператор.
VariablesВажные переменные, используемые для запроса.
NonDefaultSessionVariablesНе по умолчанию переменные сессии, используемые для запроса.
Collect Profile TimeВремя, затраченное на сбор профиля.
IsProfileAsyncУказывает, был ли сбор профиля асинхронным.

Метрики планировщика

Предоставляет комплексный обзор планировщика. Обычно, если общее время, затраченное на планировщик, составляет менее 10 мс, это не вызывает беспокойства.

В определенных сценариях планировщику может потребоваться больше времени:

  1. Сложные запросы могут потребовать дополнительного времени для парсинга и оптимизации для обеспечения оптимального плана выполнения.
  2. Наличие многочисленных материализованных представлений может увеличить время, необходимое для перезаписи запроса.
  3. Когда несколько одновременных запросов исчерпывают системные ресурсы и используется очередь запросов, время Pending может быть продлено.
  4. Запросы, включающие внешние таблицы, могут потребовать дополнительного времени для связи с внешним сервером метаданных.

Пример:

     - -- Parser[1] 0
- -- Total[1] 3ms
- -- Analyzer[1] 0
- -- Lock[1] 0
- -- AnalyzeDatabase[1] 0
- -- AnalyzeTemporaryTable[1] 0
- -- AnalyzeTable[1] 0
- -- Transformer[1] 0
- -- Optimizer[1] 1ms
- -- MVPreprocess[1] 0
- -- MVTextRewrite[1] 0
- -- RuleBaseOptimize[1] 0
- -- CostBaseOptimize[1] 0
- -- PhysicalRewrite[1] 0
- -- DynamicRewrite[1] 0
- -- PlanValidate[1] 0
- -- InputDependenciesChecker[1] 0
- -- TypeChecker[1] 0
- -- CTEUniqueChecker[1] 0
- -- ColumnReuseChecker[1] 0
- -- ExecPlanBuild[1] 0
- -- Pending[1] 0
- -- Prepare[1] 0
- -- Deploy[1] 2ms
- -- DeployLockInternalTime[1] 2ms
- -- DeploySerializeConcurrencyTime[2] 0
- -- DeployStageByStageTime[6] 0
- -- DeployWaitTime[6] 1ms
- -- DeployAsyncSendTime[2] 0
- DeployDataSize: 10916
Reason:

Метрики обзора выполнения

Высокоуровневая статистика выполнения:

МетрикаОписаниеЭмпирическое правило
FrontendProfileMergeTimeВремя обработки профиля на стороне FE< 10мс нормально
QueryAllocatedMemoryUsageОбщая выделенная память по узлам
QueryDeallocatedMemoryUsageОбщая освобожденная память по узлам
QueryPeakMemoryUsagePerNodeМаксимальная пиковая память на узел< 80% емкости нормально
QuerySumMemoryUsageОбщая пиковая память по узлам
QueryExecutionWallTimeВремя выполнения по настенным часам
QueryCumulativeCpuTimeОбщее время CPU по узламСравните с walltime * totalCpuCores
QueryCumulativeOperatorTimeОбщее время выполнения операторовЗнаменатель для процентов времени операторов
QueryCumulativeNetworkTimeОбщее сетевое время узлов Exchange
QueryCumulativeScanTimeОбщее время I/O узлов Scan
QueryPeakScheduleTimeМаксимальное ScheduleTime конвейера< 1с нормально для простых запросов
QuerySpillBytesДанные, сброшенные на диск< 1ГБ нормально

Метрики фрагментов

Детали выполнения на уровне фрагментов:

МетрикаОписание
InstanceNumКоличество FragmentInstances
InstanceIdsID всех FragmentInstances
BackendNumКоличество участвующих BE
BackendAddressesАдреса BE
FragmentInstancePrepareTimeДлительность фазы подготовки фрагмента
InstanceAllocatedMemoryUsageОбщая выделенная память для экземпляров
InstanceDeallocatedMemoryUsageОбщая освобожденная память для экземпляров
InstancePeakMemoryUsageПиковая память по экземплярам

Метрики конвейера

Детали выполнения конвейера и взаимосвязи:

profile_pipeline_time_relationship

Ключевые взаимосвязи:

  • DriverTotalTime = ActiveTime + PendingTime + ScheduleTime
  • ActiveTime = ∑ OperatorTotalTime + OverheadTime
  • PendingTime = InputEmptyTime + OutputFullTime + PreconditionBlockTime + PendingFinishTime
  • InputEmptyTime = FirstInputEmptyTime + FollowupInputEmptyTime
МетрикаОписание
DegreeOfParallelismСтепень параллелизма выполнения конвейера.
TotalDegreeOfParallelismСумма степеней параллелизма. Поскольку один и тот же Pipeline может выполняться на нескольких машинах, этот элемент агрегирует все значения.
DriverPrepareTimeВремя, затраченное на фазу подготовки. Эта метрика не включена в DriverTotalTime.
DriverTotalTimeОбщее время выполнения Pipeline, исключая время, затраченное на фазу подготовки.
ActiveTimeВремя выполнения Pipeline, включая время выполнения каждого оператора и общие накладные расходы фреймворка, такие как время, затраченное на вызов методов типа has_output, need_input и т.д.
PendingTimeВремя, когда Pipeline заблокирован от планирования по различным причинам.
InputEmptyTimeВремя, когда Pipeline заблокирован из-за пустой входной очереди.
FirstInputEmptyTimeВремя, когда Pipeline впервые заблокирован из-за пустой входной очереди. Первое время блокировки рассчитывается отдельно, поскольку первая блокировка в основном вызвана зависимостями Pipeline.
FollowupInputEmptyTimeВремя, когда Pipeline впоследствии заблокирован из-за пустой входной очереди.
OutputFullTimeВремя, когда Pipeline заблокирован из-за полной выходной очереди.
PreconditionBlockTimeВремя, когда Pipeline заблокирован из-за невыполненных зависимостей.
PendingFinishTimeВремя, когда Pipeline заблокирован в ожидании завершения асинхронных задач.
ScheduleTimeВремя планирования Pipeline, от входа в очередь готовности до планирования для выполнения.
BlockByInputEmptyКоличество раз, когда конвейер заблокирован из-за InputEmpty.
BlockByOutputFullКоличество раз, когда конвейер заблокирован из-за OutputFull.
BlockByPreconditionКоличество раз, когда конвейер заблокирован из-за невыполненных предварительных условий.

Метрики операторов

МетрикаОписание
PrepareTimeВремя, затраченное на подготовку.
OperatorTotalTimeОбщее время, потребленное Operator. Удовлетворяет уравнению: OperatorTotalTime = PullTotalTime + PushTotalTime + SetFinishingTime + SetFinishedTime + CloseTime. Исключает время, затраченное на подготовку.
PullTotalTimeОбщее время, которое Operator тратит на выполнение push_chunk.
PushTotalTimeОбщее время, которое Operator тратит на выполнение pull_chunk.
SetFinishingTimeОбщее время, которое Operator тратит на выполнение set_finishing.
SetFinishedTimeОбщее время, которое Operator тратит на выполнение set_finished.
PushRowNumКумулятивное количество входных строк для Operator.
PullRowNumКумулятивное количество выходных строк для Operator.
JoinRuntimeFilterEvaluateКоличество раз оценки Join Runtime Filter.
JoinRuntimeFilterHashTimeВремя, затраченное на вычисление хеша для Join Runtime Filter.
JoinRuntimeFilterInputRowsКоличество входных строк для Join Runtime Filter.
JoinRuntimeFilterOutputRowsКоличество выходных строк для Join Runtime Filter.
JoinRuntimeFilterTimeВремя, затраченное на Join Runtime Filter.

Scan Operator

Для облегчения лучшего понимания различных метрик в Scan Operator, следующая диаграмма демонстрирует связи между этими метриками и структурами хранения.

profile_scan_relationship

Для извлечения данных с диска и применения предикатов движок хранения использует несколько техник:

  1. Хранение данных: Закодированные и сжатые данные хранятся на диске в сегментах, сопровождаемых различными индексами.
  2. Фильтрация индексов: Движок использует индексы, такие как BitmapIndex, BloomfilterIndex, ZonemapIndex, ShortKeyIndex и NGramIndex, для пропуска ненужных данных.
  3. Pushdown предикаты: Простые предикаты, такие как a > 1, проталкиваются вниз для оценки на конкретных столбцах.
  4. Поздняя материализация: Только необходимые столбцы и отфильтрованные строки извлекаются с диска.
  5. Не-Pushdown предикаты: Предикаты, которые не могут быть протолкнуты вниз, оцениваются.
  6. Выражение проекции: Выражения, такие как SELECT a + 1, вычисляются.

Общие узкие места производительности и их решения:

Тяжелый Raw I/O или медленное хранилище

Критические метрики / симптомы: BytesRead, RawRowsRead, CompressedBytesRead, ScanTime, IOTaskExecTime доминируют

Почему это замедляет OLAP сканирование: Пропускная способность чтения диска (или объектного хранилища) становится ограничением

Решения: Поместите горячие данные на NVMe/SSD, включите кеш хранилища

Плохой предикат

Критические метрики / симптомы: PushdownPredicates≈0; ExprFilterRows доминирует; LIKE '%x%' или другие сложные предикаты

Почему это замедляет OLAP сканирование: Больше строк поступает в пул потоков CPU, поскольку фильтры не применяются на уровне хранения

Решения: Перепишите фильтры на простые сравнения, создайте целевые MV/индексы

Низкий DOP или насыщение пула потоков

Критические метрики / симптомы: Высокий IOTaskWaitTime; низкий PeakIOTasks

Почему это замедляет OLAP сканирование: Слишком мало параллельных задач сканирования или потоки заблокированы в ожидании слотов I/O

Решения: Увеличьте пропускную способность диска или расширьте кеш

Перекос Tablet / данных между BE

Критические метрики / симптомы: Большой разрыв max-min для OperatorTotalTime или BytesRead; один tablet владеет большинством данных

Почему это замедляет OLAP сканирование: Один поток выполняет непропорциональную работу, все остальные простаивают

Решения: Hash-bucket на ключе высокой кардинальности; увеличьте количество buckets

Фрагментированные Rowsets и крошечные сегменты

Критические метрики / симптомы: Высокий RowsetsReadCount / SegmentsReadCount; долгое время SegmentInit

Почему это замедляет OLAP сканирование: Много маленьких файлов заставляют часто выполнять вызовы open/seek

Решения: Увеличьте потоки компакций или выполните ручные компакции; пакетируйте мини-загрузки

Высокое количество мягко удаленных записей

Критические метрики / симптомы: Высокий DeleteFilterRows

Почему это замедляет OLAP сканирование: Мягкое удаление будет применять предикат удаления при чтении

Решения: Компактируйте данные; уменьшите частоту операций удаления

Scan Operator использует дополнительный пул потоков для выполнения IO задач. Поэтому взаимосвязь между временными метриками для этого узла показана ниже:

profile_scan_time_relationship

OLAP Scan Operator

OLAP_SCAN Operator отвечает за чтение данных из нативных таблиц Selena.

МетрикаОписание
TableИмя таблицы.
RollupИмя материализованного представления. Если материализованное представление не найдено, оно эквивалентно имени таблицы.
SharedScanВключена ли переменная сессии enable_shared_scan.
TabletCountКоличество tablets.
MorselsCountКоличество morsels, которые являются базовой единицей выполнения IO.
PushdownPredicatesКоличество pushdown предикатов.
PredicatesВыражения предикатов.
BytesReadРазмер прочитанных данных.
CompressedBytesReadРазмер сжатых данных, прочитанных с диска.
UncompressedBytesReadРазмер несжатых данных, прочитанных с диска.
RowsReadКоличество прочитанных строк (после фильтрации предикатов).
RawRowsReadКоличество сырых прочитанных строк (до фильтрации предикатов).
ReadPagesNumКоличество прочитанных страниц.
CachedPagesNumКоличество кешированных страниц.
ChunkBufferCapacityЕмкость Chunk Buffer.
DefaultChunkBufferCapacityЕмкость Chunk Buffer по умолчанию.
PeakChunkBufferMemoryUsageПиковое использование памяти Chunk Buffer.
PeakChunkBufferSizeПиковый размер Chunk Buffer.
PrepareChunkSourceTimeВремя, затраченное на подготовку Chunk Source.
ScanTimeКумулятивное время сканирования. Операции сканирования выполняются в асинхронном пуле потоков I/O.
IOTaskExecTimeВремя выполнения IO задач.
IOTaskWaitTimeВремя ожидания от успешной отправки до запланированного выполнения IO задач.
SubmitTaskCountКоличество раз отправки IO задач.
SubmitTaskTimeВремя, затраченное на отправку задач.
PeakIOTasksПиковое количество IO задач.
PeakScanTaskQueueSizeПиковый размер очереди IO задач.

Connector Scan Operator

Похож на OLAP_SCAN operator, но используется для сканирования внешних таблиц, таких как Iceberg/Hive/Hudi/Delta.

МетрикаОписание
DataSourceTypeТип источника данных, может быть HiveDataSource, ESDataSource и так далее.
TableИмя таблицы.
TabletCountКоличество tablets.
MorselsCountКоличество morsels.
PredicatesВыражение предиката.
PredicatesPartitionВыражение предиката, применяемое к разделам.
SharedScanВключена ли переменная сессии enable_shared_scan.
ChunkBufferCapacityЕмкость Chunk Buffer.
DefaultChunkBufferCapacityЕмкость Chunk Buffer по умолчанию.
PeakChunkBufferMemoryUsageПиковое использование памяти Chunk Buffer.
PeakChunkBufferSizeПиковый размер Chunk Buffer.
PrepareChunkSourceTimeВремя, затраченное на подготовку Chunk Source.
ScanTimeКумулятивное время сканирования. Операция сканирования выполняется в асинхронном пуле потоков I/O.
IOTaskExecTimeВремя выполнения I/O задач.
IOTaskWaitTimeВремя ожидания от успешной отправки до запланированного выполнения IO задач.
SubmitTaskCountКоличество раз отправки IO задач.
SubmitTaskTimeВремя, затраченное на отправку задач.
PeakIOTasksПиковое количество IO задач.
PeakScanTaskQueueSizeПиковый размер очереди IO задач.

Exchange Operator

Exchange Operator отвечает за передачу данных между узлами BE. Может быть несколько видов операций exchange: GATHER/BROADCAST/SHUFFLE.

Типичные сценарии, которые могут сделать Exchange Operator узким местом запроса:

  1. Broadcast Join: Это подходящий метод для небольшой таблицы. Однако в исключительных случаях, когда оптимизатор выбирает неоптимальный план запроса, это может привести к значительному увеличению пропускной способности сети.
  2. Shuffle Aggregation/Join: Перемешивание большой таблицы может привести к значительному увеличению пропускной способности сети.

Exchange Sink Operator

МетрикаОписание
ChannelNumКоличество каналов. Обычно количество каналов равно количеству получателей.
DestFragmentsСписок ID целевых FragmentInstance.
DestIDID целевого узла.
PartTypeРежим распределения данных, включая: UNPARTITIONED, RANDOM, HASH_PARTITIONED и BUCKET_SHUFFLE_HASH_PARTITIONED.
SerializeChunkTimeВремя, затраченное на сериализацию chunks.
SerializedBytesРазмер сериализованных данных.
ShuffleChunkAppendCounterКоличество операций Chunk Append, когда PartType равен HASH_PARTITIONED или BUCKET_SHUFFLE_HASH_PARTITIONED.
ShuffleChunkAppendTimeВремя, затраченное на операции Chunk Append, когда PartType равен HASH_PARTITIONED или BUCKET_SHUFFLE_HASH_PARTITIONED.
ShuffleHashTimeВремя, затраченное на вычисление хеша, когда PartType равен HASH_PARTITIONED или BUCKET_SHUFFLE_HASH_PARTITIONED.
RequestSentКоличество отправленных пакетов данных.
RequestUnsentКоличество неотправленных пакетов данных. Эта метрика не равна нулю при наличии логики короткого замыкания; в противном случае она равна нулю.
BytesSentРазмер отправленных данных.
BytesUnsentРазмер неотправленных данных. Эта метрика не равна нулю при наличии логики короткого замыкания; в противном случае она равна нулю.
BytesPassThroughЕсли целевой узел является текущим узлом, данные не будут передаваться по сети, что называется passthrough данными. Эта метрика указывает размер таких passthrough данных. Passthrough контролируется enable_exchange_pass_through.
PassThroughBufferPeakMemoryUsageПиковое использование памяти PassThrough Buffer.
CompressTimeВремя сжатия.
CompressedBytesРазмер сжатых данных.
OverallThroughputСкорость пропускной способности.
NetworkTimeВремя, затраченное на передачу пакетов данных (исключая время обработки после получения).
NetworkBandwidthОценочная пропускная способность сети.
WaitTimeВремя ожидания из-за полной очереди отправителя.
OverallTimeОбщее время для всего процесса передачи, то есть от отправки первого пакета данных до подтверждения правильного получения последнего пакета данных.
RpcAvgTimeСреднее время для RPC.
RpcCountОбщее количество RPC.

Exchange Source Operator

МетрикаОписание
RequestReceivedРазмер полученных пакетов данных.
BytesReceivedРазмер полученных данных.
DecompressChunkTimeВремя, затраченное на распаковку chunks.
DeserializeChunkTimeВремя, затраченное на десериализацию chunks.
ClosureBlockCountКоличество заблокированных Closures.
ClosureBlockTimeВремя блокировки для Closures.
ReceiverProcessTotalTimeОбщее время, затраченное на обработку на стороне получателя.
WaitLockTimeВремя ожидания блокировки.

Aggregate Operator

aggregation_operator Aggregate Operator отвечает за выполнение агрегатных функций, GROUP BY и DISTINCT.

Множественные формы алгоритма агрегации

ФормаКогда планировщик выбирает еёВнутренняя структура данныхОсобенности / предостережения
Hash aggregationключи помещаются в память; кардинальность не экстремальнаяКомпактная хеш-таблица с SIMD зондированиемпуть по умолчанию, отличный для умеренного количества ключей
Sorted aggregationвходные данные уже упорядочены по ключам GROUP BYПростое сравнение строк + текущее состояниенулевая стоимость хеш-таблицы, часто в 2-3 раза быстрее при тяжелых перекосах зондирования
Spillable aggregation (3.2+)хеш-таблица превышает лимит памятиГибридный hash/merge с разделами сброса на дискпредотвращает OOM, сохраняет параллелизм конвейера

Многоэтапная распределенная агрегация

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

┌─────────┐        ┌──────────┐        ┌────────────┐        ┌────────────┐
│ Stage 0 │ local │ Stage 1 │ shard/ │ Stage 2 │ gather/│ Stage 3 │ final
│ Partial │───► │ Update │ hash │ Merge │ shard │ Finalize │ output
└─────────┘ └──────────┘ └────────────┘ └────────────┘
ЭтапыКогда используетсяЧто происходит
ОдноэтапныйDISTRIBUTED BY является подмножеством GROUP BY, разделы размещены совместноЧастичные агрегаты сразу становятся финальным результатом.
Двухэтапный (локальный + глобальный)Типичный распределенный GROUP BYЭтап 0 внутри каждого BE адаптивно сворачивает дубликаты; Этап 1 перемешивает данные на основе GROUP BY, затем выполняет глобальную агрегацию
Трехэтапный (локальный + shuffle + финальный)Тяжелый DISTINCT и высококардинальный GROUP BYЭтап 0 как выше; Этап 1 перемешивает по GROUP BY, затем агрегирует по GROUP BY и DISTINCT; Этап 2 объединяет частичное состояние как GROUP BY
Четырехэтапный (локальный + частичный + промежуточный + финальный)Тяжелый DISTINCT и низкокардинальный GROUP BYВводит дополнительный этап для перемешивания по GROUP BY и DISTINCT, чтобы избежать узкого места единой точки

Общие узкие места производительности и их решения:

Причина узкого места

Высококардинальный GROUP BY → переразмерная хеш-таблица

Критические метрики / симптомы: HashTableSize, HashTableMemoryUsage и AggComputeTime раздуваются; запрос приближается к лимиту памяти

Почему это вредит операторам Agg: Hash-aggregate создает одну запись на группу; если миллионы групп попадают в RAM, хеш-таблица становится ограниченной по CPU и памяти и может даже переполниться

Решения: Включите sorted streaming aggregate; добавьте предварительно агрегированные MV или roll-ups; уменьшите ширину ключа / приведите к INT

Перекошенное перемешивание данных между частичными → финальными этапами

Критические метрики / симптомы: Огромный разрыв в HashTableSize или InputRowCount между экземплярами; AggComputeTime одного фрагмента затмевает другие

Почему это вредит операторам Agg: Один backend получает большинство горячих ключей и блокирует конвейер

Решения: Добавьте salt столбец в агрегацию; используйте подсказку DISTINCT [skew]

Дорогие или DISTINCT-стильные агрегатные функции (например, ARRAY_AGG, HLL_UNION, BITMAP_UNION, COUNT(DISTINCT))

Критические метрики / симптомы: AggregateFunctions доминирует время оператора; CPU все еще около 100% после завершения построения хеша

Почему это вредит операторам Agg: Функции агрегации с тяжелым состоянием хранят значительные эскизы и выполняют SIMD-тяжелые циклы каждую строку

Решения: Предварительно вычислите HLL/Bitmap столбцы при загрузке; используйте approx_count_distinct или multi_distinct_* где позволяет точность;

Плохая агрегация первого этапа (частичная)

Критические метрики / симптомы: Очень большой InputRowCount, но AggComputeTime скромный; PassThroughRowCount высокий; upstream EXCHANGE показывает массивный BytesSent

Почему это вредит операторам Agg: Если частичная агрегация на каждом BE не предварительно агрегирует набор данных хорошо, большинство сырых строк проходят по сети и накапливаются в финальном AGG

Решения: Подтвердите, что план показывает двух- или трехэтапную агрегацию; перепишите запрос на простые ключи GROUP BY, чтобы оптимизатор мог протолкнуть частичный AGG; установите streaming_preaggregation_mode = 'force_preaggregation'

Тяжелая оценка выражений на ключах GROUP BY

Критические метрики / симптомы: ExprComputeTime высокий относительно AggComputeTime

Почему это вредит операторам Agg: Сложные функции на каждой строке перед хешированием доминируют CPU

Решения: Материализуйте вычисленные ключи в подзапросе или генерируемом столбце; используйте словарь столбцов / предварительно закодированные значения; проецируйте downstream вместо этого

Список метрик

МетрикаОписание
GroupingKeysСтолбцы GROUP BY.
AggregateFunctionsВремя, затраченное на вычисления агрегатных функций.
AggComputeTimeВремя для AggregateFunctions + Group By.
ChunkBufferPeakMemПиковое использование памяти Chunk Buffer.
ChunkBufferPeakSizeПиковый размер Chunk Buffer.
ExprComputeTimeВремя для вычисления выражений.
ExprReleaseTimeВремя для освобождения выражений.
GetResultsTimeВремя для извлечения результатов агрегации.
HashTableSizeРазмер Hash Table.
HashTableMemoryUsageРазмер памяти Hash Table.
InputRowCountКоличество входных строк.
PassThroughRowCountВ режиме Auto количество строк данных, обработанных в потоковом режиме после низкой агрегации, приводящей к деградации в потоковый режим.
ResultAggAppendTimeВремя, затраченное на добавление столбцов результата агрегации.
ResultGroupByAppendTimeВремя, затраченное на добавление столбцов Group By.
ResultIteratorTimeВремя для итерации по Hash Table.
StreamingTimeВремя обработки в потоковом режиме.

Join Operator

join_operator

Join Operator отвечает за реализацию явных соединений или неявных соединений.

Во время выполнения оператор соединения разделяется на фазы Build (построение хеш-таблицы) и Probe, которые выполняются параллельно внутри движка конвейера. Векторные chunks (до 4096 строк) пакетно хешируются с SIMD; потребляемые ключи генерируют runtime фильтры — Bloom или IN фильтры — проталкиваемые обратно к upstream сканированиям для раннего сокращения входных данных probe.

Стратегии соединения

Selena полагается на векторизованное, дружественное к конвейеру ядро hash-join, которое может быть подключено к четырем физическим стратегиям, которые оптимизатор на основе стоимости взвешивает во время планирования:

СтратегияКогда оптимизатор выбирает еёЧто делает её быстрой
Colocate JoinОбе таблицы принадлежат одной и той же группе размещения (идентичные ключи bucket, количество bucket и макет реплик).Нет сетевого перемешивания: каждый BE соединяет только свои локальные buckets.
Bucket-Shuffle JoinОдна из таблиц соединения имеет тот же ключ bucket, что и ключ соединенияНужно перемешать только одну таблицу соединения, что может снизить сетевые затраты
Broadcast JoinСторона Build очень мала (пороги строк/байтов или явная подсказка).Маленькая таблица реплицируется на каждый узел probe; избегает перемешивания большой таблицы.
Shuffle (Hash) JoinОбщий случай, ключи не выровнены.Hash-разделение каждой строки по ключу соединения, чтобы probes были сбалансированы между BE.

Когда соединения становятся узким местом

Таблица Build-стороны слишком велика для RAM

Симптомы профиля / горячие метрики: BuildRows, HashTableSize, BuildHashTableTime доминируют; память близка к лимиту или переполняется

Почему это вредит: Хеш-таблица должна находиться в памяти, может стать медленной, если не помещается в кеш CPU

Решения:

  • меньшая таблица как сторона build
  • Добавьте предварительную агрегацию или селективную проекцию
  • Увеличьте память запроса/BE или включите hash-spill

Большое время probe соединения

Симптомы профиля / горячие метрики: Высокий SearchHashTableTime

Почему это вредит: Неэффективная кластеризация данных может привести к плохой локальности кеша CPU

Решения: Оптимизируйте кластеризацию данных, сортируя таблицу probe по ключам соединения

Избыточные выходные столбцы

Симптомы профиля / горячие метрики: Высокий OutputBuildColumnTime или OutputProbeColumnTime

Почему это вредит: Обработка многочисленных выходных столбцов требует существенного копирования данных, что может быть интенсивным по CPU

Решения: Оптимизируйте выходные столбцы, уменьшив их количество; исключите тяжелые столбцы из вывода; рассмотрите получение ненужных столбцов после соединения

Перекос данных после перемешивания

Симптомы профиля / горячие метрики: ProbeRows одного фрагмента ≫ других; OperatorTotalTime сильно несбалансирован

Почему это вредит: Один BE получает большинство горячих ключей; другие простаивают

Решения:

  • Используйте ключ более высокой кардинальности
  • дополните составной ключ (concat(key,'-',mod(id,16)))

Трансляция не-такой-маленькой таблицы

Симптомы профиля / горячие метрики: Тип соединения BROADCAST; BytesSent и SendBatchTime взлетают на каждом BE

Почему это вредит: O(N²) сетевой трафик и десериализация

Решения:

  • Позвольте оптимизатору выбрать shuffle (SET broadcast_row_limit = lower)
  • Принудительная подсказка shuffle
  • Анализируйте таблицу для сбора статистики.

Отсутствующие или неэффективные runtime фильтры

Симптомы профиля / горячие метрики: JoinRuntimeFilterEvaluate маленький, сканирования все еще читают полную таблицу

Почему это вредит: Сканирования проталкивают все строки в сторону probe, тратя CPU и I/O

Решения: Перепишите предикат соединения на чистое равенство, чтобы RF мог быть сгенерирован; избегайте приведения типов в ключе соединения

Проникновение Non-equi (nested-loop) соединения

Симптомы профиля / горячие метрики: Узел Join показывает CROSS или NESTLOOP; ProbeRows*BuildRows взлетает

Почему это вредит: O(строки×строки) сравнения; нет хеш-ключа

Решения:

  • Добавьте правильный предикат равенства или предварительный фильтр
  • Материализуйте результат предиката во временной таблице, затем повторно соедините

Стоимость приведения/выражения хеш-ключа

Симптомы профиля / горячие метрики: Высокий ExprComputeTime; время хеш-функции соперничает со временем probe

Почему это вредит: Ключи должны быть приведены или оценены на строку перед хешированием

Решения:

  • Храните ключи с соответствующими типами
  • Предварительно вычислите сложные выражения в генерируемых столбцах

Нет размещения на большом соединении

Симптомы профиля / горячие метрики: Shuffle соединение между фактом и измерением, хотя buckets совпадают

Почему это вредит: Случайное размещение заставляет перемешивать каждый запрос

Решения:

  • Поместите две таблицы в одну группу размещения
  • Проверьте идентичное количество/ключ bucket перед загрузкой

Список метрик

МетрикаОписание
DistributionModeТип распределения, включая: BROADCAST, PARTITIONED, COLOCATE и т.д.
JoinPredicatesПредикаты соединения.
JoinTypeТип соединения.
BuildBucketsКоличество buckets в Hash Table.
BuildKeysPerBucketКоличество ключей на bucket в Hash Table.
BuildConjunctEvaluateTimeВремя, затраченное на оценку conjunct во время фазы build.
BuildHashTableTimeВремя, затраченное на построение Hash Table.
ProbeConjunctEvaluateTimeВремя, затраченное на оценку conjunct во время фазы probe.
SearchHashTableTimerВремя, затраченное на поиск в Hash Table.
CopyRightTableChunkTimeВремя, затраченное на копирование chunks из правой таблицы.
OutputBuildColumnTimeВремя, затраченное на вывод столбца стороны build.
OutputProbeColumnTimeВремя, затраченное на вывод столбца стороны probe.
HashTableMemoryUsageИспользование памяти Hash Table.
RuntimeFilterBuildTimeВремя, затраченное на построение runtime фильтров.
RuntimeFilterNumКоличество runtime фильтров.

Window Function Operator

МетрикаОписание
ProcessModeРежим выполнения, включающий две части: первая часть включает Materializing и Streaming; вторая часть включает Cumulative, RemovableCumulative, ByDefinition.
ComputeTimeВремя, затраченное на вычисления оконных функций.
PartitionKeysСтолбцы разделов.
AggregateFunctionsАгрегатные функции.
ColumnResizeTimeВремя, затраченное на изменение размера столбцов.
PartitionSearchTimeВремя, затраченное на поиск границ разделов.
PeerGroupSearchTimeВремя, затраченное на поиск границ Peer Group. Имеет смысл только когда тип окна RANGE.
PeakBufferedRowsПиковое количество строк в буфере.
RemoveUnusedRowsCountКоличество раз удаления неиспользуемых буферов.
RemoveUnusedTotalRowsОбщее количество строк, удаленных из неиспользуемых буферов.

Sort Operator

МетрикаОписание
SortKeysКлючи сортировки.
SortTypeМетод сортировки результатов запроса: полная сортировка или сортировка топ N результатов.
MaxBufferedBytesПиковый размер буферизованных данных.
MaxBufferedRowsПиковое количество буферизованных строк.
NumSortedRunsКоличество отсортированных прогонов.
BuildingTimeВремя, затраченное на поддержание внутренних структур данных во время сортировки.
MergingTimeВремя, затраченное на объединение отсортированных прогонов во время сортировки.
SortingTimeВремя, затраченное на сортировку.
OutputTimeВремя, затраченное на построение выходной отсортированной последовательности.

Merge Operator

Для облегчения понимания различных метрик Merge может быть представлен как следующий механизм состояний:

               ┌────────── PENDING ◄──────────┐
│ │
│ │
├──────────────◄───────────────┤
│ │
▼ │
INIT ──► PREPARE ──► SPLIT_CHUNK ──► FETCH_CHUNK ──► FINISHED

|
| one traverse from leaf to root
|

PROCESS
МетрикаОписаниеУровень
LimitЛимит.Первичный
OffsetСмещение.Первичный
StreamingBatchSizeРазмер данных, обрабатываемых за операцию Merge, когда Merge выполняется в потоковом режимеПервичный
LateMaterializationMaxBufferChunkNumМаксимальное количество chunks в буфере при включенной поздней материализации.Первичный
OverallStageCountОбщее количество выполнений всех этапов.Первичный
OverallStageTimeОбщее время выполнения для каждого этапа.Первичный
1-InitStageCountКоличество выполнений этапа Init.Вторичный
2-PrepareStageCountКоличество выполнений этапа Prepare.Вторичный
3-ProcessStageCountКоличество выполнений этапа Process.Вторичный
4-SplitChunkStageCountКоличество выполнений этапа SplitChunk.Вторичный
5-FetchChunkStageCountКоличество выполнений этапа FetchChunk.Вторичный
6-PendingStageCountКоличество выполнений этапа Pending.Вторичный
7-FinishedStageCountКоличество выполнений этапа Finished.Вторичный
1-InitStageTimeВремя выполнения этапа Init.Вторичный
2-PrepareStageTimeВремя выполнения этапа Prepare.Вторичный
3-ProcessStageTimeВремя выполнения этапа Process.Вторичный
4-SplitChunkStageTimeВремя, затраченное на этап Split.Вторичный
5-FetchChunkStageTimeВремя, затраченное на этап Fetch.Вторичный
6-PendingStageTimeВремя, затраченное на этап Pending.Вторичный
7-FinishedStageTimeВремя, затраченное на этап Finished.Вторичный
LateMaterializationGenerateOrdinalTimeВремя, затраченное на генерацию порядковых столбцов во время поздней материализации.Третичный
SortedRunProviderTimeВремя, затраченное на получение данных от провайдера во время этапа Process.Третичный

TableFunction Operator

МетрикаОписание
TableFunctionExecTimeВремя вычисления Table Function.
TableFunctionExecCountКоличество выполнений Table Function.

Project Operator

Project Operator отвечает за выполнение SELECT <expr>. Если в запросе есть дорогие выражения, этот оператор может занимать значительное время.

МетрикаОписание
ExprComputeTimeВремя вычисления выражений.
CommonSubExprComputeTimeВремя вычисления общих подвыражений.

LocalExchange Operator

МетрикаОписание
TypeТип Local Exchange, включая: Passthrough, Partition и Broadcast.
ShuffleNumКоличество перемешиваний. Эта метрика действительна только когда Type равен Partition.
LocalExchangePeakMemoryUsageПиковое использование памяти.
LocalExchangePeakBufferSizeПиковый размер буфера.
LocalExchangePeakBufferMemoryUsageПиковое использование памяти буфера.
LocalExchangePeakBufferChunkNumПиковое количество chunks в буфере.
LocalExchangePeakBufferRowNumПиковое количество строк в буфере.
LocalExchangePeakBufferBytesПиковый размер данных в буфере.
LocalExchangePeakBufferChunkSizeПиковый размер chunks в буфере.
LocalExchangePeakBufferChunkRowNumПиковое количество строк на chunk в буфере.
LocalExchangePeakBufferChunkBytesПиковый размер данных на chunk в буфере.

OlapTableSink Operator

OlapTableSink Operator отвечает за выполнение операции INSERT INTO <table>.

подсказка
  • Чрезмерная разница между значениями Max и Min метрики PushChunkNum у OlapTableSink указывает на перекос данных в upstream операторах, что может привести к узкому месту в производительности загрузки.
  • RpcClientSideTime равен RpcServerSideTime плюс время передачи по сети плюс время обработки RPC фреймворка. Если есть значительная разница между RpcClientSideTime и RpcServerSideTime, рассмотрите включение сжатия для уменьшения времени передачи.
МетрикаОписание
IndexNumКоличество синхронных материализованных представлений, созданных для целевой таблицы.
ReplicatedStorageВключена ли Single Leader Replication.
TxnIDID транзакции загрузки.
RowsReadКоличество строк, прочитанных из upstream операторов.
RowsFilteredКоличество строк, отфильтрованных из-за неадекватного качества данных.
RowsReturnedКоличество строк, записанных в целевую таблицу.
RpcClientSideTimeОбщее потребление времени RPC для загрузки, записанное на стороне клиента.
RpcServerSideTimeОбщее потребление времени RPC для загрузки, записанное на стороне сервера.
PrepareDataTimeОбщее потребление времени для фазы подготовки данных, включая преобразование формата данных и проверку качества данных.
SendDataTimeЛокальное потребление времени для отправки данных, включая время для сериализации и сжатия данных, и для отправки задач в очередь отправителя.