Профилирование
Профилирование необходимо, чтобы понять, почему определенные запросы демонстрируют определенные характеристики производительности. DuckDB содержит несколько встроенных функций для включения профилирования запросов, которые рассматриваются на этой странице.
EXPLAIN Запрос
Первым шагом при профилировании запроса может быть изучение плана запроса. Запрос EXPLAIN отображает план запроса и описывает то, что происходит под капотом.
EXPLAIN ANALYZE Запрос
План запроса помогает разработчикам понять характеристики производительности запроса. Однако часто также необходимо изучить числовые показатели производительности отдельных операторов и кардинальности, которые через них проходят. Запрос EXPLAIN ANALYZE позволяет получить эти данные, так как он красиво отображает план запроса и также выполняет запрос. Таким образом, он предоставляет фактические временные показатели производительности.
Прагмы
DuckDB поддерживает несколько прагм для включения и выключения профилирования и управления уровнем детализации выходных данных профилирования.
Доступны следующие прагмы, которые можно задать, используя либо PRAGMA или SET. Их также можно сбросить, используя RESET, за которым следует имя настройки. Более подробную информацию можно найти в разделе «Профилирование» на странице прагм “Profiling”.
| Настройка | Описание | Значение по умолчанию | Варианты |
|---|---|---|---|
enable_profiling, enable_profile
| Включить профилирование. | query_tree |
query_tree, json, query_tree_optimizer, no_output
|
profiling_output | Установить файл вывода профилирования. | Консоль | Путь к файлу. |
profiling_mode | Включить дополнительные метрики оптимизатора и планировщика. | standard |
standard, detailed
|
custom_profiling_settings | Включить или отключить определенные метрики. | Все метрики, кроме тех, что активированы подробным профилированием. | Объект JSON, соответствующий следующему: {"METRIC_NAME": "boolean", ...}. Смотрите раздел метрики ниже. |
disable_profiling, disable_profile
| Отключить профилирование. |
Метрики
Дерево запроса имеет два типа узлов: узлы QUERY_ROOT и OPERATOR. Узел QUERY_ROOT относится исключительно к узлу верхнего уровня, и метрики, которые он содержит, измеряются по всему запросу. Узлы OPERATOR относятся к отдельным операторам в плане запроса. Некоторые метрики доступны только для узлов QUERY_ROOT, в то время как другие — только для узлов OPERATOR. Ниже представлена таблица, описывающая каждую метрику и для каких узлов она доступна.
Помимо QUERY_NAME и OPERATOR_TYPE, можно включить или отключить все метрики.
| Метрика | Тип возвращаемого значения | Единица измерения | Запрос | Оператор | Описание |
|---|---|---|---|---|---|
BLOCKED_THREAD_TIME | double | секунды | ✅ | Общее время блокировки потоков. | |
EXTRA_INFO | string | ✅ | ✅ | Уникальные метрики операторов. | |
LATENCY | double | секунды | ✅ | Общее время выполнения запроса. | |
OPERATOR_CARDINALITY | uint64 | абсолютное значение | ✅ | Кардинальность каждого оператора, т. е. количество строк, возвращаемых ему родительским узлом. Операторный эквивалент ROWS_RETURNED.. | |
OPERATOR_ROWS_SCANNED | uint64 | абсолютное значение | ✅ | Общее количество просканированных строк каждым оператором. | |
OPERATOR_TIMING | double | секунды | ✅ | Время, затраченное каждым оператором. Операторный эквивалент LATENCY.. | |
OPERATOR_TYPE | string | ✅ | Имя каждого оператора. | ||
QUERY_NAME | string | ✅ | Строка запроса. | ||
RESULT_SET_SIZE | uint64 | байты | ✅ | ✅ | Размер результата. |
ROWS_RETURNED | uint64 | абсолютное значение | ✅ | Количество строк, возвращенных запросом. |
Кумулятивные метрики
DuckDB также поддерживает несколько кумулятивных метрик, которые доступны во всех узлах. В узле QUERY_ROOT эти метрики представляют собой сумму соответствующих метрик по всем операторам в запросе. Узлы OPERATOR представляют собой сумму метрики конкретного оператора и всех его потомков рекурсивно.
Эти кумулятивные метрики могут быть включены независимо, даже если соответствующие метрики отключены. В таблице ниже показаны кумулятивные метрики. Также показана метрика, на основе которой DuckDB вычисляет кумулятивную метрику.
| Метрика | Единица измерения | Метрика, вычисляемая кумулятивно |
|---|---|---|
CPU_TIME | секунды | OPERATOR_TIMING |
CUMULATIVE_CARDINALITY | абсолютное значение | OPERATOR_CARDINALITY |
CUMULATIVE_ROWS_SCANNED | абсолютное значение | OPERATOR_ROWS_SCANNED |
CPU_TIME измеряет кумулятивные временные затраты операторов. В нее не входит время, затраченное на другие стадии, такие как парсинг, планирование запроса и т. д. Поэтому для некоторых запросов LATENCY в узле QUERY_ROOT может быть больше, чем CPU_TIME.
Детальное профилирование
Когда profiling_mode установлено в detailed, включается дополнительный набор метрик, которые доступны только в узле QUERY_ROOT Это включает OPTIMIZER, PLANNER и PHYSICAL_PLANNER метрики. Они измеряются в секундах и возвращаются как double. Можно включать и отключать каждую из этих дополнительных метрик индивидуально.
Метрики оптимизатора
В узле QUERY_ROOT есть метрики, которые измеряют время, затраченное каждым оптимизатором. Эти метрики доступны только при включенном соответствующем оптимизаторе. Доступные оптимизации можно запросить, используя duckdb_optimizers() table function.
Каждый оптимизатор имеет соответствующую метрику, которая следует шаблону: OPTIMIZER_⟨OPTIMIZER_NAME⟩. Например, метрика OPTIMIZER_JOIN_ORDER соответствует оптимизатору JOIN_ORDER.
Кроме того, доступны следующие метрики для поддержки метрик оптимизатора:
-
ALL_OPTIMIZERS: Включает все метрики оптимизатора и измеряет время, которое родительский узел оптимизатора тратит на свою работу. -
CUMMULATIVE_OPTIMIZER_TIMING: Кумулятивная сумма всех метрик оптимизатора. Она может использоваться без включения всех метрик оптимизатора.
Метрики планировщика
Планировщик отвечает за генерацию логического плана. В настоящее время DuckDB измеряет две метрики в планировщике:
-
PLANNER: Время генерации логического плана из обработанных узлов SQL. -
PLANNER_BINDING: Время привязки логического плана.
Метрики физического планировщика
Физический планировщик отвечает за генерацию физического плана из логического плана. Ниже приведены метрики, поддерживаемые физическим планировщиком:
-
PHYSICAL_PLANNER: Время, потраченное на генерацию физического плана. -
PHYSICAL_PLANNER_COLUMN_BINDING: Время, потраченное на привязку столбцов в логическом плане к физическим столбцам. -
PHYSICAL_PLANNER_RESOLVE_TYPES: Время, потраченное на преобразование типов в логическом плане к физическим типам. -
PHYSICAL_PLANNER_CREATE_PLAN: Время, потраченное на создание физического плана.
Примеры пользовательских метрик
Следующие примеры демонстрируют, как включить пользовательское профилирование и установить формат вывода в json. В первом примере мы включаем профилирование и устанавливаем вывод в файл. Мы включаем только EXTRA_INFO, OPERATOR_CARDINALITY, и OPERATOR_TIMING.
CREATE TABLE students (name VARCHAR, sid INTEGER);
CREATE TABLE exams (eid INTEGER, subject VARCHAR, sid INTEGER);
INSERT INTO students VALUES ('Mark', 1), ('Joe', 2), ('Matthew', 3);
INSERT INTO exams VALUES (10, 'Physics', 1), (20, 'Chemistry', 2), (30, 'Literature', 3);
PRAGMA enable_profiling = 'json';
PRAGMA profiling_output = '/path/to/file.json';
PRAGMA custom_profiling_settings = '{"CPU_TIME": "false", "EXTRA_INFO": "true", "OPERATOR_CARDINALITY": "true", "OPERATOR_TIMING": "true"}';
SELECT name
FROM students
JOIN exams USING (sid)
WHERE name LIKE 'Ma%'; Содержимое файла после выполнения запроса:
{
"extra_info": {},
"query_name": "SELECT name\nFROM students\nJOIN exams USING (sid)\nWHERE name LIKE 'Ma%';",
"children": [
{
"operator_timing": 0.000001,
"operator_cardinality": 2,
"operator_type": "PROJECTION",
"extra_info": {
"Projections": "name",
"Estimated Cardinality": "1"
},
"children": [
{
"extra_info": {
"Join Type": "INNER",
"Conditions": "sid = sid",
"Build Min": "1",
"Build Max": "3",
"Estimated Cardinality": "1"
},
"operator_cardinality": 2,
"operator_type": "HASH_JOIN",
"operator_timing": 0.00023899999999999998,
"children": [
... Второй пример добавляет подробные метрики в вывод.
PRAGMA profiling_mode = 'detailed'; SELECT name FROM students JOIN exams USING (sid) WHERE name LIKE 'Ma%';
Содержимое выведенного файла:
{
"all_optimizers": 0.001413,
"cumulative_optimizer_timing": 0.0014120000000000003,
"planner": 0.000873,
"planner_binding": 0.000869,
"physical_planner": 0.000236,
"physical_planner_column_binding": 0.000005,
"physical_planner_resolve_types": 0.000001,
"physical_planner_create_plan": 0.000226,
"optimizer_expression_rewriter": 0.000029,
"optimizer_filter_pullup": 0.000002,
"optimizer_filter_pushdown": 0.000102,
...
"optimizer_column_lifetime": 0.000009999999999999999,
"rows_returned": 2,
"latency": 0.003708,
"cumulative_rows_scanned": 6,
"cumulative_cardinality": 11,
"extra_info": {},
"cpu_time": 0.000095,
"optimizer_build_side_probe_side": 0.000017,
"result_set_size": 32,
"blocked_thread_time": 0.0,
"query_name": "SELECT name\nFROM students\nJOIN exams USING (sid)\nWHERE name LIKE 'Ma%';",
"children": [
{
"operator_timing": 0.000001,
"operator_rows_scanned": 0,
"cumulative_rows_scanned": 6,
"operator_cardinality": 2,
"operator_type": "PROJECTION",
"cumulative_cardinality": 11,
"extra_info": {
"Projections": "name",
"Estimated Cardinality": "1"
},
"result_set_size": 32,
"cpu_time": 0.000095,
"children": [
... Графики запросов
Также можно отобразить выходные данные профилирования в виде графа запроса. Граф запроса визуально представляет план запроса, показывая операторы и их взаимосвязи. План запроса должен быть выведен в формате json и сохранён в файле. После записи выходных данных профилирования в назначенный файл, скрипт Python может отобразить их в виде графа запроса. Для работы скрипта необходимо установить модуль Python duckdb. Он генерирует HTML-файл и открывает его в вашем веб-браузере.
python -m duckdb.query_graph /path/to/file.json
Нотация в планах запросов
В планах запросов операторы hash join следуют следующей конвенции: сторона пробы соединения — это левый операнд, а сторона построения — это правый операнд.
Операторы соединения в плане запроса показывают тип соединения, используемого:
- Внутренние соединения обозначаются как
INNER. - Левые внешние соединения и правые внешние соединения обозначаются как
LEFTиRIGHTсоответственно. - Полные внешние соединения обозначаются как
FULL.
© Copyright 2018–2024 Stichting DuckDB Foundation
Licensed under the MIT License.
https://duckdb.org/docs/dev/profiling.html