使用Explain進(jìn)行查詢及應(yīng)用優(yōu)化
2024-07-21 02:38:02
供稿:網(wǎng)友
 
             
  應(yīng)用的優(yōu)化不僅需要知道應(yīng)用作了什么,還必須知道應(yīng)用是如何工作的以及使用何種數(shù)據(jù)庫設(shè)計(jì)來支持,必須了解使用哪種類型的SQL語句,語句中表與視圖的結(jié)構(gòu)及與這些表相關(guān)的各類索引。                                                                                            另外,優(yōu)化整個(gè)應(yīng)用系統(tǒng)可能并不是必需的,了解應(yīng)用的各個(gè)部分可以讓我們了解哪些部分是需要優(yōu)化的。我們將主要討論使用Oracle RDBMS提供的性能優(yōu)化工具進(jìn)行SQL級(jí)的優(yōu)化。 
  
  EXPlain可以用來迅速方便地查出對(duì)于給定SQL語句中的查詢數(shù)據(jù)是如何得到的即搜索路徑(我們通常稱為access Path)。Access Path對(duì)性能會(huì)有非常大的影響。我們將會(huì)討論各種Access Path和使用的優(yōu)缺點(diǎn)。 
  
  使用Explain 
  使用Explain工具需要?jiǎng)?chuàng)建Explain_plan表,這必須先進(jìn)入相關(guān)應(yīng)用表、視圖和索引的所有者的帳戶內(nèi)。Oracle的介質(zhì)中包含有執(zhí)行此項(xiàng)工作的SQL源程序,例如: 
  
  ORA_RDBMS: XPLAINPL.SQL (VMS) 
  
  $ORACLE_HOME/rdbms/admin/utlxplan.sql (UNIX) 
  
  這個(gè)SQL程序應(yīng)與catalog.sql在同一目錄,這個(gè)程序會(huì)創(chuàng)建一個(gè)名為plan_table的表,表結(jié)構(gòu)如下: 
  
  Name Type 
  
  STATEMEN_ID VARCHAR2(30) 
  
  TIMESTAMP DATE 
  
  REMARKS VARCHAR2(80) 
  
  OperaTION VARCHAR2(30) 
  
  OPTIONS VARCHAR2(30) 
  
  Object_node VARCHAR2(128) 
  
  Object_owner VARCHAR2(30) 
  
  Object_name VARCHAR2(30) 
  
  Object_instance NUMBER(38) 
  
  Object_type VARCHAR2(30) 
  
  Search_columns NUMBER(38) 
  
  ID NUMBER(38) 
  
  PARENT_ID NUMBER(38) 
  
  POSITION NUMBER(38) 
  
  OTHER LONG 
  這里介紹一些我們將會(huì)討論的column的主要概念。假如需要每一個(gè)column的具體介紹,請看explain.doc文件。 
  
  STATEMENT_ID:為一條指定的SQL語句確定特定的執(zhí)行計(jì)劃名稱。假如在EXPLAN PLAN語句中沒有使用SET STATEMENT_ID,那么此值會(huì)被設(shè)為NULL。 
  
  OPERATION:在計(jì)劃的某一步驟執(zhí)行的操作名稱,例如:Filters,Index,Table,Marge Joins and Table等。 
  
  OPTION:對(duì)OPERATION操作的補(bǔ)充,例如:對(duì)一個(gè)表的操作,OPERATION可能是TABLE ACCESS,但OPTION可能為by ROWID或FULL。 
  
  Object_Owner:擁有此database Object的Schema名或Oracle帳戶名。 
  
  Object_name:Database Object名 
  
  Object_type:類型,例如:表、視圖、索引等等 
  
  ID:指明某一步驟在執(zhí)行計(jì)劃中的位置。 
  
  PARENT_ID:指明從某一操作中取得信息的前一個(gè)操作。通過對(duì)與ID和PARENT_ID使用Connect By操作,我們可以查詢整個(gè)執(zhí)行計(jì)劃樹。 
  
  這個(gè)PLAN表一旦創(chuàng)建成功,用戶就可在應(yīng)用中使用EXPLAIN。使用語法如下: 
  
  EXPLAIN PLAN [ SET STATEMENT_ID [=] < string literal > ] 
  
  [ INTO < table_name > ] 
  
  FOR < sql_statement > 
  
  其中: 
  STATEMENT_ID是一個(gè)唯一的字符串,把當(dāng)前執(zhí)行計(jì)劃與存儲(chǔ)在同一PLAN表中的其它執(zhí)行計(jì)劃區(qū)別開來。 
  
  TABLE_NAME是plan表名,它結(jié)構(gòu)如前所示,你可以任意設(shè)定這個(gè)名稱。 
  
  SQL_STATEMENT是真正的SQL語句。 
  
  例如: 
  
  EXPLAIN PLAN 
  
  SET STATEMENT_ID=‘QUERY1’ 
  
  FOR 
  
  SELECT 
  
  FROM EMP 
  
  WHERE DEPTNO=10; 
  
  執(zhí)行后將會(huì)得到以下信息: 
  
  operation 50 sUCceeded 
  
  注重,假如在Explain語句中忽略INTO句,則EXPLAIN會(huì)使用PLAN_TABLE作為表名,我們可以用查詢plan table的方法來檢查執(zhí)行計(jì)劃,如: 
  
  SELECT OPERATION, OPTIONS, OBJECT_NAME, OBJECT_TYPE, ID, PARENT_ID 
  
  FROM PLAN_TABLE 
  
  WHERE STATEMENT_ID=‘QUERY1’ 
  
  ORDER BY ID; 
  
  將會(huì)返回如下: 
  
  OPERATION OPTION Object_name Object_type ID Parent_ID 
  
  TABLE ACCESS FULL EMP TABLE 1 1 
  
  1 row selected 
  
  這意味在這個(gè)查詢中將會(huì)使用全表掃描,假如在EMP表上沒有創(chuàng)建索引,對(duì)EMP的所有查詢都將使用全表掃描,但是假如在DEPTNO列上創(chuàng)建一個(gè)非唯一的索引: 
  
  CREATE INDEX EMP_IDX ON EMP(DEPTNO); 
  
  現(xiàn)在,假如我們重新解釋查詢: 
  
  EXPLAIN PLAN 
  
  SET STATEMENT_ID=’QUERY2’ 
  
  FOR 
  
  SELECT * 
  
  FROM EMP 
  
  WHERE DEPTNO=10; 
  
  然后檢查計(jì)劃表: 
  
  SELECT OPERATION, OPTIONS, OBJECT_NAME,OBJECT_TYPE,ID,PARENT_ID 
  
  FROM PLAN_TABLE 
  
  WHERE STATEMENT_ID=‘QUERY2’ 
  
  ORDER BY IB; 
  
  將返回: 
  
  OPERATION OPTION Object_name Object_type ID Parent_ID 
  
  TABLE ACCESS BY RAWID EMP TABLE 1 
  
  INDEX RANGE SCAN EMP_IDX NON_UNIQUE 2 1 
  
  2 row selected 
  
  這樣,我們可以看到索引EMP_IDX被用于得到所有DEPTNO等于10的行,然后根據(jù)ROWID取得數(shù)據(jù),索引存儲(chǔ)了表中每行的ROWID,每當(dāng)在索引中找到一行,就會(huì)根據(jù)ROWID去查詢該行的其余部分。
                         假如是對(duì)一個(gè)很大的表的操作,這樣的搜索路徑較前一種(全表掃描)會(huì)對(duì)減少磁盤 I / O 操作有明顯的效果。但是,假如索引是“低選擇性的”,那么一個(gè)全表掃描可能會(huì)更有效。 
  
  考慮以下的查詢及其執(zhí)行計(jì)劃: 
  
  EXPLAIN PLAN 
  
  SET STATEMENT_ID=‘QUERY3’ 
  
  FOR 
  
  SELECT DEPTNO 
  
  FROM EMP 
  
  WHERE DEPTNO=10 
  
  執(zhí)行路徑的計(jì)劃是: 
  
  OPERATION OPTION Object_name Object_type ID Parent_ID 
  
  INDEX RANGE_SCAN EMP_IDX NON_UNIQUE 1 
  
  以上的執(zhí)行計(jì)劃表示不需在table中取得數(shù)據(jù),此查詢只須使用索引。 
  
  EXPLAIN搜索路徑解釋 
  任何SQL語句的執(zhí)行計(jì)劃都遵循一些優(yōu)化原則,這些原則在Oracle數(shù)據(jù)庫治理員手冊中有具體介紹。同時(shí),這些原則也被列在文本100040.163中。這些原則都試圖在從數(shù)據(jù)庫取出數(shù)據(jù)時(shí)找出一條最佳搜索路徑。一旦優(yōu)化器評(píng)估過查詢并確定了搜索路徑,優(yōu)化器就會(huì)創(chuàng)建一個(gè)執(zhí)行計(jì)劃樹。我們可以使用SQL*Plus查詢plan table從而看到執(zhí)行計(jì)劃樹: 
  
  COLUMN plan FORMAT a70 
  
  select lpad (‘ ‘, 3*level)  operation  (‘  options ’) ‘ object_name  ‘ ‘  object_type 
  
  from plan_table 
  
  connect by PRior id=parent_id and statement_id=‘ & stmt_id’; 
  
  例如下面這個(gè)查詢 
  
  SELECT ENAME 
  
  FROM EMP 
  
  WHERE DEPTNO=10 
  
  ORDER BY ENAME; 
  
  使用以上SQL語句檢查plan table的結(jié)果是: 
  
  SORT ( ORDER BY ) 
  
  TABLE ACCESS ( BY ROWID ) EMP 
  
  INDEX ( RANGE SCAN ) EMP_IDX N0N_UNIQUE 
  
  這個(gè)執(zhí)行計(jì)劃樹表示在EMP_IDX索引上執(zhí)行一個(gè)索引掃描,然后ENAME數(shù)據(jù)被按照ROWID從表中取了出來,最后這些數(shù)據(jù)被ORDER BY操作歸類。假如EMP表大的話,那么這個(gè)執(zhí)行計(jì)劃樹的最后一步可能花較長的時(shí)間。 
  
  假設(shè)我們解釋如下查詢: 
  
  select deptno, ename 
  
  from emp 
  
  where deptno between 1 and 30 
  
  order by deptno; 
  
  那么執(zhí)行樹為: 
  
  TABLE ACCESS ( BY ROWID) EMP 
  
  INDEX (RANGE SCAN) EMP_IDX NON_UNIQUE 
  
  請注重,雖然在查詢時(shí)使用了order by,但在執(zhí)行樹中并未出現(xiàn)SORT (ORDER BY)。為什么呢?不使用SORT有二個(gè)原因:1) deptno列上已經(jīng)建立了index,已作過sort;2)deptno被定義為not null(如:DEPTNO NOT NULL NUMBER)。 
  
  假設(shè)下面這個(gè)普通的連接查詢: 
  
  SELECT * 
  
  from emp. dept 
  
  where emp.deptno=dept.deptno 
  
  and sal >5000; 
  
  執(zhí)行樹為: 
  
  NESTED LOOPS () 
  
  TABLE ACCESS (FULL)DEPT 
  
  TABLE ACCESS (BY ROWID)EMP 
  
  INDEX (RANGE SCAN) EMP_IDX NON_UNIQUE 
  
  NESTED LOOPS意味著在一個(gè)表(DEPT)上作了一個(gè)序列查詢,同時(shí)在EMP表上的索引EMP_IDX中,每一個(gè)DEPTNO均作查找。這個(gè)查詢被稱為一個(gè)驅(qū)動(dòng)表( driving table )。在這種情況下,驅(qū)動(dòng)表是DEPT。在這種類型的連接中,驅(qū)動(dòng)表是被列在后面的表。因?yàn)閮蓚€(gè)表有相同級(jí)別的搜索路徑 (都在deptno列上有非唯一的索引) ,既然至少有一個(gè)表上的所有記錄必須被檢索,那么在一個(gè)表上執(zhí)行全表掃描,同時(shí)在另一個(gè)表的索引上尋找符合條件的記錄是比較有效的。 
  
  這種情況下,我們應(yīng)把具有最少列的表作為驅(qū)動(dòng)表放在from子句的最后,注重,在這種類型的連接中,from子句中表的先后次序決