关闭

首次Oracle SQL Tuning经历

534人阅读 评论(0) 收藏 举报

这篇文章跟我一样的菜鸟可以看看。事情的缘由在上一篇Blog:如何判断程序处于I/O等待里面已经介绍过了。这件事情我也得到一个教训,QA环境和Production环境还是很不同的,特别是DB 操作,QA环境数据库数据量比较小,SQL执行计划在Production上可能得出完全不同的结果,而我的遭遇就是一条SQL在Production上一跑就再也回不来了,最后只好让DBA kill掉那个任务。

原来的SQL是这样的:一共牵涉5张表,表名和 每张表的索引情况如下:

Table Index Name Index Infomation
Table_Item Item_PK PRIMARY KEY (ID)
Table_Item Item_LM_IDX Last_Modified Asc
Table_Attr Attr_PK PRIMARY KEY (ID)
Table_Desc Desc_PK PRIMARY KEY (ID)
Table_Delay Delay_PK PRIMARY KEY (ID, PARTITION_KEY)
Table_Product Product_P_PK PRIMARY KEY(Product_ID)

select … from , Table_Desc , Table_Product , Table_Attr , Table_Delayed
where
(Table_Item.id = Table_Desc.id)
and (Table_Item.product_id = Table_Product.product_id(+))
and (Table_Item.id = Table_Attr.id(+))
and (Table_Item.id = Table_Delay.id(+))
and (Table_Item.last_modified between to_date( :start_time, ‘YYYY-MM-DD HH24:MI:SS’ )
                                                and to_date( :end_time , ‘YYYY-MM-DD HH24:MI:SS’ ) 
      )
order by Table_Item.last_modified;

使用的DB Hint 是:

/*+ index(table_item item_lm_idx) use_nl(table_delay) index(table_delay delay_pk) */

SQL>  explain plan for
select …

SQL>  @?/rdbms/admin/utlxpls

由于打印格式不同,我就不把执行计划贴出来了,执行情况良好:

  1. 所有的表都使用的了索引,Index Range Scan或者Index Unique Scan
  2. 执行Join的时候使用了Nested Loops或者Nested Loops Outer
  3. 没有发现Sort Join和Merge Join
  4. 没有发现Table Access Full –> Full Table Scan

我们发现即使在SQL中有order by table_item.last_modified,在SQL执行计划中也没有出现任何Sort有关的信息,一般在SQL中尽量不要使用order by,因为在DB Server端进行Sort是很耗费资源的。但是有一个例外,就是表中的数据已经排好序的情况下,我们这里就是这样的哦情况,执行计划中没有Sort是因为当从Table_Item表中以 Table_Item.last_modified between :start_time and :end_time取数据时,走的是Item_LM_IDX –>Last_Modified Asc, 所以取出来的数据本身已经按照last_modified 排好序了。

但是我更改过的SQL是这样的:

where Table_Item.last_modified=:start_time and Table_Item.ID>:last_id order by Table_Item.ID

让我悲哀的是这条修改过的SQL在有些数据库服务器上的执行计划是好的,有些不好。DBA给我的解释是并不是所有的表的统计信息都是更新过的,于是在那些统计信息没有更新的表上的执行计划很有可能就不对了。不如说这个:

00 SELECT STATEMENT
01     MERGE JOIN OUTER
02         NESTED LOOPS OUTER(将Item,Product,Desc的结果和Table_Delay进行Nested Join)
03             MERGE JOIN(由两个SORT JOIN的结果组成,得到Item,Product,Desc3张表的Join结果)
04                 SORT JOIN(将Table_Item和Table_Product的结果按照ID进行排序)
05                    NESTED LOOPS OUTER(将Table_Item和Table_Product进行Nested Outer Join)
06                         TABLE ACCESS BY INDEX ROWID   –> Table_Item
07                             INDEX RANGE SCAN                 –> Item_LM_IDX(使用Item_LM_IDX索引)
08                         TABLE ACCESS BY INDEX ROWID   –> Table_Product
09                             INDEX UNIQUE SCAN               –>  Product_P_PK
10                 SORT JOIN(将Table_Desc满足条件的记录按照ID进行排序)
11                    TABLE ACCESS BY INDEX ROWID       –> Table_Desc
12                          INDEX RANGE SCAN                   –>  RTable_DESC_PK
13             PARTITION RANGE ALL
14                 TABLE ACCESS BY LOCAL INDEX ROWID  –> Table_Delay
15                     INDEX RANGE SCAN                         –> Delay_PK
16         SORT JOIN
17             TABLE ACCESS FULL                                –> Table_Attr

注意到使用了很多Merge Sort,而处理Table_Attr的时候更是出现了Full Table Scan,更本就不使用索引。这里说句老实话,我不知道是不是应该DBA更新统计信息,重新给相关的表更新一下统计信息,还是就赶紧改一下DB Hint把自己的事情做完就算。不过我也理解的,Ebay的数据库太大了,不是随便想做什么就行的,最后我还是选择了修改DB Hint.

Ticket resolve的结果是,修改Db Hint :

/*+
index_asc(Table_Attr ATTR_PK)
index(Table_Item Item_LM_IDX)
use_nl(Table_Item Table_Attr Table_Desc Table_Delayed Table_Product) index(Table_Delay DELAY_PK)
index(Table_Product PRODUCT_P_PK)
index(Table_Desc RTable_DESC_PK)
*/

给所有的表都制定Index,建议Oracle对所有的表都使用Nested Loops Join。重新看一下执行计划:

00  SELECT STATEMENT
01      SORT ORDER BY
02          NESTED LOOPS OUTER
03              NESTED LOOPS OUTER
…(以下省略)

可以看到,修改DB Hint以后,只有对最后的结果进行SORT,这也是我们的初衷。没有SORT JOIN和Merge Join了。速度也快了很多。因为Ebay的DB是Split的,DBA为了保险,帮我把这条修改过的SQL对该数据库所有的Splits都运行了一次,才把Ticket标识成Resolved.

我刚把代码改了,添加了判断,如果运行新的SQL就使用修改的Hint。待会给QA测试,IDC的Sunil还不肯发BUG给我,因为他在QA环境没有观察到这个问题,甚至的Production StandBy DB也不能重现。这一点只能用楼上DBA的话来解释了:虽然是同一个DB的不同的Split,但是不同的Splits之间数据还是有差异,统计数据也不同,结果是影响Oracle做出判断,所以他们做SQL Review的时候都是要运行一下的,而不是光看看执行计划。

谢谢COC DBA Team的amao和alex给我上了一课。          

 

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:78710次
    • 积分:1441
    • 等级:
    • 排名:千里之外
    • 原创:60篇
    • 转载:0篇
    • 译文:8篇
    • 评论:16条
    最新评论