⌘+k ctrl+k
1.1.3 (stable)
Search Shortcut cmd + k | ctrl + k
Profiling

性能分析对于帮助理解为什么某些查询表现出特定的性能特征至关重要。 DuckDB 包含多个内置功能以启用查询性能分析,本页面将介绍这些功能。 有关使用 EXPLAIN 的高级示例,请参阅 “检查查询计划”页面。 有关深入的解释,请参阅开发者文档中的 “性能分析”页面

EXPLAIN 语句

分析查询的第一步可以包括检查查询计划。 EXPLAIN 语句显示查询计划并描述其背后的操作。

EXPLAIN ANALYZE 语句

查询计划帮助开发者理解查询的性能特征。 然而,通常还需要检查各个操作符的性能数据以及通过它们的基数。 EXPLAIN ANALYZE 语句可以获取这些信息,因为它不仅漂亮地打印查询计划,还执行查询。 因此,它提供了实际的运行时性能数据。

Pragmas

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

以下是一些可用的编译指示,可以使用PRAGMASET来设置。 它们也可以使用RESET后跟设置名称来重置。 更多信息,请参阅编译指示页面的“性能分析”部分。

设置 描述 默认值 选项
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_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 absolute   每个操作符扫描的总行数。
OPERATOR_TIMING double   每个操作符所花费的时间。操作符等效于LATENCY
OPERATOR_TYPE string     每个操作符的名称。
QUERY_NAME string     查询字符串。
RESULT_SET_SIZE uint64 字节 结果的大小。
ROWS_RETURNED uint64 absolute   查询返回的行数。

累计指标

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: 启用所有优化器指标,并测量优化器父节点所花费的时间。
  • CUMMULATIVE_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