PG顺序扫描和索引扫描代价估算

一、顺序扫描

select * from tb1 where id<8000;

公式:启动代价+运行代价
顺序扫描启动代价为0.

run_cost = cpu_run_cost+disk_run_cost
		 = (cpu_tuple_cost+cpu_operator_cost)*num_tuple+seq_page_cost*num_page;

seq_page_cost、cpu_tuple_cost、cpu_operator_cost可以通过参数配置来确定

test=# show seq_page_cost;

 seq_page_cost 
---------------

 1
(1 row)

test=# show cpu_tuple_cost
test-# ;

 cpu_tuple_cost 
----------------

 0.01
(1 row)

test=# show cpu_operator_cost;

 cpu_operator_cost 
-------------------

 0.0025
(1 row)


num_tuple和num_page可以查看数据字典

test=# select relpages,reltuples from pg_class where relname='tb1';
 relpages | reltuples 
----------+-----------
       23 |     10000
	   
	   
可得 run_cost=(0.01+0.0025)*10000+1*23=148
顺序扫描代价=0+148=148。

验证
test=# explain select * from tb1 where id<8000;

                       QUERY PLAN                       
--------------------------------------------------------

 Seq Scan on tb1  (cost=0.00..148.00 rows=7999 width=8)
   Filter: (id < 8000)
(2 rows)

前面的0.00为启动代价;后面的148.00为总代价

二、索引扫描

假设查询:select id ,data from tb1 where date<240;
num_page和num_tuple 值参数上面
num_index_page 和num_index_tuple如下

test=# select relpages,reltuples from pg_class where relname='tb1_data_idx';
 relpages | reltuples 
----------+-----------
       16 |     10000
	   

启动代价:
启动代价就是读取索引页以访问第一条元组的代价,公式如下:

startup_cost={ceil(log2(num_index_tuple))+(index_high+1)*50}*cpu_operator_cost

ceil为取整函数
log2为取对数 即 2的多少次方等于括号内的值

index_high为索引高度,目前值为1

startup_cost={ceil(log2(10000)+(1+1)*50}*0.0025=0.2825

运行代价:
索引的运行代价是表和索引的CPU代价与IO代价之和。

run_cost=(index_cpu_cost+table_cpu_cost)+(index_io_cost+table_io_cost)

如果使用仅索引扫描,则不会估计表的CPU和IO消耗

index_cpu_cost=Snum_index_tuple(cpu_index_tuple_cost+qual_op_cost)
table_cpu_cost=Snum_tuplecpu_tuple_cost
index_io_cost=ceil(S*num_index_page)*random_page_cost

test=# show cpu_index_tuple_cost;

 cpu_index_tuple_cost 
----------------------

 0.005
(1 row)
test=# show cpu_tuple_cost;

 cpu_tuple_cost 
----------------

 0.01
(1 row)

test=# show random_page_cost;

 random_page_cost 
------------------

 4
(1 row)

test=# 

qual_op_cost粗略的来说是索引求值的代价,默认是0.0025

S是指选择率 Selectivity,S*元组总数即为估计要读取的元组的数量 

查询谓词的选择率是通过直方图界值与高频值估计的、这些信息在pg_stats视图可以查到
 most_common_freqs 即为选择率

select schemaname,tablename, most_common_vals,most_common_freqs 
 from pg_stats where tablename='tb1';
 schemaname | tablename | most_common_vals | most_common_freqs 
------------+-----------+------------------+-------------------
 public     | tb1       |                  | 
 public     | tb1       |                  | 
(2 rows)

test=# 

如果高频直方值不可用,则采用目标列上的直方图界值计算

test=# select histogram_bounds from pg_stats 
test-# where tablename='tb1' and attname='data';
                                                                                                                                                                                                   

                                  histogram_bounds                                                     

-------------------------------------------------------------------------------------------------------

 {1,100,200,300,400,500,600,700,800,900,1000,1100,1200,1300,1400,1500,1600,1700,1800,1900,2000,2100,220
0,2300,2400,2500,2600,2700,2800,2900,3000,3100,3200,3300,3400,3500,3600,3700,3800,3900,4000,4100,4200,4
300,4400,4500,4600,4700,4800,4900,5000,5100,5200,5300,5400,5500,5600,5700,5800,5900,6000,6100,6200,6300
,6400,6500,6600,6700,6800,6900,7000,7100,7200,7300,7400,7500,7600,7700,7800,7900,8000,8100,8200,8300,84
00,8500,8600,8700,8800,8900,9000,9100,9200,9300,9400,9500,9600,9700,9800,9900,10000}
(1 row)

test=# 

从上述查询可以看到,10000个值被分为100个bucket,桶从0开始,每个桶保存了大概相同数量的元组。直方图界值就是相应桶的边界。
比如直方图界值的第0个值是1 ,即 这个bucket_0中最小的值为1,hb(0)=1。bucket_1 的界值为100,即最小值为100,hb(1)=100。

240在第3个桶,即hb(2)
根据界值计算选择率公式

S={2+[240-hb(2)]/[hb(3)-hb(2)]}/100={2+[240-200]/[300-200]}/100=0.024

选择率S即为0.024

带入上面公式

index_cpu_cost=0.02410000(0.005+0.0025)=1.8
table_cpu_cost=0.024100000.01=2.4
index_io_cost=ceil(0.024*16)*4=4

table_io_cost=max_io_cost+indexCorrelation平方*(min_io_cost-max_io_cost)

max_io_cost是最差情况下的IO代价,即扫描所有数据页的代价 max_io_cost=num_pagerandom_page_cost=234=92
min_io_cost是最优情况下的IO代价,即顺序扫描选定的数据页

min_io_cost=1random_page_cost+[ceil{Snum_page)-1]seq_page_cost
=1
4+[ceil(0.024*23)-1]*1=5

indexCorerelation是索引相关性,取值范围从-1 到1 从pg_stats视图可以查看到

select tablename,attname,correlation from pg_stats where tablename='tb1';
 tablename | attname | correlation 
-----------+---------+-------------
 tb1       | id      |           1
 tb1       | data    |           1
(2 rows)

table_io_cost=92+11(5-92)=5

run_cost=(1.8+2.4)+(4+5)=13.2
综上所述 总代价=0.2825+13.2=13.4825

test=#  explain select id ,data from tb1 where data<240;

                                QUERY PLAN                                 
---------------------------------------------------------------------------

 Index Scan using tb1_data_idx on tb1  (cost=0.29..12.47 rows=239 width=8)
   Index Cond: (data < 240)
(2 rows)

seq_page_cost和random_page_cost分表代表顺序扫描和随机扫描 1/4显然是机械硬盘的代价。

对SSD来说,随机扫描代价为4显然太大,可以设置为1

<think>好的,我现在需要回答用户的问题:“如何调用SQL的优化器”。首先,我需要理解用户的需求。用户可能对SQL优化器的工作机制不太熟悉,想知道如何主动调用或干预优化器的行为。用户可能是数据库开发者或管理员,希望优化查询性能,但不知道具体如何操作。 接下来,我应该回顾已有的知识。SQL优化器是数据库管理系统的核心组件,负责生成执行计划。用户无法直接“调用”优化器,但可以通过各种方式影响其决策。之前的对话中,用户询问了LEFT JOIN的优化,现在转向更通用的优化器调用方法。需要区分不同数据库系统的差异,比如MySQL、PostgreSQL、Oracle等,但用户可能希望得到通用的方法。 用户的问题需要明确几个方面:如何触发优化器生成执行计划,如何干预优化器的选择,以及如何利用数据库提供的工具来调整优化器的行为。需要覆盖的主要点包括使用EXPLAIN命令查看执行计划、设置优化器提示、调整数据库参数、更新统计信息等。 需要检查是否有遗漏的优化方法,比如物化视图、索引策略、查询重写等,但重点应放在与优化器直接交互的方法上。同时,要避免过于技术化的术语,保持解释清晰易懂。 还要考虑用户可能的误区,比如认为可以像调用函数一样调用优化器,但实际上优化器是自动运行的,用户只能通过间接方式影响它。需要明确这一点,并给出实际可行的步骤。 最后,组织内容结构,分点说明各种方法,并举例说明,确保用户能逐步理解应用。同时,提醒用户不同数据库系统的差异,建议查阅具体文档。</think>在关系型数据库中,SQL优化器是自动工作的核心组件,用户不能直接"调用"但可以通过以下方式与其交互影响其决策。以下是系统化的方法指南: --- ### **一、查看优化器工作过程(核心方法)** #### 1. 使用 `EXPLAIN` 命令 通过解析执行计划观察优化器决策: ```sql -- 基本语法(通用) EXPLAIN SELECT * FROM table WHERE condition; -- 带实际执行耗时(PostgreSQL/MySQL 8.0+) EXPLAIN ANALYZE SELECT ...; -- 显示详细格式(MySQL示例) EXPLAIN FORMAT=JSON SELECT ...; ``` #### 2. 执行计划关键元素 $$ \text{优化路径示意图:} $$ ``` Seq Scan → Index Scan → Hash Join ↘ Nested Loop ↗ ``` - **Cost值**:估算代价单位(相对值) - **Rows**:预计处理行数 - **Width**:行平均字节数 --- ### **二、干预优化器决策的6种方式** #### 1. 优化器提示(Hints) ```sql /*+ 提示语法 */ -- MySQL示例: SELECT /*+ INDEX(t idx_name) */ * FROM table t; -- PostgreSQL示例: SET enable_seqscan = off; -- 禁用全表扫描 ``` #### 2. 统计信息管理 ```sql -- 手动更新统计信息(通用) ANALYZE table_name; -- Oracle示例: EXEC DBMS_STATS.GATHER_TABLE_STATS('SCHEMA','TABLE'); ``` #### 3. 参数调优 ```sql -- PostgreSQL工作内存设置 SET work_mem = '64MB'; -- MySQL关联优化 SET optimizer_switch='block_nested_loop=off'; ``` #### 4. 索引策略干预 ```sql -- 强制使用特定索引(MySQL) SELECT * FROM table USE INDEX (idx_name); -- 创建覆盖索引 CREATE INDEX idx_cover ON orders (customer_id) INCLUDE (total_price); ``` #### 5. 查询重写技巧 ```sql -- 将IN改为EXISTS SELECT * FROM A WHERE EXISTS (SELECT 1 FROM B WHERE A.id=B.id) -- 拆分复杂查询为多个CTE WITH cte1 AS (...), cte2 AS (...) SELECT ... ``` #### 6. 优化器模式设置 ```sql -- MySQL选择优化器版本 SET optimizer_switch='derived_merge=off'; -- Oracle选择优化器模式 ALTER SESSION SET OPTIMIZER_MODE = FIRST_ROWS_10; ``` --- ### **三、不同数据库的优化器调用特性** | 数据库 | 核心优化器 | 特色工具 | |----------|-----------------------------------|--------------------------| | MySQL | Cost-Based Optimizer (CBO) | `EXPLAIN FORMAT=JSON` | | PostgreSQL| GEQO遗传算法 | `EXPLAIN (BUFFERS,ANALYZE)` | | Oracle | Rule-Based + Cost-Based | `DBMS_XPLAN` 包 | | SQL Server| Cascades框架 | `SET SHOWPLAN_ALL ON` | --- ### **四、诊断优化问题的黄金公式** $$ \text{性能问题定位} = \text{EXPLAIN分析} + \text{实际执行时间比对} + \text{统计信息验证} $$ #### 排查流程: 1. 对比`EXPLAIN`估算行数与实际`COUNT(*)`差异 2. 检查是否存在: - 全表扫描(Seq Scan)未走索引 - 不合理的连接顺序(Join Order) - 错误选择Join算法(Nested Loop vs Hash Join) 3. 验证统计信息最后更新时间 ```sql -- PostgreSQL查看统计 SELECT last_analyzed FROM pg_stat_all_tables WHERE relname='table'; ``` --- ### **五、高级优化器控制(需谨慎)** #### 1. 自定义代价参数(PostgreSQL) ```sql ALTER TABLE table SET (seq_page_cost = 0.5, random_page_cost = 0.8); ``` #### 2. 物化视图预计算 ```sql CREATE MATERIALIZED VIEW mv_data AS SELECT ... FROM ... WHERE ...; -- 定期刷新 ``` #### 3. 直方图收集(Oracle) ```sql EXEC DBMS_STATS.GATHER_TABLE_STATS( ownname => 'SCOTT', tabname => 'EMP', method_opt => 'FOR COLUMNS sal SIZE 254' ); ``` --- ### **最佳实践建议** 1. **渐进式调优**:每次只修改一个参数并记录影响 2. **版本特性**:MySQL 8.0新增了直方图统计,PG14增强并行优化 3. **工具辅助**: - MySQL: `pt-query-digest` - PG: `pg_stat_statements` - Oracle: SQL Tuning Advisor 通过以上方法,开发者可以间接"调用"优化器的决策机制。但需注意:优化器的选择是基于代价模型的数学最优解,强制干预可能适得其反,建议优先保证统计信息准确性合理索引设计。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南風_入弦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值