⌘+k ctrl+k
1.4 (LTS)
搜索快捷键 cmd + k | ctrl + k
性能分析

性能分析对于理解某些查询为何表现出特定的性能特征至关重要。DuckDB 包含多个内置功能以支持查询性能分析,本页将对此进行介绍。有关使用 EXPLAIN 的高阶示例,请参阅“检查查询计划”页面

语句

EXPLAIN 语句

性能分析查询的第一步通常包括检查查询计划。EXPLAIN 语句会显示查询计划并描述其底层运行机制。

EXPLAIN ANALYZE 语句

查询计划有助于开发者理解查询的性能特征。然而,通常还需要检查各个算子的性能数据以及流经这些算子的基数(cardinality)。EXPLAIN ANALYZE 语句可以获取这些信息,因为它在以易读格式打印查询计划的同时还会实际执行查询。因此,它能够提供真实的运行时性能数据。

FORMAT 选项

EXPLAIN [ANALYZE] 语句支持导出为多种格式:

  • text – 默认的 ASCII 艺术风格输出
  • graphviz – 生成 DOT 输出,可使用 Graphviz 进行渲染
  • html – 生成 HTML 输出,可使用 treeflex 进行渲染
  • json – 生成 JSON 输出

若要指定格式,请使用 FORMAT 标签。

EXPLAIN (FORMAT html) SELECT 42 AS x;

Pragmas

DuckDB 支持多种 pragma 用于开启/关闭性能分析,并控制分析输出的详细程度。

可以使用 PRAGMASET 来设置以下 pragma。也可以使用 RESET 加设置名称来重置它们。有关更多信息,请参阅 pragma 页面中的“性能分析”部分。

设置 描述 默认值 选项
enable_profiling, enable_profile 开启性能分析 query_tree query_tree, json, query_tree_optimizer, no_output
profiling_coverage 设置要进行分析的算子 SELECT SELECT, ALL
profiling_output 设置性能分析输出文件 控制台 (Console) 文件路径
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_NAMEOPERATOR_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 bytes 结果集大小
ROWS_RETURNED uint64 绝对值   查询返回的总行数

累计指标

DuckDB 还支持多种可用于所有节点的累计指标。在 QUERY_ROOT 节点中,这些指标代表查询中所有算子相应指标的总和。OPERATOR 节点则代表算子自身指标与其所有子节点指标递归相加的总和。

这些累计指标可以独立启用,即使底层对应的特定指标已被禁用。下表显示了累计指标,并展示了 DuckDB 计算该累计指标所依据的基础指标。

度量 单位 计算累计的指标
CPU_TIME OPERATOR_TIMING
CUMULATIVE_CARDINALITY 绝对值 OPERATOR_CARDINALITY
CUMULATIVE_ROWS_SCANNED 绝对值 OPERATOR_ROWS_SCANNED

CPU_TIME 用于衡量累计的算子耗时。它不包括解析、查询规划等其他阶段所花费的时间。因此,对于某些查询,QUERY_ROOT 中的 LATENCY 可能大于 CPU_TIME

详细性能分析

profiling_mode 设置为 detailed 时,会启用额外的一组指标,这些指标仅在 QUERY_ROOT 节点中可用。这包括 OPTIMIZER(优化器)、PLANNER(规划器)和 PHYSICAL_PLANNER(物理规划器)指标。它们以秒为单位进行测量,并以 double 类型返回。可以单独切换每个额外的指标。

优化器指标

QUERY_ROOT 节点处,存在用于衡量每个优化器所花费时间的指标。这些指标仅在特定优化器开启时才可用。可用的优化项可以通过 duckdb_optimizers() 表函数进行查询。

每个优化器都有一个对应的指标,遵循以下模板:OPTIMIZER_OPTIMIZER_NAME。例如,OPTIMIZER_JOIN_ORDER 指标对应于 JOIN_ORDER 优化器。

此外,以下指标可用于支持优化器指标:

  • ALL_OPTIMIZERS:启用所有优化器指标,并衡量优化器父节点所花费的时间。
  • CUMULATIVE_OPTIMIZER_TIMING:所有优化器指标的累计总和。无需开启所有优化器指标即可使用。

规划器指标

规划器负责生成逻辑计划。目前,DuckDB 在规划器中测量两个指标:

  • PLANNER:从解析后的 SQL 节点生成逻辑计划所花费的时间。
  • PLANNER_BINDING:绑定逻辑计划所花费的时间。

物理规划器指标

物理规划器负责从逻辑计划生成物理计划。物理规划器支持以下指标:

  • PHYSICAL_PLANNER:生成物理计划所花费的时间。
  • PHYSICAL_PLANNER_COLUMN_BINDING:将逻辑计划中的列绑定到物理列所花费的时间。
  • PHYSICAL_PLANNER_RESOLVE_TYPES:将逻辑计划中的类型解析为物理类型所花费的时间。
  • PHYSICAL_PLANNER_CREATE_PLAN:创建物理计划所花费的时间。

自定义指标示例

以下示例演示了如何启用自定义性能分析并将输出格式设置为 json。在第一个示例中,我们开启性能分析并将输出保存到文件。我们仅启用 EXTRA_INFOOPERATOR_CARDINALITYOPERATOR_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 脚本将其渲染为查询图。该脚本需要安装 duckdb Python 模块。它会生成一个 HTML 文件并在您的 Web 浏览器中打开。

python -m duckdb.query_graph /path/to/file.json

查询计划中的符号

在查询计划中,哈希连接 (Hash Join) 算子遵循以下约定:连接的探测侧 (probe side) 是左操作数,而构建侧 (build side) 是右操作数。

查询计划中的连接算子会显示所使用的连接类型:

  • 内连接记为 INNER
  • 左外连接和右外连接分别记为 LEFTRIGHT
  • 全外连接记为 FULL

提示:要可视化查询计划,请考虑使用由 图宾根大学数据库系统研究小组开发的 DuckDB 执行计划可视化工具

© 2025 DuckDB 基金会,阿姆斯特丹,荷兰
行为准则 商标使用指南