优化器靠它给每个计划打分
产品作用
在关系型 SQL 引擎里,同一条查询往往有多种执行办法:全表扫描还是走索引,嵌套循环、哈希连接还是归并连接,几张表按什么顺序连。成本估算器(也常叫代价估算、代价模型)为每种办法算出一个数,优化器据此挑出最省的那一个。TiDB 文档把这一步放在物理优化阶段:优化器根据统计信息确定不同物理实现的代价,选择整体代价最小的物理执行计划[8]。它随 SQL 引擎一起发布,用户接触到的是它的输出(EXPLAIN 里的 cost、rows)和一组可调的代价常数。
工作原理
估算分两步。第一步估行数:先从统计信息算出每个过滤条件的选择率,再乘以表的总行数。PostgreSQL 文档的例子里,tenk1 表有 10000 行,unique1 < 1000 落在直方图第二个桶(993–1997)内,按桶内线性分布算得选择率 0.100697,估计行数为 1007[4]。第二步把行数和页数换算成代价:同一张表 345 个页、10000 行,顺序扫描的代价为 345 × 1.0 + 10000 × 0.01 = 445[2]。加上 WHERE 条件后,每行多一次比较,代价再增加 10000 × cpu_operator_cost,变为 470[2]。上层节点的代价包含其所有子节点的代价,所以计划树根节点上的数就是整棵树的总估算[2]。
三块逻辑:统计信息、代价公式、计划树遍历
三部分都在 SQL 引擎内部,以源代码形式存在;用户能调整的是统计信息的收集方式和代价常数。
左右滑动查看完整表格 →
| 部件 | 作用 | 材料与配置 |
|---|---|---|
| 统计信息分析器 | 读取表级和列级统计信息,给代价公式提供行数、页数和选择率。PostgreSQL 把表和索引的行数、页数存放在 pg_class 的 reltuples、relpages 中,由 VACUUM、ANALYZE 和 CREATE INDEX 等命令更新,平时并非实时值[3];列的高频值和直方图存放在 pg_statistic,可通过 pg_stats 视图查看[3]。TiDB 为列建立等深直方图估算范围条件,Version 2 统计信息用 Top-N 和直方图估算等值与 IN 条件[6]。 | SQL 引擎内的软件逻辑;数据来自 ANALYZE 抽样得到的系统表 |
| 代价模型引擎 | 用代价公式把每个扫描、连接、排序节点的行数和页数换算成代价单位。PostgreSQL 的代价单位是相对值,惯例以顺序读一页为 1.0,其余常数以它为基准[1]。TiDB 在物理优化时计算每个索引的访问代价和每个物理算子的执行代价,选代价最低的计划[5]。 | SQL 引擎内的软件逻辑;参数为可配置的代价常数 |
| 计划遍历模块 | 沿执行计划树自下而上累加代价,并在多种连接顺序之间搜索。PostgreSQL 中上层节点的代价包含全部子节点的代价[2];FROM 项达到 geqo_threshold(默认 12)个时,改用遗传查询优化代替穷举搜索,以控制规划耗时[1]。 | SQL 引擎内的软件逻辑 |
调估算时动得到的九个旋钮
以下各项均为 SQL 引擎的配置变量,默认值取自 PostgreSQL 18 与 TiDB v8.5 文档。PostgreSQL 的代价常数只看相对大小,整体同比例放大或缩小,优化器的选择保持不变[1]。
左右滑动查看完整表格 →
| 参数 | 资料与范围 | 选型含义 |
|---|---|---|
| 顺序读页代价 seq_page_cost | PostgreSQL 默认 1.0,可按表空间单独设置[1] | 其余代价常数的基准,一般保持 1.0,只调其他常数的相对比例。 |
| 随机读页代价 random_page_cost | PostgreSQL 默认 4.0[1] | 文档说明这个默认值已假定多数随机读命中缓存;数据能全部放进内存时可调低,机械盘或缓存命中率低时可调高[1]。调低会让优化器更倾向索引扫描。 |
| 估计行数与实际行数之比 | 用 EXPLAIN ANALYZE 读取:PostgreSQL 在同一节点上并列显示估计 rows 与 actual rows[2];TiDB 对应 estRows 与 actRows[7] | 两者相差越大,代价越失真;偏差通常出在统计信息过期、采样太粗或多列相关。 |
| 遗传优化启用阈值 geqo_threshold | PostgreSQL 默认 12 个 FROM 项[1] | 连接表数达到阈值后改用遗传搜索,规划更快,但找到的计划可能比穷举搜索差。 |
| 每行处理代价 cpu_tuple_cost | PostgreSQL 默认 0.01;索引项处理代价 cpu_index_tuple_cost 默认 0.005[1] | 数据多半在内存时,文档建议相对 CPU 常数调低两项读页代价,效果等同于提高 CPU 部分的比重[1]。 |
| 运算符代价 cpu_operator_cost | PostgreSQL 默认 0.0025,每次运算符或函数调用计一次[1] | WHERE 条件越多,这一项累加越多;tenk1 例子中一个比较条件使代价从 445 增至 470[2]。 |
| 统计信息精细度 default_statistics_target | PostgreSQL 默认 100 项,即高频值列表和直方图边界的条目上限,可按列用 ALTER TABLE SET STATISTICS 调整[3];TiDB 用 ANALYZE 的 WITH NUM BUCKETS、WITH NUM TOPN 控制[6] | 分布不均匀的列调高后估算更准,代价是统计信息占用更多空间、规划稍慢[3]。 |
| 代价模型与统计信息版本 | TiDB v6.2.0 引入 Cost Model Version 2,由 tidb_cost_model_version 切换[5];tidb_analyze_version 自 v5.3.0 起默认值为 2[6] | 切换代价模型版本可能引起执行计划变化[5],升级或切换前先在测试环境比对关键 SQL 的计划。 |
| 有效缓存大小 effective_cache_size | PostgreSQL 默认 4GB[1] | 按 shared_buffers 加操作系统可用于缓存数据文件的内存来设;数值越大,索引扫描越容易被选中[1]。 |
EXPLAIN 里 cost=0.00..445.00 这串数各指什么
PostgreSQL 的 EXPLAIN 在每个节点后给出四个估算值:启动代价、总代价、输出行数和平均行宽(字节)[2]。启动代价是开始输出第一行之前的开销,例如排序节点要先排完才能输出;总代价按节点运行到底计算[2]。
这些代价没有时间单位,只能在同一实例、同一组代价常数下互相比较。代价也只覆盖优化器能通过换计划改变的部分,把结果转成文本、传给客户端的时间没有计入[2]。
TiDB 的 EXPLAIN 用 estRows 列显示预计处理的行数,来源可能是主键或唯一键等字典信息,也可能是 CMSketch 或直方图[7]。自 v6.4.0 起,IndexJoin 和 Apply 算子 Probe 端子节点的 estRows 表示总行数,与 actRows 口径一致[7];对比新旧版本的计划时要留意这一点。
估计行数和实际差得远:先查统计信息
排查慢 SQL 时,先用 EXPLAIN ANALYZE 实际执行一次,逐个节点比较估计行数与实际行数[2]。偏差最大的那一层,往往就是计划选错的起点。
常见原因和对应做法如下。
左右滑动查看完整表格 →
| 现象 | 原因 | 做法 |
|---|---|---|
| 表刚经过大批量导入或删除,估计行数明显偏旧 | reltuples、relpages 只在 VACUUM、ANALYZE 等命令后更新[3] | 手动执行 ANALYZE,或调整自动收集的触发条件 |
| 同一条件每次 ANALYZE 后估计行数都略有变化 | 统计信息来自随机抽样[2] | 属正常波动;偏差大时提高该列统计目标 |
| 两列同时作条件时估计行数偏小很多 | 优化器默认按各列相互独立相乘[3] | PostgreSQL 用 CREATE STATISTICS 建扩展统计信息[3];TiDB 有对应的扩展统计信息功能[8] |
| TiDB 等值查询估算偏离实际很大 | Version 1 统计信息使用的 Count-Min Sketch 存在哈希冲突[6] | 改用 Version 2 统计信息,等值与 IN 条件改由 Top-N 与直方图估算[6] |
按存储和内存调代价常数
PostgreSQL 文档提醒,代价常数宜视为整个查询负载的平均值,只凭几次试验就改动风险很大[1]。比较稳妥的顺序是:先把统计信息收准,再看存储类型。数据基本都在内存里、或库容量小于服务器内存时,可调低 random_page_cost;机械盘或缓存命中率低时可调高[1]。
effective_cache_size 只影响估算,按 shared_buffers 加系统页缓存估一个值即可[1]。改完后在测试环境对比关键 SQL 的计划和耗时,再推广到生产实例。
关联的应用系统与部件
以下入口来自产品关系,用于继续查阅;具体机型的装配和兼容性仍需对应型号资料。
应用系统
关联部件
资料来源与适用范围
核对日期:2026-10-17。全部来源为 PostgreSQL 全球开发组和 PingCAP 公开的官方文档,2026-10-17 打开阅读;PostgreSQL 取 18 版,TiDB 取 stable(v8.5)版。其他 SQL 引擎的代价常数名称和默认值各不相同,以各自文档为准。
- PostgreSQL 18 文档 19.7 Query Planning代价常数定义与默认值(seq_page_cost 1.0、random_page_cost 4.0、cpu_tuple_cost 0.01、cpu_index_tuple_cost 0.005、cpu_operator_cost 0.0025、effective_cache_size 4GB、geqo_threshold 12)及调整建议,用于参数表和调常数一节。
- PostgreSQL 18 文档 14.1 Using EXPLAINcost 四个数的含义、tenk1 顺序扫描代价 445 与 470 的算法、上层节点代价包含子节点、EXPLAIN ANALYZE 用法,用于原理与读 cost 一节。
- PostgreSQL 18 文档 14.2 Statistics Used by the Plannerreltuples、relpages 的更新时机,pg_stats 视图,default_statistics_target 默认 100,扩展统计信息 CREATE STATISTICS,用于部件表和估算偏差表。
- PostgreSQL 18 文档 69.1 Row Estimation Examplesunique1 < 1000 的直方图选择率 0.100697 与估计行数 1007 的计算过程,用于原理一段。
- TiDB 文档:代价模型索引访问代价与算子执行代价、Cost Model Version 2 自 v6.2.0 引入、tidb_cost_model_version 切换可能改变计划。
- TiDB 文档:常规统计信息等深直方图、Top-N、Count-Min Sketch 的用途与哈希冲突,tidb_analyze_version 自 v5.3.0 默认 2,ANALYZE 的桶数与 Top-N 选项。
- TiDB 文档:TiDB 执行计划概览estRows 的来源,v6.4.0 起 IndexJoin、Apply 算子 Probe 端 estRows 口径变化,EXPLAIN ANALYZE 的 actRows。
- TiDB 文档:物理优化物理优化是基于代价的优化、按统计信息确定代价,章节内含扩展统计信息与代价模型条目,用于定义一段。
