sql性能调整-总结

每种类型的语句都需要以下阶段:
第一步,创建游标
第二步,分析语句Parse the statement
第五步,绑定变量bind any variables
第七步,运行语句run the statement
第九步,关闭游标close thecursor
如果使用了并行功能,还包含:
第六步,并行执行语句parallelize the statement
如果是查询语句,还需要:
第三步,描述查询的结果集describe results of a query
第四步,定义查询的输出数据define output of a query
第八步,取查询出来的行fetch rows of a query

分析语句阶段的操作:
翻译sql语句,验证合法性(书写是否正确)
实现数据字典的查找,验证是否符合表和列的定义
在所要求对象上获取语法分析锁,使语句在语法分析过程中不改变对象的定义
验证为存取所涉及的模式对象所需的权限是否满足
决定语句的最佳执行计划
将他装入共享sql区
将分布的语句全部或部分路由到包含所涉及数据的远程节点


下面就是基于规则的优化器使用的执行路径与各个路径对应的等级:
RBO Path 1: Single Row by Rowid(等级最高)
RBO Path 2: Single Row by Cluster Join
RBO Path 3: Single Row by Hash Cluster Key with Unique or Primary Key
RBO Path 4: Single Row by Unique or Primary Key
RBO Path 5: Clustered Join
RBO Path 6: Hash Cluster Key
RBO Path 7: Indexed Cluster Key
RBO Path 8: Composite Index
RBO Path 9: Single-Column Indexes
RBO Path 10: Bounded Range Search on Indexed Columns
RBO Path 11: Unbounded Range Search on Indexed Columns
RBO Path 12: Sort Merge Join
RBO Path 13: MAX or MIN of Indexed Column
RBO Path 14: ORDER BY on Indexed Column
RBO Path 15: Full Table Scan(等级最低)
上面的执行路径中,RBO认为越往下执行的代价越大,即等级越低
基于规则的索引按照上面的顺序进行查找,有等级低的优先使用

 


重新分析表:
analyze table tablename compute statistics for table;
基于代价的优化器需要定期对表进行重新分析,一周左右即可。

 


判断当前数据库使用何种优化器:
主要是由optimizer_mode初始化参数决定的。可能的取值为:first_rows_[1 | 10 | 100 | 1000] | first_rows | all_rows | choose | rule。

RULE为使用RBO优化器。
CHOOSE则是根据实际情况,如果数据字典中包含被引用的表的统计数据,即引用的对象已经被分析,则就使用CBO优化器,否则为RBO优化器。
ALL_ROWS为CBO优化器使用的第一种具体的优化方法,是以数据的吞吐量为主要目标,以便可以使用最少的资源完成语句。
FIRST_ROWS为优化器使用的第二种具体的优化方法,是以数据的响应时间为主要目标,以便快速查询出开始的几行数据。
FIRST_ROWS_[1 | 10 | 100 | 1000] 为优化器使用的第三种具体的优化方法,让优化器选择一个能够把响应时间减到最小的查询执行计划,以迅速产生查询结果的前 n 行。该参数为ORACLE 9I新引入的。

==============================================
执行计划

1.共享sql语句:
执行一条sql语句时,若内存中存在与之完全相同的语句及对应的执行计划,则直接得到该语句的执行计划。
这要求有大的共享池(shared_buffer_pool)并尽可能的使用绑定变量的方法执行sql语句。

2.recursive sql 为了执行用户发出的一个sql,oracle可能执行的一些额外操作

3.row source 行源
查询中由上一个操作返回的符合条件的行的集合

4.predicate 谓词
一个查询中的where限制条件

5.driving table驱动表(outer table)
where后面的表或行源(row source 1)

6.probed table 被探查表(inner table)
在驱动表中得到具体一行的数据后,从该表中寻找符合连接条件的行。row source 2

7.组合索引 concatenated index
索引由多列组成

8.可选择性selectivity
唯一键的数量/表中的行数 比值越接近1,该列的可选择性越高,越适合建立索引。


执行计划可以通过树状图来表现,如果父步骤只需要单一的子步骤就可执行,可以在树上串联上去,使用first_rows作为优化目标。
对每个由子步骤依次检索出来的每一行,oracle就实现父步骤及所有串联在一起的步骤一次。

有些父步骤在被实现之前需要子步骤的所有行,直到所有子步骤返回之前oracle不能实现该父步骤。包括排序、排序与合并的连接,组功能和总计,这些不能使用first_rows,可使用all_rows作为优化目标

访问路径--access path
在物理层oracle读取数据的最小单位是数据块,一次读取的最大值有操作系统一次I/O的最大值和multiblock参数共同决定

逻辑上访问数据的方法:
(1)全表扫描Full Table Scans (FTS)
顺序读取分配给表的每个数据块,直到最高水位线(HWM);多块读操作可以是一次I/O读取多个数据块。只有全表扫描可以使用多块读操作。
delete不会改变HWM,全表扫描时间不会缩短;truncate可以收缩HWM。10g以后可以人工收缩HWM。

全表扫描用于数据较少或数据量超过总量的5%-10%

(2)通过ROWID的表存取Table Access By ROWID
rowid指出了该行所在的数据文件、数据块以及行在块中的位置,rowid是获取单行数据的最快方法
一次I/O只读取一个数据块。

(3)索引扫描(Index Scan)
通过index查找到数据所对应的rowid值,在根据rowid直接从表中得到具体的数据。
索引除了存储每个索引的值外,还存储其对应的rowid
索引扫描先扫描索引得到rowid,再根据rowid找到相对应的数据。由于索引经常使用,一般是在内存中的,这一步经常是逻辑I/O,而第二步如果表比较大,一般都是物理I/O,相对费时,因此若用索引读取总数据的5%-10%以上的数据,索引效率下降很快。

 

根据索引的类型与where限制条件的不同,有4种类型的索引扫描:
索引唯一扫描(index unique scan)
索引范围扫描(index range scan)
索引全扫描(index full scan)
索引快速扫描(index fast full scan)


(1)索引唯一扫描index unique scan
如果要对含有某个索引的行为基础创建联合索引,索引的返回值通常是一行

(2)索引范围扫描index range scan
分为三种:
在唯一索引列上使用了range操作符<、>等
组合索引上只使用了部分列进行查询&#xff0c;导致查询出多行
对非唯一索引列上进行的任何查询

&#xff08;3&#xff09;索引全扫描index full scan
只在CBO模式下有效。CBO通过数据统计得知进行全索引扫描比全表扫描更有效时才使用&#xff0c;且此时查询出的数据必须都从索引中直接得到

&#xff08;4&#xff09;索引快速扫描index fast full scan
可以使用多块读功能&#xff0c;也可使用并行读入&#xff0c;以获取最大吞吐量和缩短执行时间。


表之间的连接
join是一种试图将两个表结合在一起的谓词&#xff0c;一次只能连接两个表&#xff08;row source&#xff09;。
row resource之间的连接顺序对查询效率有很大的影响&#xff0c;先存取特定表作为驱动表&#xff0c;获得较小的row source&#xff0c;可以提高效率。

根据连接条件可分为等值连接、非等值连接、外连接
根据连接类型分为&#xff1a;
排序--合并连接 sort merge join&#xff08;SMJ&#xff09;
嵌套循环nested loops&#xff08;NL&#xff09;
哈希连接hash join


排序 - - 合并连接(Sort Merge Join, SMJ)
内部连接过程&#xff1a;
1) 首先生成row source1需要的数据&#xff0c;然后对这些数据按照连接操作关联列(如A.col3)进行排序。
2) 随后生成row source2需要的数据&#xff0c;然后对这些数据按照与sort source1对应的连接操作关联列(如B.col4)进行排序。
3) 最后两边已排序的行被放在一起执行合并操作&#xff0c;即将2个row source按照连接条件连接起来
排序费时、费资源&#xff0c;但如果2个row resource都已经排序了&#xff0c;SMJ连接方法效率还是很高的

嵌套循环NL
一般选择能够产生较少row resource的表作为驱动表&#xff0c;进行2层的嵌套循环效率比较高。但有时不遵守此规律。
优点是&#xff1a;可以先返回已经连接的行&#xff0c;不必等待所有的连接操作处理完才返回数据。

哈希连接&#xff0c;理论上比SMJ、NL效率高&#xff0c;只用在CBO优化器中。使哈希连接生效&#xff0c;要设置HASH_JOIN_ENABLED&#61;TRUE&#xff0c;还要注意hash_area_size参数。


应用范围&#xff1a;
排序--合并连接&#xff08;SMJ&#xff09;
a&#xff09;非等值连接
b&#xff09;关联列上都有索引&#xff0c;效果更好
c&#xff09;将两个较大的row source做连接&#xff0c;比NL效果好
d&#xff09;如果sort merge返回的row source过大&#xff0c;会导致使用过多的rowid&#xff0c;性能下降

嵌套循环&#xff08;NL&#xff09;
a&#xff09;在driving row source&#xff08;外部表&#xff09;比较小&#xff0c;在inner row source&#xff08;内部表&#xff09;上有唯一索引或高选择性非唯一索引时&#xff0c;效率较高

哈希连接&#xff08;HJ&#xff09;
a&#xff09;在2个较大的row source 之间连接时会取得相对较好的效率&#xff0c;一个row source较小时效率更好
b&#xff09;只能用于等值连接中


笛卡尔积
一般是由于编码时忘了写关联条件&#xff0c;执行计划中带有cartesian&#xff0c;得到的结果是几个结果的乘积。

如何产生执行计划&#xff1a;
1.
set autotrace on
sql语句
若不想看到执行结果&#xff0c;只看执行计划&#xff0c;执行
set autotrace traceonly


如果执行该语句时遇到错误&#xff0c;解决方法为&#xff1a;
(1&#xff09;在要分析的用户下&#xff1a;
Sqlplus > &#64; ?\rdbms\admin\utlxplan.sql
(2) 用sys用户登陆 31
Sqlplus > &#64; ?\sqlplus\admin\plustrce.sql
Sqlplus > grant plustrace to user_name; - - user_name是上面所说的分析用户

2&#xff0e;用explain plan命令
(1) sqlplus > &#64; ?\rdbms\admin\utlxplan.sql
(2) sqlplus > explain plan set statement_id &#61;’???’ for select ………………
此方法并不执行sql语句&#xff0c;所以只会列出执行计划&#xff0c;不会列出统计信息&#xff0c;并且执行计划只存在plan_table中

找出耗资源的语句&#xff1a;
SELECT ADDRESS,
substr(SQL_TEXT,1,20) Text,
buffer_gets,
executions,
buffer_gets/executions AVG
FROM v$sqlarea
WHERE executions>0
AND buffer_gets > 100000
ORDER BY 5;


3&#xff0e;用dbms_system存储过程生成执行计划

Optimizer&#61;CHOOSE 指明这个查询的optimizer_mode&#xff0c;即optimizer_mode初始化参数指定的值&#xff0c;它并不是指语句执行时真的使用了该优化器。决定该语句使用何种优化器的唯一方法是看后面的cost部分。
如果cost有值&#xff0c;则是CBO模式&#xff1b;如果cost部分为空或没有cost&#xff0c;则使用的是RBO模式。
特别的&#xff0c;如果Optimizer&#61;ALL_ROWS| FIRST_ROWS| FIRST_ROWS_n&#xff0c;则使用的是CBO优化器&#xff1b;如果Optimizer&#61;RULE&#xff0c;则使用的是RBO优化器。

 

得到执行计划中哪个表应该为驱动表&#xff1a;
在执行计划中&#xff0c;需要知道哪个操作是先执行的&#xff0c;哪个操作是后执行的&#xff0c;这对于判断哪个表为驱动表有用处。判断之前&#xff0c;如果对表的访问是通过rowid&#xff0c;且该rowid的值是从索引扫描中得来得&#xff0c;则将该索引扫描先从执行计划中暂时去掉。然后在执行计划剩下的部分中&#xff0c;判断执行顺序的指导原则就是&#xff1a;最右、最上的操作先执行

 


CBO根据统计信息选择驱动表&#xff0c;假如没有统计信息&#xff0c;则在from 子句中从左到右的顺序选择驱动表

 


----------------------------------------
使用hint提示干预执行计划

 

基于代价的优化器绝大多数时间可以提供正确的优化器&#xff0c;但有时不够优化&#xff0c;需要人工干预。
用hint可实现的功能:
&#xff08;1&#xff09;使用优化器的类型
&#xff08;2&#xff09;基于代价的优化器的优化目标&#xff0c;是all_rows还是first_row
&#xff08;3&#xff09;表的访问路径&#xff0c;是全表扫描、索引扫描还是直接利用rowid
&#xff08;4&#xff09;表之间的连接类型
&#xff08;5&#xff09;表之间的连接顺序
&#xff08;6&#xff09;语句的并行程度


使用CBO或HINTS提示&#xff0c;最好对表和索引进行定期的分析

hints只应用在它们所在sql语句块&#xff08;statement block&#xff0c;由select update delete关键字标识&#xff09;上&#xff0c;对其他语句或语句的其他部分没有影响。
可以使用注释comment来为一个语句添加hints&#xff0c;一个语句只能有一个注释&#xff0c;且只能在select 、update、delete关键字后面

语法&#xff1a;
delete|insert|select|update /*&#43;hint [text][hint[text]]……*/
或者语句在一行的话&#xff0c;使用“--”作为注释符

注意&#xff1a;
&#43;表示该注释是一个hints&#xff0c;且“/*”与“&#43;”间不能有空格
text是其他说明hint的注释行文本
如果没有正确指定hints&#xff0c;oracle忽略该hints&#xff0c;并不报错


使用全套的hints
为了确保优化器产生最优的执行计划&#xff0c;可能不但指定要使用的索引&#xff0c;而且指定连接方式与连接顺序等

指定优化器的方法与目标的hints
all_rows  基于代价的优化器&#xff0c;以吞吐量为目标
first_rows&#xff08;n&#xff09; 基于代价的优化器&#xff0c;以响应时间为目标
choose   根据是否有统计信息&#xff0c;选择不同的优化器
rule    使用基于规则的优化器


&#xff08;1&#xff09;指定存储路径的hints&#xff1a;
/*&#43;FULL(table)*/  指定该表使用全表扫描
/*&#43;ROWID(table)*/ 指定该表使用rowid存取方法&#xff0c;用的较少
/*&#43;INDEX(table[index])*/ 使用该表上指定的索引对表进行索引扫描
/*&#43;INDEX_FFS(table[index]))*/ 使用快速全表扫描
/*&#43;NO_INDEX(table[index])*/   不适用该表上指定的索引进行存取&#xff0c;仍可使用其他的索引进行扫描

&#xff08;2&#xff09;指定连接顺序的hints
/*&#43;ordered*/ 按from子句中表的顺序从左到右的连接
/*&#43;star*/ 指示优化器使用星型查询

&#xff08;3&#xff09;指定连接类型的hints&#xff1a;
/*&#43;USE_NL(table[,table,……])*/  使用嵌套连接
/*&#43;USE_MERGE(table[,table,……])*/ 使用排序-合并连接
/*&#43;USE_HASH(table[,table,……])*/ 使用HASH连接


对于use_nl与use_hash&#xff0c;建议同ordered提示一起使用&#xff0c;否则不容易指定哪个表是驱动表

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

 

其他注意事项

不管optimizer_mode参数设为什么&#xff0c;只要满足三个条件&#xff0c;就是用CBO
1&#xff09;使用index only tables&#xff08;IOTs&#xff09;&#xff0c;自动使用CBO
2&#xff09;如果表上的paralle degree option设为>1,自动使用CBO&#xff0c;不管是否用rule hints
3&#xff09;除rule外的任何hints都将导致自动使用CBO执行语句


总结一下&#xff0c;一个语句在运行时到底使用何种优化器可以从下面的表格中识别出来&#xff0c;从上到下看你的语句到底是否满足description列中描述的条件&#xff1a;
Description 对象是否被分析 优化器的类型
~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~
Non-RBO Object (Eg:IOT) n/a #1
Parallelism > 1 n/a #1
RULE hint n/a RULE
ALL_ROWS hint n/a ALL_ROWS
FIRST_ROWS hint n/a FIRST_ROWS
*Other Hint n/a #1
OPTIMIZER_GOAL&#61;RULE n/a RULE
OPTIMIZER_GOAL&#61;ALL_ROWS n/a ALL_ROWS
OPTIMIZER_GOAL&#61;FIRST_ROWS n/a FIRST_ROWS
OPTIMIZER_GOAL&#61;CHOOSE NO RULE
OPTIMIZER_GOAL&#61;CHOOSE YES ALL_ROWS
#1 表示除非OPTIMIZER_GOAL 被设置为FIRST_ROWS &#xff0c;否则将使用ALL_ROWS。在PL/SQL中&#xff0c;则一直是使用ALL_ROWS
*Other Hint 表示是指除RULE、ALL_ROWS 和FIRST_ROWS以外的其它提示
2) 当CBO选择了一个次优化的执行计划时, 不要同CBO过意不去, 先采取如下措施:
a) 检查是否在表与索引上又最新的统计数据
b) 对所有的数据进行分析&#xff0c;而不是只分析一部分数据
c) 检查是否引用的数据字典表&#xff0c;在oracle 10G之前&#xff0c;缺省情况下是不对数据字典表进行分析的。
d) 试试RBO优化器&#xff0c;看语句执行的效率如何&#xff0c;有时RBO能比CBO产生的更好的执行计划
e) 如果还不行&#xff0c;跟踪该语句的执行&#xff0c;生成trace信息&#xff0c;然后用tkprof格式化trace信息&#xff0c;这样可以得到全面的供优化的信息。
3) 假如利用附录的方法对另一个会话进行trace&#xff0c;则该会话应该为专用连接
4) 不要认为绑定变量(bind variables)的缺点只有书写麻烦&#xff0c;而优点多多&#xff0c;实际上使用绑定
53
变量虽然避免了重复parse&#xff0c;但是它导致优化器不能使用数据库中的列统计&#xff0c;从而选择了较差的执行计划。而使用硬编码的SQL则可以使用列统计。当然随着CBO功能的越来越强&#xff0c;这种情况会得到改善。目前就已经实现了在第一次运行绑定变量的sql语句时&#xff0c;考虑列统计。
5) 如果一个row source 超过10000行数据&#xff0c;则可以被认为大row source
6) 有(&#43;)的表不是driving table&#xff0c;注意&#xff1a;如果有外联接&#xff0c;而且order hint指定的顺序与外联结决定的顺序冲突&#xff0c;则忽略order hint
7) 影响CBO选择execution plan的初始化参数:
这些参数会影响cost值
ALWAYS_ANTI_JOIN
B_TREE_BITMAP_PLANS
COMPLEX_VIEW_MERGING
DB_FILE_MULTIBLOCK_READ_COUNT
FAST_FULL_SCAN_ENABLED
HASH_AREA_SIZE
HASH_JOIN_ENABLED
HASH_MULTIBLOCK_IO_COUNT
OPTIMIZER_FEATURES_ENABLE
OPTIMIZER_INDEX_CACHING
OPTIMIZER_INDEX_COST_ADJ
OPTIMIZER_MODE> / GOAL
OPTIMIZER_PERCENT_PARALLEL
OPTIMIZER_SEARCH_LIMIT
PARTITION_VIEW_ENABLED
PUSH_JOIN_PREDICATE
SORT_AREA_SIZE
SORT_DIRECT_WRITES
SORT_WRITE_BUFFER_SIZE
STAR_TRANSFORMATION_ENABLED
V733_PLANS_ENABLED
CURSOR_SHARING


&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;
附录&#xff1a;

如何通过跟踪一个客户端程序发出的sql的方法来优化SQL


简要说来&#xff0c;跟踪一个客户程序发出的SQL主要分成下面几步&#xff1a;
1) 识别要跟踪的客户端程序到数据库的连接(后面都用session代替)&#xff0c;主要找出能唯一识别一个session的sid与serial#.
2) 设定相应的参数&#xff0c;如打开时间开关(可以知道一个sql执行了多长时间)&#xff0c;存放跟踪数据的文件的位置、最大值。
3) 启动跟踪功能
4) 让系统运行一段时间&#xff0c;以便可以收集到跟踪数据
5) 关闭跟踪功能
6) 格式化跟踪数据&#xff0c;得到我们易于理解的跟踪结果。