⌘+k ctrl+k
1.3 (稳定版)
搜索快捷键 cmd + k | ctrl + k
性能分析

性能分析对于理解特定查询为何表现出特定性能特征至关重要。DuckDB 包含几项内置功能,可实现查询性能分析,本页将介绍这些功能。有关使用 EXPLAIN 的高层示例,请参阅“检查查询计划”页面。有关深入的解释,请参阅开发者文档中的“性能分析”页面

语句

EXPLAIN 语句

性能分析查询的第一步可以包括检查查询计划。EXPLAIN 语句显示查询计划并描述其幕后的运行情况。

EXPLAIN ANALYZE 语句

查询计划有助于开发者理解查询的性能特征。然而,通常还需要检查各个运算符的性能数据以及流经它们的数据基数。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 支持多种编译指示(pragmas),用于打开和关闭性能分析,并控制性能分析输出的详细程度。

以下编译指示可用,并可以通过 PRAGMASET 进行设置。它们也可以通过 RESET 后跟设置名称进行重置。更多信息,请参阅编译指示页面中的“性能分析”部分。

设置 描述 默认值 选项
enable_profiling, enable_profile 开启性能分析 query_tree query_tree, json, query_tree_optimizer, no_output
profiling_output 设置性能分析输出文件 控制台 文件路径
profiling_mode 切换额外的优化器和规划器指标 标准 standard, detailed
custom_profiling_settings 启用或禁用特定指标 除详细性能分析激活的指标外,所有指标 一个匹配以下格式的 JSON 对象:{"METRIC_NAME": "boolean", ...}。请参阅下面的指标部分。
disable_profiling, disable_profile 关闭性能分析    

指标

查询树有两种类型的节点:QUERY_ROOTOPERATOR 节点。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 字节 结果集的大小
ROWS_RETURNED uint64 绝对值   查询返回的行数
SYSTEM_PEAK_BUFFER_MEMORY uint64 字节   查询期间系统所有已分配缓冲区的峰值内存使用量
SYSTEM_PEAK_TEMP_DIR_SIZE 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 节点中可用。这包括 OPTIMIZERPLANNERPHYSICAL_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 文件并在您的网络浏览器中打开。

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

查询计划中的表示法

在查询计划中,哈希连接运算符遵循以下约定:连接的探查侧是左操作数,而构建侧是右操作数。

查询计划中的连接运算符显示使用的连接类型

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

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