性能剖析
性能剖析是理解查询性能特征的重要手段。
Goose 内置了多种查询剖析能力,本页进行说明。
如需 EXPLAIN 的高层示例,请参阅 “Inspect Query Plans” 页面。
语句
EXPLAIN 语句
查询剖析的第一步通常是查看查询计划。
EXPLAIN 会展示查询计划并说明底层执行过程。
EXPLAIN ANALYZE 语句
查询计划有助于理解性能特征。
但很多场景还需查看各算子的耗时与经过它们的基数。
EXPLAIN ANALYZE 会在格式化输出查询计划的同时执行查询,从而给出真实运行时性能数据。
FORMAT 选项
EXPLAIN [ANALYZE] 支持导出多种格式:
通过 FORMAT 指定输出格式:
EXPLAIN (FORMAT html) SELECT 42 AS x;
Pragmas
Goose 支持多种 pragma,用于开启/关闭 profiling 以及控制输出细节级别。
以下 pragma 可通过 PRAGMA 或 SET 设置。
也可通过 RESET 加设置名恢复默认值。
更多信息请见 pragmas 页面中的 “Profiling” 小节。
| 设置 | 说明 | 默认值 | 可选值 |
|---|---|---|---|
enable_profiling, enable_profile | 开启 profiling | query_tree | query_tree, json, query_tree_optimizer, no_output |
profiling_coverage | 设置要剖析的语句范围 | SELECT | SELECT, ALL |
profiling_output | 设置 profiling 输出文件 | Console | 文件路径 |
profiling_mode | 切换附加优化器/规划器指标 | standard | standard, detailed |
custom_profiling_settings | 启用或禁用特定指标 | 除 detailed 模式附加指标外全部启用 | JSON 对象:{"METRIC_NAME": "boolean", ...},见下方 metrics 小节。 |
disable_profiling, disable_profile | 关闭 profiling |
指标
查询树中有两类节点:QUERY_ROOT 与 OPERATOR。
QUERY_ROOT 是顶层节点,其指标覆盖整个查询。
OPERATOR 节点对应查询计划中的各个算子。
部分指标仅适用于 QUERY_ROOT,部分仅适用于 OPERATOR。
下表给出各指标及其适用节点。
除 QUERY_NAME 与 OPERATOR_TYPE 外,其余指标都可单独开关。
| Metric | Return type | Unit | Query | Operator | Description |
|---|---|---|---|---|---|
BLOCKED_THREAD_TIME | double | seconds | ✅ | The total time threads are blocked | |
EXTRA_INFO | string | ✅ | ✅ | Unique operator metrics | |
LATENCY | double | seconds | ✅ | The total elapsed query execution time | |
OPERATOR_CARDINALITY | uint64 | absolute | ✅ | The cardinality of each operator, i.e., the number of rows it returns to its parent. Operator equivalent of ROWS_RETURNED | |
OPERATOR_ROWS_SCANNED | uint64 | absolute | ✅ | The total rows scanned by each operator | |
OPERATOR_TIMING | double | seconds | ✅ | The time taken by each operator. Operator equivalent of LATENCY | |
OPERATOR_TYPE | string | ✅ | The name of each operator | ||
QUERY_NAME | string | ✅ | The query string | ||
RESULT_SET_SIZE | uint64 | bytes | ✅ | ✅ | The size of the result |
ROWS_RETURNED | uint64 | absolute | ✅ | The number of rows returned by the query |
累计指标
Goose 还支持若干在所有节点可用的累计指标。
在 QUERY_ROOT 中,它们表示整个查询所有算子对应指标的总和。
在 OPERATOR 中,它们表示当前算子及其所有子节点对应指标的递归总和。
累计指标可独立启用,即使底层具体指标被禁用也可以。 下表展示累计指标及其基于的原始指标。
| Metric | Unit | Metric calculated cumulatively |
|---|---|---|
CPU_TIME | seconds | OPERATOR_TIMING |
CUMULATIVE_CARDINALITY | absolute | OPERATOR_CARDINALITY |
CUMULATIVE_ROWS_SCANNED | absolute | OPERATOR_ROWS_SCANNED |
CPU_TIME 衡量算子耗时总和,
不包含解析、查询规划等其他阶段耗时。
因此某些查询中,QUERY_ROOT 的 LATENCY 可能大于 CPU_TIME。
详细 Profiling
当 profiling_mode 设为 detailed 时,会启用一组仅在 QUERY_ROOT 可见的附加指标,
包括 OPTIMIZER、PLANNER 与 PHYSICAL_PLANNER。
这些指标单位为秒,返回类型为 double,且可逐项开关。
Optimizer 指标
在 QUERY_ROOT 节点中,可查看各optimizer耗时指标。
仅在对应优化器启用时可见。
可通过 goose_optimizers() 表函数查询可用优化器。
每个优化器对应一个指标,命名模板为 OPTIMIZER_⟨OPTIMIZER_NAME⟩。
例如 OPTIMIZER_JOIN_ORDER 对应 JOIN_ORDER 优化器。
此外,还有以下辅助指标:
ALL_OPTIMIZERS:启用全部优化器指标,并统计优化器父节点耗时。CUMULATIVE_OPTIMIZER_TIMING:所有优化器指标累计和,无需启用全部优化器指标即可使用。
Planner 指标
planner 负责生成逻辑计划。目前 Goose 提供两项 planner 指标:
PLANNER:从已解析 SQL 节点生成逻辑计划的耗时。PLANNER_BINDING:逻辑计划绑定耗时。
Physical Planner 指标
physical planner 负责从逻辑计划生成物理计划。 支持以下指标:
PHYSICAL_PLANNER:生成物理计划总耗时。PHYSICAL_PLANNER_COLUMN_BINDING:将逻辑列绑定到物理列的耗时。PHYSICAL_PLANNER_RESOLVE_TYPES:将逻辑类型解析到物理类型的耗时。PHYSICAL_PLANNER_CREATE_PLAN:创建物理计划阶段耗时。
自定义指标示例
以下示例展示如何启用自定义 profiling 并将输出设为 json。
第一个示例中我们启用 profiling 并输出到文件,
只开启 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": [
...
第二个示例会在输出中增加 detailed 指标。
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": [
...
查询图
profiling 输出也可以渲染为查询图。
查询图会可视化展示查询计划中的算子及其关系。
查询计划需先以 json 格式输出到文件。
随后可用 Python 脚本将其渲染为查询图。
该脚本依赖已安装的 goose Python 模块。
它会生成 HTML 文件并在浏览器中打开。
python -m goose.query_graph /path/to/file.json
查询计划中的记号
在查询计划中,hash join 采用如下约定: join 的 probe side 为左操作数,build side 为右操作数。
查询计划中的 join 算子会标明使用的 join 类型:
- 内连接记为
INNER - 左/右外连接分别记为
LEFT/RIGHT - 全外连接记为
FULL
提示:如需可视化查询计划,可使用Goose execution plan visualizer(由图宾根大学数据库系统研究组开发)。