前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >深入探索PostgreSQL优化器的代价模型(建议收藏)

深入探索PostgreSQL优化器的代价模型(建议收藏)

作者头像
PawSQL
发布2024-09-10 21:16:28
1090
发布2024-09-10 21:16:28
举报

PawSQL优化引擎的实现深受PostgreSQL优化器的影响,本文我们来揭密PostgreSQL的代价模型。PawSQL专注于数据库性能优化自动化和智能化,提供的解决方案覆盖SQL开发、测试、运维的整个流程,广泛支持MySQL、PostgreSQL、OpenGauss、Oracle等主流商用和开源数据库,以及openGauss,人大金仓、达梦等国产数据库,为开发者和企业提供一站式的创新SQL优化解决方案,提升了数据库系统的稳定性、应用性能和基础设施利用率。 https://pawsql.com

优化器的代价模型(Cost Model)是数据库查询优化器用于评估不同查询执行计划的代价,并选择最优执行计划的重要核心部分。PostgreSQL 的代价模型通过估算查询执行时的各种操作(如顺序扫描、索引扫描、连接等)的成本,来确定最有效率的查询执行路径。

📈代价的组成

PostgreSQL考虑了多个因素来估算成本:

  • CPU成本:处理元组的CPU时间
  • I/O成本:读取数据页的磁盘I/O时间
  • 网络成本:数据传输时间(对分布式查询)

📊 成本估算的公式

在 PostgreSQL 的源代码文件 src/backend/optimizer/path/costsize.c 中,每个成本估算函数都有特定的计算公式。下面我将对之前列出的所有成本函数逐一列出其计算公式,并解释各个公式中的主要变量。

以下是 PostgreSQL 中的成本估算函数的完整列表,包含每个函数的计算公式和变量解释,并按照类别进行组织:

1. 基础扫描操作的成本估算函数

1.1 cost_seqscan - 顺序扫描的成本估算

公式:

代码语言:javascript
复制
total_cost = startup_cost + run_cost
run_cost = seq_page_cost * pages + cpu_tuple_cost * tuples

变量解释:

  • startup_cost: 启动扫描的初始成本。
  • run_cost: 完成扫描所有数据的成本。
  • seq_page_cost: 顺序扫描每个页面的 I/O 成本。
  • pages: 需要读取的页面数。
  • cpu_tuple_cost: 处理每个元组的 CPU 成本。
  • tuples: 需要处理的元组数。

1.2 cost_index - 索引扫描的成本估算

公式:

代码语言:javascript
复制
total_cost = indexStartupCost + indexTotalCost + cpu_index_tuple_cost * tuples
indexTotalCost = random_page_cost * pages

变量解释:

  • indexStartupCost: 索引扫描的启动成本。
  • indexTotalCost: 索引扫描的总成本。
  • cpu_index_tuple_cost: 处理每个索引元组的 CPU 成本。
  • random_page_cost: 随机页面读取的 I/O 成本。
  • pages: 需要读取的页面数。
  • tuples: 需要处理的元组数。

1.3 cost_indexonlyscan - 索引仅扫描的成本估算

公式:

代码语言:javascript
复制
total_cost = indexStartupCost + indexTotalCost + cpu_index_tuple_cost * tuples

变量解释:

  • indexStartupCost: 索引扫描的启动成本。
  • indexTotalCost: 索引扫描的总成本。
  • cpu_index_tuple_cost: 处理每个索引元组的 CPU 成本。
  • tuples: 需要处理的元组数。

1.4 cost_bitmap_heap_scan - 位图堆扫描的成本估算

公式:

代码语言:javascript
复制
total_cost = startup_cost + run_cost
run_cost = page_cost + cpu_tuple_cost * tuples
page_cost = random_page_cost * pages

变量解释:

  • startup_cost: 启动扫描的初始成本。
  • run_cost: 完成扫描所有数据的成本。
  • random_page_cost: 随机页面读取的 I/O 成本。
  • page_cost: 需要读取页面的总 I/O 成本。
  • cpu_tuple_cost: 处理每个元组的 CPU 成本。
  • pages: 需要读取的页面数。
  • tuples: 需要处理的元组数。

1.5 cost_bitmap_tree_node - 位图索引扫描的成本估算

公式:

代码语言:javascript
复制
total_cost = cpu_cost + random_page_cost * pages

变量解释:

  • cpu_cost: 处理位图的 CPU 成本。
  • random_page_cost: 随机页面读取的 I/O 成本。
  • pages: 需要读取的页面数。

1.6 cost_subqueryscan - 子查询扫描的成本估算

公式:

代码语言:javascript
复制
total_cost = subquery_startup_cost + subquery_total_cost

变量解释:

  • subquery_startup_cost: 执行子查询的启动成本。
  • subquery_total_cost: 子查询的总执行成本。

1.7 cost_functionscan - 函数扫描的成本估算

公式:

代码语言:javascript
复制
total_cost = cpu_function_cost * num_function_calls

变量解释:

  • cpu_function_cost: 每次调用函数的 CPU 成本。
  • num_function_calls: 函数调用的次数。

1.8 cost_valuesscan - 值扫描的成本估算

公式:

代码语言:javascript
复制
total_cost = cpu_tuple_cost * num_tuples

变量解释:

  • cpu_tuple_cost: 处理每个元组的 CPU 成本。
  • num_tuples: 值扫描中的元组数。

1.9 cost_ctescan - 公用表表达式扫描的成本估算

公式:

代码语言:javascript
复制
total_cost = cte_startup_cost + cte_run_cost

变量解释:

  • cte_startup_cost: 执行 CTE 的启动成本。
  • cte_run_cost: 执行 CTE 的运行成本。

1.10 cost_tablefuncscan - 表函数扫描的成本估算

公式:

代码语言:javascript
复制
total_cost = cpu_function_cost * num_tuples

变量解释:

  • cpu_function_cost: 调用表函数时的 CPU 成本。
  • num_tuples: 表函数返回的元组数。

1.11 cost_tidscan - TID 扫描的成本估算

公式:

代码语言:javascript
复制
total_cost = random_page_cost * num_pages + cpu_tuple_cost * num_tuples

变量解释:

  • random_page_cost: 随机页面读取的 I/O 成本。
  • num_pages: 需要读取的页面数。
  • cpu_tuple_cost: 处理每个元组的 CPU 成本。
  • num_tuples: 需要处理的元组数。

1.12 cost_worktablescan - 工作表扫描的成本估算

公式:

代码语言:javascript
复制
total_cost = startup_cost + run_cost

变量解释:

  • startup_cost: 扫描的启动成本。
  • run_cost: 完成扫描的运行成本。

1.13 cost_foreignscan - 外部数据源扫描的成本估算

公式:

代码语言:javascript
复制
total_cost = foreign_scan_cost

变量解释:

  • foreign_scan_cost: 外部数据源提供的扫描成本。

2. 连接操作的成本估算函数

2.1 cost_nestloop - 嵌套循环连接的成本估算

公式:

代码语言:javascript
复制
total_cost = outer_cost + outer_tuples * inner_cost_per_tuple

变量解释:

  • outer_cost: 扫描外部表的总成本。
  • outer_tuples: 外部表中的元组数。
  • inner_cost_per_tuple: 内部表每个元组的扫描成本。

2.2 cost_mergejoin - 归并连接的成本估算

公式:

代码语言:javascript
复制
total_cost = outer_path_cost + inner_path_cost + merge_cost
merge_cost = cpu_operator_cost * num_outer * log2(num_inner)

变量解释:

  • outer_path_cost: 扫描外部表的成本。
  • inner_path_cost: 扫描内部表的成本。
  • merge_cost: 合并两个已排序输入的成本。
  • cpu_operator_cost: 每个连接操作的 CPU 成本。
  • num_outer: 外部表的行数。
  • num_inner: 内部表的行数。

2.3 cost_hashjoin - 哈希连接的成本估算

公式:

代码语言:javascript
复制
total_cost = outer_cost + inner_cost + hash_cost + probe_cost

变量解释:

  • outer_cost: 扫描外部表的成本。
  • inner_cost: 扫描内部表的成本。
  • hash_cost: 构建哈希表的成本。
  • probe_cost: 探测哈希表的成本。

3. 其他操作的成本估算函数

3.1 cost_sort - 排序操作的成本估算

公式:

代码语言:javascript
复制
total_cost = startup_cost + run_cost
run_cost = (log2(input_tuples) * input_tuples) * cpu_operator_cost

变量解释:

  • startup_cost: 启动排序的成本。
  • run_cost: 排序的运行成本。
  • input_tuples: 需要排序的元组数。
  • cpu_operator_cost: 每次比较操作的 CPU 成本。

3.2 cost_material - 物化操作的成本估算

公式:

代码语言:javascript
复制
total_cost = startup_cost + cpu_tuple_cost * tuples

变量解释:

  • startup_cost: 物化操作的启动成本。
  • cpu_tuple_cost: 处理每个元组的 CPU 成本。
  • tuples: 需要处理的元组数。

3.3 cost_agg - 聚合操作的成本估算

公式:

代码语言:javascript
复制
total_cost = cpu_agg_cost * numGroups

变量解释:

  • cpu_agg_cost: 每个聚合操作的 CPU 成本。
  • numGroups: 分组的数量。

3.4 cost_windowagg - 窗口聚合操作的成本估算

公式:

代码语言:javascript
复制
total_cost = cpu_windowagg_cost * numGroups

变量解释:

  • cpu_windowagg_cost: 每个窗口聚合操作的 CPU 成本。
  • numGroups: 窗口函数处理的分组数。

3.5 cost_group - 分组操作的成本估算

公式:

代码语言:javascript
复制
total_cost = cpu_group_cost * numGroups

变量解释:

  • cpu_group_cost: 每个分组操作的 CPU 成本。
  • numGroups: 分组的数量。

3.6 cost_unique - 唯一操作的成本估算

公式:

代码语言:javascript
复制
total_cost = cpu_unique_cost * numGroups

变量解释:

  • cpu_unique_cost: 去重操作的 CPU 成本。
  • numGroups: 处理的分组数量。

3.7 cost_setop - 集合操作的成本估算

公式:

代码语言:javascript
复制
total_cost = cpu_setop_cost * numGroups

变量解释:

  • cpu_setop_cost: 集合操作的 CPU 成本。
  • numGroups: 操作中涉及的分组数量。

3.8 cost_limit - LIMIT 操作的成本估算

公式:

代码语言:javascript
复制
total_cost = startup_cost + cpu_tuple_cost * limit_tuples

变量解释:

  • startup_cost: LIMIT 操作的启动成本。
  • cpu_tuple_cost: 处理每个元组的 CPU 成本。
  • limit_tuples: LIMIT 限制的元组数。

3.9 cost_result - Result 节点的成本估算

公式:

代码语言:javascript
复制
total_cost = cpu_result_cost

变量解释:

  • cpu_result_cost: 处理结果的 CPU 成本。

3.10 cost_append - Append 操作的成本估算

公式:

代码语言:javascript
复制
total_cost = startup_cost + run_cost

变量解释:

  • startup_cost: Append 操作的启动成本。
  • run_cost: 执行 Append 操作的运行成本。

3.11 cost_recursive_union - 递归 UNION 操作的成本估算

公式:

代码语言:javascript
复制
total_cost = left_cost + right_cost + recursive_cost

变量解释:

  • left_cost: 左子查询的成本。
  • right_cost: 右子查询的成本。
  • recursive_cost: 递归操作的成本。

4. 路径成本估算函数

4.1 get_path_cost - 获取路径成本

公式:

代码语言:javascript
复制
total_cost = path->total_cost

变量解释:

  • path->total_cost: 已计算的路径的总成本。

4.2 cost_index_path - 索引路径的成本估算

公式:

代码语言:javascript
复制
total_cost = indexStartupCost + indexTotalCost + cpu_index_tuple_cost * tuples

变量解释:

  • indexStartupCost: 索引路径的启动成本。
  • indexTotalCost: 索引路径的总成本。
  • cpu_index_tuple_cost: 每个元组的 CPU 成本。
  • tuples: 处理的元组数。

4.3 cost_bitmap_and_node - 位图 AND 节点的成本估算

公式:

代码语言:javascript
复制
total_cost = cpu_bitmap_and_cost

变量解释:

  • cpu_bitmap_and_cost: 位图 AND 操作的 CPU 成本。

4.4 cost_bitmap_or_node - 位图 OR 节点的成本估算

公式:

代码语言:javascript
复制
total_cost = cpu_bitmap_or_cost

变量解释:

  • cpu_bitmap_or_cost: 位图 OR 操作的 CPU 成本。

5. 其他相关辅助函数

5.1 get_reltuples - 获取关系的行数

公式:

代码语言:javascript
复制
reltuples = rel->rows

变量解释:

  • rel->rows: 估算的表中行数。

5.2 get_relwidth - 获取关系的宽度

公式:

代码语言:javascript
复制
relwidth = rel->reltarget->width

变量解释:

  • rel->reltarget->width: 表中每行的字节宽度。

5.3 clamp_row_est - 行估算值的限制

公式:

代码语言:javascript
复制
clamped_rows = max(min(rows, MAX_ROWS), MIN_ROWS)

变量解释:

  • rows: 估算的行数。
  • MAX_ROWS: 行数的最大合理值。
  • MIN_ROWS: 行数的最小合理值。

这些公式是 PostgreSQL 查询优化器评估不同查询执行计划成本的核心工具。它们根据特定查询操作的性质,结合系统参数和表的统计信息,计算出各种执行路径的成本,从而帮助优化器选择最优路径。

🛠️ 公式中的参数

PostgreSQL优化器的代价模型使用下面这些参数来估算每个算子的代价,这些参数默认值可能会因PostgreSQL的版本或特定的系统配置而有所不同。以下是基于PostgreSQL 14版本的常见默认值:

  1. seq_page_cost: 1.0
    • 含义: 顺序读取一个磁盘页面的成本
  2. random_page_cost: 4.0
    • 含义: 随机读取一个磁盘页面的成本
  3. cpu_tuple_cost: 0.01
    • 含义: 处理每个元组的CPU成本
  4. cpu_index_tuple_cost: 0.005
    • 含义: 处理每个索引元组的CPU成本
  5. cpu_operator_cost: 0.0025
    • 含义: 执行每个操作符或函数的CPU成本
  6. pages:
    • 含义: 表或索引的页面数,根据实际表或索引大小计算,没有固定默认值
  7. tuples:
    • 含义: 预计处理的元组数,基于表统计信息,没有固定默认值
  8. index_pages_fetched:
    • 含义: 预计读取的索引页面数,根据索引统计信息计算,没有固定默认值
  9. tuples_fetched:
    • 含义: 通过索引获取的元组数,根据查询条件和统计信息估算,没有固定默认值
  10. bitmap_pages:
    • 含义: 位图中的页面数,根据查询条件和统计信息估算,没有固定默认值
  11. tids_fetched:
    • 含义: 通过TID扫描获取的元组数,根据查询条件确定,没有固定默认值

重要的是要注意,这些成本参数(如seq_page_cost, random_page_cost等)是相对值,它们之间的比例关系比绝对值更重要。优化器使用这些值来比较不同执行计划的相对成本,从而选择最优计划。

您可以通过修改postgresql.conf文件或使用SET命令来调整这些参数,以更好地适应您的特定硬件和工作负载。在调整这些参数时,建议进行充分的测试,以确保更改确实提高了查询性能。

🌟 总结

  1. 基于统计信息
    • PostgreSQL 利用表和索引的统计信息(如行数、页面数、行宽度、唯一值数目等)来估算每种操作的成本。
  2. 成本的组成
    • 代价模型综合考虑了I/O成本(顺序扫描、随机读取)、CPU成本(处理行和执行操作符)以及其他初始化或启动成本,来估算每种查询操作的代价。
  3. 多路径评估
    • 对于每个查询,优化器会生成多种可行路径,并基于代价模型对这些路径进行评估,选择总成本最低的路径。
  4. 灵活性
    • 代价模型中的参数(如seq_page_costcpu_tuple_cost等)可以根据数据库的实际硬件环境和应用场景进行调优,以获得更好的查询性能。

通过这种代价模型,PostgreSQL 能够在复杂查询的多种执行路径中选择最优路径,确保高效的查询执行。

🌐 PawSQL产品家族

  • PawSQL优化平台 - 面向应用开发者,只需三步即可智能优化SQL性能。该平台提供丰富的查询重写功能、智能索引推荐以及自动化性能验证,通过语义等价转换和执行计划分析,最大限度提升SQL执行效率。
  • PawSQL审核平台 - 面向DevOps管控人员,集成完备的审核规则集,能覆盖数据定义语言(DDL)、数据操作语言(DML)、数据查询语言(DQL)等各种SQL语法,审核准确率高达95%,有效提升SQL代码质量。
  • PawSQL巡检平台 - 面向数据库运维人员,具备自动化采集和分析慢查询的能力,并提供专业的SQL优化建议。同时支持对数据库对象如表、索引等进行审核巡检,及时发现并规避潜在风险。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-09-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 PawSQL 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 📈代价的组成
  • 📊 成本估算的公式
  • 1. 基础扫描操作的成本估算函数
    • 1.1 cost_seqscan - 顺序扫描的成本估算
      • 1.2 cost_index - 索引扫描的成本估算
        • 1.3 cost_indexonlyscan - 索引仅扫描的成本估算
          • 1.4 cost_bitmap_heap_scan - 位图堆扫描的成本估算
            • 1.5 cost_bitmap_tree_node - 位图索引扫描的成本估算
              • 1.6 cost_subqueryscan - 子查询扫描的成本估算
                • 1.7 cost_functionscan - 函数扫描的成本估算
                  • 1.8 cost_valuesscan - 值扫描的成本估算
                    • 1.9 cost_ctescan - 公用表表达式扫描的成本估算
                      • 1.10 cost_tablefuncscan - 表函数扫描的成本估算
                        • 1.11 cost_tidscan - TID 扫描的成本估算
                          • 1.12 cost_worktablescan - 工作表扫描的成本估算
                            • 1.13 cost_foreignscan - 外部数据源扫描的成本估算
                            • 2. 连接操作的成本估算函数
                              • 2.1 cost_nestloop - 嵌套循环连接的成本估算
                                • 2.2 cost_mergejoin - 归并连接的成本估算
                                  • 2.3 cost_hashjoin - 哈希连接的成本估算
                                  • 3. 其他操作的成本估算函数
                                    • 3.1 cost_sort - 排序操作的成本估算
                                      • 3.2 cost_material - 物化操作的成本估算
                                        • 3.3 cost_agg - 聚合操作的成本估算
                                          • 3.4 cost_windowagg - 窗口聚合操作的成本估算
                                            • 3.5 cost_group - 分组操作的成本估算
                                              • 3.6 cost_unique - 唯一操作的成本估算
                                                • 3.7 cost_setop - 集合操作的成本估算
                                                  • 3.8 cost_limit - LIMIT 操作的成本估算
                                                    • 3.9 cost_result - Result 节点的成本估算
                                                      • 3.10 cost_append - Append 操作的成本估算
                                                        • 3.11 cost_recursive_union - 递归 UNION 操作的成本估算
                                                        • 4. 路径成本估算函数
                                                          • 4.1 get_path_cost - 获取路径成本
                                                            • 4.2 cost_index_path - 索引路径的成本估算
                                                              • 4.3 cost_bitmap_and_node - 位图 AND 节点的成本估算
                                                                • 4.4 cost_bitmap_or_node - 位图 OR 节点的成本估算
                                                                • 5. 其他相关辅助函数
                                                                  • 5.1 get_reltuples - 获取关系的行数
                                                                    • 5.2 get_relwidth - 获取关系的宽度
                                                                      • 5.3 clamp_row_est - 行估算值的限制
                                                                      • 🛠️ 公式中的参数
                                                                        • 🌟 总结
                                                                          • 🌐 PawSQL产品家族
                                                                          相关产品与服务
                                                                          云数据库 MySQL
                                                                          腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
                                                                          领券
                                                                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档