Oracle調優總結

531968912發表於2016-05-24

Oracle調優總結  

  
Problem Description:
1.每個表的結構及主鍵索引情況
2.每個表的count(*)記錄是多少
3.對於建立索引的列,索引的型別是什麼?count(distinct indexcol)的值是多少?
4.最後一次對錶進行分析是在什麼時間,分析後,是否又對相關表做過大的操作
5.索引最後一次rebuild,是在什麼時間,此後對錶的操作型別又是什麼狀況?索引中浪費的空間是多少?
6.這些表的儲存情況,表的儲存引數,表空間的型別,儲存引數等
7.執行該SQL語句時,系統等候的資源是什麼? Trace SQL語句的執行過程
8.另一臺執行相似SQL速度很快的機器上的相關表的如上資訊是什麼?
一:SQL tuning 類
1:列舉幾種表連線方式
  hash join/merge join/nest loop(cluster join)/index join
2:不借助第三方工具,怎樣檢視sql的執行計劃
set autotrace on
set autotrace traceonly
explain plan set statement_id = &item_id for &sql;
select * from table(dbms_xplan.display);
3:如何使用CBO,CBO與RULE的區別
  在optimizer_mode=choose時,如果表有統計資訊(分割槽表外),優化器將選擇CBO,否則選RBO。
  RBO遵循簡單的分級方法學,使用15種級別要點,當接收到查詢,優化器將評估使用到的要點數目,
然後選擇最佳級別(最少的數量)的執行路徑來執行查詢。
CBO嘗試找到最低成本的訪問資料的方法,為了最大的吞吐量或最快的初始響應時間,計算使用不同
的執行計劃的成本,並選擇成本最低的一個,關於表的資料內容的統計被用於確定執行計劃。
4:如何定位重要(消耗資源多)的SQL
  select sql_text
  from v$sql
where disk_reads > 1000 or (executions > 0 and buffer_gets/executions > 30000);
5:如何跟蹤某個session的SQL
select sid,serial# from v$session where sid = (select sid from v$mystat where rownum = 1);
6:SQL調整最關注的是什麼
  檢視該SQL的response time(db block gets/consistent gets/physical reads/sorts (disk))
7:說說你對索引的認識(索引的結構、對dml影響、為什麼提高查詢效能)
  b-tree index/bitmap index/function index/patitional index(local/global)
  索引通常能提高select/update/delete的效能,會降低insert的速度,
  
8:使用索引查詢一定能提高查詢的效能嗎?為什麼
  索引就是為了提高查詢效能而存在的,
如果在查詢中索引沒有提高效能,
只能說是用錯了索引,或者講是場合不同
9:繫結變數是什麼?繫結變數有什麼優缺點?
  繫結變數是相對文字變數來講的,所謂文字變數是指在SQL直接書寫查詢條件,
這樣的SQL在不同條件下需要反覆解析,繫結變數是指使用變數來代替直接書寫條件,
查詢bind value在執行時傳遞,然後繫結執行。
  
優點是減少硬解析,降低CPU的爭用,節省shared_pool
缺點是不能使用histogram,sql優化比較困難
10:如何穩定(固定)執行計劃
  query_rewrite_enabled = true
  star_transformation_enabled = true
  optimizer_features_enable = 9.2.0
建立並使用stored outline
  
這個貼子:
  
11:和排序相關的記憶體在8i和9i分別怎樣調整,臨時表空間的作用是什麼
   8i中sort_area_size/sort_area_retained_size決定了排序所需要的記憶體
  如果排序操作不能在sort_area_size中完成,就會用到temp表空間
  9i中如果workarea_size_policy=auto時,
  排序在pga內進行,通常pga_aggregate_target的1/20可以用來進行disk sort;
  如果workarea_size_policy=manual時,排序需要的記憶體由sort_area_size決定
   在執行order by/group by/distinct/union/create index/index rebuild/minus等操作時,
  如果在pga或sort_area_size中不能完成,排序將在臨時表空間進行(disk sort),
  臨時表空間主要作用就是完成系統中的disk sort.
12:存在表T(a,b,c,d),要根據欄位c排序後取第21—30條記錄顯示,請給出sql
   create table t(a number(,b number(,c number(,d number();
  /
  begin      
    for i in 1 .. 300 loop
      insert into t values(mod(i,2),i/2,dbms_random.value(1,300),i/4);
    end loop;
  end;
  /
   select * from (select c.*,rownum as rn from (select * from t order by c desc) c) where rn between 21 and 30;
   /
  select * from (select * from test order by c desc) x where rownum < 30
  minus
   select * from (select * from test order by c desc) y where rownum < 20 order by 3 desc
   相比之 minus效能較差
二:資料庫基本概念類
1ctused and pctfree 表示什麼含義有什麼作用
  pctused與pctfree控制資料塊是否出現在freelist中,
pctfree控制資料塊中保留用於update的空間,當資料塊中的free space小於pctfree設定的空間時,
該資料塊從freelist中去掉,當塊由於dml操作free space大於pct_used設定的空間時,該資料庫塊將
被新增在freelist連結串列中。
--PCTFREE儲存引數
  PCTFREE儲存引數告訴ORACLE什麼時候應該將資料塊從物件的空閒列表中移出。ORACLE的預設引數是PCTFREE=10;
也就是說,一旦一個INSERT操作使得資料塊的90%被使用,這個資料塊就從空閒列表(free list)中移出。
 --PCTUSED儲存引數
  PCTUSED儲存引數告訴ORACLE什麼時候將以前滿的資料塊加到空閒列表中。當記錄從資料表中刪除時,資料庫的數
據塊就有空間接受新的記錄,但只有當填充的空間降到PCTUSED值以下時,該資料塊才被連線到空閒列表中,才可以往
其中插入資料。PCTUSED的預設值是PCTUSED=40。
--儲存引數規則小結
  (1)PCTUSED較高意味著相對較滿的資料塊會被放置到空閒列表中,從而有效的重複使用資料塊的空間,但會導致
I/O消耗。PCTUSED低意味著在一個資料塊快空的時候才被放置到空閒列表中,資料塊一次能接受很多的記錄,因此可以
減少I/O消耗,提高效能。
  (2)PCTFREE的值較大意味著資料塊沒有被利用多少就從空閒列表中斷開連線,不利於資料塊的充分使用。PCTFREE
過小的結果是,在更新時可能會出現資料記錄遷移(Migration)的情況。(注:資料記錄遷移(Migration)是指記錄在是
UPDATE操作擴充套件了一個VARCHAR2型別的列或BLOB列後,PCTFREE引數所指定的空間不夠擴充套件,從而記錄被ORACLE強制遷移到
新的資料塊,發生這種情況將較嚴重的影響ORACLE的效能,出現更新緩慢)。
  (3)在批量的插入、刪除或者更新操作之前,先刪除該表上的索引,在操作完畢之後在重新建立,這樣有助於提高
批量操作的整體速度,並且保證B樹索引在操作之後有良好的效能。
---------------------------------------------------------------------------------------------------------------------------------------------------
--表的pctfree和pctused兩個引數進行估算的方法
對於不同的應用系統,表的pctfree 和pctused兩個引數有不同的設計原則,以下是根據特定的應用系統進行估算的例子,從中可以掌握基本的估算方法。

表的儲存引數調整,一般情況,設定為pctfree 5 pctused 85即可(預設為pctfree 10 pctused 40)
1.對於Pctfree引數
除了可以按欄位及欄位長度估算平均行長外,下面的方面可以根據已有資料分析出平均行長和每塊行數
例:
analyze table 病人資訊 compute statistics for table for all indexes for all indexed columns;
Select Num_Rows,Blocks,Round(Num_Rows / Blocks) Avg_Rows_Block, Avg_Row_Len From User_Tables Where Table_Name = '病人資訊'
    NUM_ROWS BLOCKS AVG_ROWS_BLOCK AVG_ROW_LEN   
857291 14161 61 117  
對於一般8192的塊,實際可用空間為8100左右.
假設以前該表的pctfree為15,改為5後,pctfree減少10,就可以再存入約7行.
相同的1萬4千塊就可以多存放約10萬行資料,
這10萬行資料,如果按每塊60行算,就可以少佔用約1700塊(約13M的空間)
如果全表掃描該表的話,少讀1700塊資料,少106次IO操作(按預設db_file_multiblock_read_count=16計算)
少佔13M的記憶體
另外,需要考慮的兩個因素
1。更新操作時,資料增長量大不大,例如:主要是把狀態欄位由1改為3,還是把摘要由空改為一段文字
2。併發事務的多少,因為一個事務資訊在塊中要佔用約24Byte,如果有10個併發事務的話,至少額外考慮240Byte的空閒空間。
2.對於Pctused引數
主要考慮刪除後插入資料的情況多不多,以及平均行長大小
例如:
病人費用記錄,醫保如果存在校對操作的話,是先產生預交結算資料,正式結算時,刪除這些資料再重新生成
所以,病人預交記錄,Pctused不能設定太高,否則重用那些低於Pctused的塊,只能插入少量資料行,增加了IO操作
analyze table 病人預交記錄 compute statistics for table for all indexes for all indexed columns;
Select Num_Rows,Blocks,Round(Num_Rows / Blocks) Avg_Rows_Block, Avg_Row_Len From User_Tables Where Table_Name = '病人預交記錄'
    NUM_ROWS BLOCKS AVG_ROWS_BLOCK AVG_ROW_LEN   
181758 2147 85 83  
如果設定pctfree 5 pctused 85,那麼當刪除一些行使塊的已用空間低於85%時,塊會被重用,但是因為要預留5%的空閒空間,
所以,對於已用空間剛剛低於85%的塊,重用空間就只有10%,對於8K的塊,可用810Byte,平均行長83,可以再放入9行,所以這個引數也是可以的。
但是,如果是病人費用記錄,平均行長229,這樣設定,只能放下3行,這個引數就不太合適了

    NUM_ROWS BLOCKS AVG_ROWS_BLOCK AVG_ROW_LEN   
925133 38278 24 229  
根據分析,病人費用記錄的資料更新量不大,但是併發操作比較大,最好把Pctfree設定高一點
所以,可以設定為pctfree 10 pctused 75(重用的塊至少可以放5行,約1-2張單據),甚至pctused 70也是可以的。
如果一個塊的資料行數太多,可能造成熱塊爭用,但是相對於減少儲存,減少IO,減少記憶體佔用帶來的好處來說,熱塊不是特別突出的情況下可以不考慮。
PCTFREE=(Average Row Size-Initial Row Size)*100/Average Row Size
PCTUSED=(100-PCTFREE) -Average Row Size * 100/Availabe Data Space
  
  Oracle的其中一個優點時它可以管理每個表空間中的自由空間。Oracle負責處理表和索引的空間管理,這樣就可以讓我們無需懂得Oracle的表和索引的
內部運作。不過,對於有經驗的Oracle調優專家來說,他需要懂得Oracle是如何管理表的extent和空閒的資料塊。對於調整擁有高的insert或者update的系
統來說,這是非常重要的。
  
  要精通物件的調整,你需要懂得freelists和freelist組的行為,它們和pctfree及pctused引數的值有關。這些知識對於企業資源計劃(ERP)的應用是
特別重要的,因為在這些應用中,不正確的表設定通常是DML語句執行慢的原因。 
  對於初學者來說,最常見的錯誤是認為預設的Oracle引數對於所有的物件都是最佳的。除非磁碟的消耗不是一個問題,否則在設定表的pctfree和pctused
引數時,就必須考慮平均的行長和資料庫的塊大小,這樣空的塊才會被有效地放到freelists中。當這些設定不正確時,那些得到的freelists也是"dead"塊,
因為它們沒有足夠的空間來儲存一行,這樣將會導致明顯的處理延遲。
  Freelists對於有效地重新使用Oracle表空間中的空間是很重要的,它和pctfree及pctused這兩個儲存引數的設定直接相關。如果將pctused設定為一個高的值,
這時資料庫就會盡快地重新使用塊。不過,高效能和有效地重新使用表的塊是對立的。在調整Oracle的表格和索引時,需要認真考慮究竟需要高效能還是有效的空
間重用,並且據此來設定表的引數。以下我們來看一下這些freelists是如何影響Oracle的效能的。
  
  當有一個請求需要插入一行到表格中時,Oracle就會到freelist中尋找一個有足夠的空間來容納一行的塊。你也許知道,freelist串是放在表格或者索引的第
一個塊中,這個塊也被稱為段頭(segment header)。pctfree和pctused 引數的唯一目的就是為了控制塊如何在freelists中進出。雖然freelist link和 unlink
是簡單的Oracle功能,不過設定freelist link (pctused) 和unlink (pctfree) 對Oracle的效能確實有影響。
  
  由DBA的基本知識知道,pctfree引數是控制freelist un-links的(即將塊由freelists中移除)。設定pctfree=10 意味著每個塊都保留10%的空間用作行擴充套件。
pctused引數是控制freelist re-links的。設定pctused=40意味著只有在塊的使用低於40%時才會回到表格的freelists中。
  
  許多新手對於一個塊重新回到freelists後的處理都有些誤解。其實,一旦由於一個刪除的操作而令塊被重新加入到freelist中,它將會一直保留在freelist中
即使空間的使用超過了60%,只有在到達pctfree時才會將資料塊由freelist中移走。
  
  表格和索引儲存引數設定的要求總結
  
  以下的一些規則是用來設定freelists, freelist groups, pctfree和pctused儲存引數的。你也知道,pctused和pctfree的值是可以很容易地通過alter table
命令修改的,一個好的DBA應該知道如何設定這些引數的最佳值。
  
  有效地使用空間和高效能之間是有矛盾的,而表格的儲存引數就是控制這個方面的矛盾:
  
  . 對於需要有效地重新使用空間,可以設定一個高的pctused值,不過副作用是需要額外的I/O。一個高的pctused值意味著相對滿的塊都會放到freelist中。因
此,這些塊在再次滿之前只可以接受幾行記錄,從而導致更多的I/O。
  
  . 追求高效能的話,可以將pctused設定為一個低的值,這意味著Oracle不會將資料塊放到freelists中直到它幾乎是空的。那麼塊將可以在滿之前接收更多的行,
因此可以減少插入操作的I/O。要記住Oracle擴充套件新塊的效能要比重新使用現有的塊高。對於Oracle來說,擴充套件一個表比管理freelists消耗更少的資源。
  
  讓我們來回顧一下設定物件儲存引數的一些常見規則:
  
  .經常將pctused設定為可以接收一條新行。對於不能接受一行的free blocks對於我們來說是沒有用的。如果這樣做,將會令Oracle的效能變慢,因為Oracle將
在擴充套件表來得到一個空的塊之前,企圖讀取5個"dead" 的free block。
  
  .表格中chained rows的出現意味著pctfree太低或者是db_block_size太少。在很多情況下,RAW和LONG RAW列都很巨大,以至超過了Oracle的最大塊的大小,
這時chained rows是不可以避免的。
  
  .如果一個表有同時插入的SQL語句,那麼它需要有同時刪除的語句。執行單一個一個清除的工作將會把全部的空閒塊放到一個freelist中,而沒有其它包含有任何
空閒塊的freelists出現。
  
  .freelist引數應該設定為表格同時更新的最大值。例如,如果在任何時候,某個表最多有20個使用者執行插入的操作,那麼該表的引數應該設定為freelists=20。
  
  應記住的是freelist groups引數的值只是對於Oracle Parallel Server和Real Application Clusters才是有用的。對於這類Oracle,freelist groups應該設定
為訪問該表格的Oracle Parallel Server例項的數目。
---------------------------------------------------------------------------------------------------------------------------------------------------
2:簡單描述table / segment / extent / block之間的關係
  table建立時,預設建立了一個data segment,
每個data segment含有min extents指定的extents數,
每個extent據據表空間的儲存引數分配一定數量的blocks
3:描述tablespace和datafile之間的關係
一個tablespace可以有一個或多個datafile,每個datafile只能在一個tablespace內,
table中的資料,通過hash演算法分佈在tablespace中的各個datafile中,
tablespace是邏輯上的概念,datafile則在物理上儲存了資料庫的種種物件。
4:本地管理表空間和字典管理表空間的特點,ASSM有什麼特點
  本地管理表空間(Locally Managed Tablespace簡稱LMT)
  8i以後出現的一種新的表空間的管理模式,通過點陣圖來管理表空間的空間使用。
  字典管理表空間(Dictionary-Managed Tablespace簡稱DMT)
  8i以前包括以後都還可以使用的一種表空間管理模式,通過資料字典管理表空間的空間使用。
  動段空間管理(ASSM),
  它首次出現在Oracle920裡有了ASSM,連結列表freelist被點陣圖所取代,它是一個二進位制的陣列,
  能夠迅速有效地管理儲存擴充套件和剩餘區塊(free block),因此能夠改善分段儲存本質,
  ASSM表空間上建立的段還有另外一個稱呼叫Bitmap Managed Segments(BMB 段)。
5:回滾段的作用是什麼
事務回滾:當事務修改表中資料的時候,該資料修改前的值(即前影像)會存放在回滾段中,
          當使用者回滾事務(ROLLBACK)時,ORACLE將會利用回滾段中的資料前影像來將修改的資料恢復到原來的值。
 
  事務恢復:當事務正在處理的時候,例程失敗,回滾段的資訊儲存在undo表空間中,
           ORACLE將在下次開啟資料庫時利用回滾來恢復未提交的資料。
 讀一致性:當一個會話正在修改資料時,其他的會話將看不到該會話未提交的修改。
           當一個語句正在執行時,該語句將看不到從該語句開始執行後的未提交的修改(語句級讀一致性)
      當ORACLE執行SELECT語句時,ORACLE依照當前的系統改變號(SYSTEM CHANGE NUMBER-SCN)
      來保證任何前於當前SCN的未提交的改變不被該語句處理。可以想象:當一個長時間的查詢正在執行時,
      若其他會話改變了該查詢要查詢的某個資料塊,ORACLE將利用回滾段的資料前影像來構造一個讀一致性檢視。
6:日誌的作用是什麼
  記錄資料庫事務,最大限度地保證資料的一致性與安全性
  重做日誌檔案:含對資料庫所做的更改記錄,這樣萬一出現故障可以啟用資料恢復,一個資料庫至少需要兩個重做日誌檔案
  歸檔日誌檔案:是重做日誌檔案的離線副本,這些副本可能對於從介質失敗中進行恢復很必要。
7:SGA主要有那些部分,主要作用是什麼
  SGA:db_cache/shared_pool/large_pool/Java_pool
db_cache:
  資料庫快取(Block Buffer)對於Oracle資料庫的運轉和效能起著非常關鍵的作用,
   它佔據Oracle資料庫SGA(系統共享記憶體區)的主要部分。Oracle資料庫通過使用LRU
   演算法,將最近訪問的資料塊存放到快取中,從而優化對磁碟資料的訪問.
shared_pool:
  共享池的大小對於Oracle 效能來說都是很重要的。
  共享池中儲存資料字典高速緩衝和完全解析或編譯的的PL/SQL 塊和SQL 語句及控制結構
large_pool:
  使用MTS配置時,因為要在SGA中分配UGA來保持使用者的會話,就是用Large_pool來保持這個會話記憶體
   使用RMAN做備份的時候,要使用Large_pool這個記憶體結構來做磁碟I/O快取器
java_pool:
  為java procedure預備的記憶體區域,如果沒有使用java proc,java_pool不是必須的
   
8racle系統程式主要有哪些,作用是什麼
  資料寫程式(dbwr):負責將更改的資料從資料庫緩衝區快取記憶體寫入資料檔案
  日誌寫程式(lgwr):將重做日誌緩衝區中的更改寫入線上重做日誌檔案
  系統監控(smon)  :檢查資料庫的一致性如有必要還會在資料庫開啟時啟動資料庫的恢復
  程式監控(pmon)  :負責在一個Oracle 程式失敗時清理資源
  檢查點程式(chpt):負責在每當緩衝區快取記憶體中的更改永久地記錄在資料庫中時,更新控制檔案和資料檔案中的資料庫狀態資訊。
  歸檔程式(arcn)  :在每次日誌切換時把已滿的日誌組進行備份或歸檔
  作業排程器(cjq)  :負責將排程與執行系統中已定義好的job,完成一些預定義的工作.
恢復程式(reco)  :保證分散式事務的一致性,在分散式事務中,要麼同時commit,要麼同時rollback;
三:備份恢復類
1:備份如何分類
邏輯備份:exp/imp
物理備份:
    RMAN備份
     full backup/incremental backup(累積/差異)
     熱備份:alter tablespace begin/end backup;
     冷備份:離線備份(database shutdown)
     
2:歸檔是什麼含義
關於歸檔日誌:Oracle要將填滿的線上日誌檔案組歸檔時,則要建立歸檔日誌(archived redo log)。
其對資料庫備份和恢復有下列用處:
    <1>資料庫後備以及線上和歸檔日誌檔案,在作業系統和磁碟故障中可保證全部提交的事物可被恢復。
    <2>在資料庫開啟和正常系統使用下,如果歸檔日誌是永久儲存,線上後備可以進行和使用。
  資料庫可執行在兩種不同方式下:
   NOARCHIVELOG方式或ARCHIVELOG 方式
資料庫在NOARCHIVELOG方式下使用時,不能進行線上日誌的歸檔,
如果資料庫在ARCHIVELOG方式下執行,可實施線上日誌的歸檔。
3:如果一個表在2004-08-04 10:30:00 被drop,在有完善的歸檔和備份的情況下,如何恢復?
  手工拷貝回所有備份的資料檔案
sql>startup mount;
sql>alter database recover automatic until time '2004-08-04:10:30:00';
sql>alter database open resetlogs;
4:rman是什麼,有何特點?
RMAN(Recovery Manager)是DBA的一個重要工具,用於備份、還原和恢復oracle資料庫,
RMAN 可以用來備份和恢復資料庫檔案、歸檔日誌、控制檔案、系統引數檔案,也可以用來執行完全或不完全的資料庫恢復。
RMAN有三種不同的使用者介面:
  COMMAND LINE方式、GUI 方式(整合在OEM 中的備份管理器)、API 方式(用於整合到第三方的備份軟體中)。
具有如下特點:
1)功能類似物理備份,但比物理備份強大N倍;
2)可以壓縮空塊;
3)可以在塊水平上實現增量;
4)可以把備份的輸出打包成備份集,也可以按固定大小分割備份集;
5)備份與恢復的過程可以自動管理;
6)可以使用指令碼(存在Recovery catalog 中)
7)可以做壞塊監測
5:standby的特點
備用資料庫(standby database):ORACLE推出的一種高可用性(HIGH AVAILABLE)資料庫方案,
在主節點與備用節點間通過日誌同步來保證資料的同步,備用節點作為主節點的備份
可以實現快速切換與災難性恢復,從920開始,還開始支援物理與邏輯備用伺服器。
  9i中的三種資料保護模式分別是:
  1)、MAXIMIZE PROTECTION :最大資料保護與無資料分歧,LGWR將同時傳送到備用節點,
    在主節點事務確認之前,備用節點也必須完全收到日誌資料。如果網路不好,引起LGWR不能傳送資料,將引起嚴重的效能問題,導致主節點DOWN機。
  2)、MAXIMIZE AVAILABILITY :無資料丟失模式,允許資料分歧,允許非同步傳送。
    正常情況下執行在最大保護模式,在主節點與備用節點的網路斷開或連線不正常時,自動切換到最大效能模式,
   主節點的操作還是可以繼續的。在網路不好的情況下有較大的效能影響。
  3)、MAXIMIZE PERFORMANCE:這種模式應當可以說是從8i繼承過來的備用伺服器模式,非同步傳送,
    無資料同步檢查,可能丟失資料,但是能獲得主節點的最大效能。9i在配置DATA GUARD的時候預設就是MAXIMIZE PERFORMANCE
6:對於一個要求恢復時間比較短的系統(資料庫50G,每天歸檔5G),你如何設計備份策略
  rman/每月一號 level 0 每週末/週三 level 1 其它每天level 2  
四:系統管理類
1:對於一個存在系統效能的系統,說出你的診斷處理思路
1 做statspack收集系統相關資訊
  瞭解系統大致情況/確定是否存在引數設定不合適的地方/檢視top 5 event/檢視top sql等
2 查v$system_event/v$session_event/v$session_wait
   從v$system_event開始,確定需要什麼資源(db file sequential read)等
  深入研究v$session_event,確定等待事件涉及的會話
  從v$session_wait確定詳細的資源爭用情況(p1-p3的值:file_id/block_id/blocks等)
3 通過v$sql/v$sqltext/v$sqlarea表確定disk_reads、(buffer_gets/executions)值較大的SQL
2:列舉幾種診斷IO、CPU、效能狀況的方法
    top/vmstat
  statspack
  sql_trace/tkprof
    查v$system_event/v$session_event/v$session_wait
  查v$sqlarea(disk_reads或buffer_gets/executions較大的SQL)
3:對statspack有何認識
StapSpack是Oracle公司提供的一個收集資料庫執行效能指標的軟體包,該軟體包從8i起,在9i、10g都有顯著的增強
該軟體包的輔助表(儲存相關引數與收集的效能指標的表)由最初的25個增長到43個
收集級別引數由原來的3個(0、5、10)增加到5個(0、5、6、7、10)
通過分析收集的效能指標,資料庫管理員可以詳細地瞭解資料庫目前的執行情況,對資料庫例項、等待事件、SQL等進行優化調整
利用statspack收集的snapshot,可以統計製作資料庫的各種效能指標的統計趨勢圖表。
4:如果系統現在需要在一個很大的表上建立一個索引,你會考慮那些因素,如何做以儘量減小對應用的影響
在系統比較空閒時
nologging選項(如果有dataguard則不可以使用nologging)
大的sort_ared_size或pga_aggregate_target較大
5:對raid1+0 和raid5有何認識
RAID 10(或稱RAID 1+0)與RAID 0+1不同,它是用硬碟驅動器先組成RAID 1陣列,然後在RAID 1陣列之間再組成RAID 0陣列。
RAID 10模式同RAID 0+1模式一樣具有良好的資料傳輸效能,但卻比RAID 0+1具有更高的可靠性。RAID 10陣列的實際容量為M×n/2,
磁碟利用率為50%。RAID 10也需要至少4個硬碟驅動器構成,因而價格昂貴。
  RAID 10的可靠性同RAID 1一樣,但由於RAID 10硬碟驅動器之間有資料分割,因而資料傳輸效能優良。
RAID 5與RAID 3很相似,不同之處在於RAID 5的奇偶校驗資訊也同資料一樣被分割儲存到所有的硬碟驅動器,
而不是寫入一個指定的硬碟驅動器,從而消除了單個奇偶校驗硬碟驅動器的瓶頸問題。RAID 5磁碟陣列的效能比RAID 3有所提高,
但仍然需要至少3塊硬碟驅動器。其實際容量為M×(n-1),磁碟利用率為(n-1)/n 。
五:綜合隨意類
1:你最擅長的是oracle哪部分?
pl/sql及sql優化
2:喜歡oracle嗎?喜歡上論壇嗎?或者偏好oracle的哪一部分?
喜歡,sql的優化
3:隨意說說你覺得oracle最有意思的部分或者最困難的部分
latch free的處理
4:為何要選擇做DBA呢?
興趣所在
消耗在準備利用Oracle執行計劃機制提高查詢效能新的SQL語句的時間是Oracle SQL語句執行時間的最重要的組成部分。
但是通過理解Oracle內部產生執行計劃的機制,你能夠控制Oracle花費在評估連線順序的時間數量,並且能在大體上提高查詢效能。
準備執行SQL語句
當SQL語句進入Oracle的庫快取後,在該語句準備執行之前,將執行下列步驟:
1) 語法檢查:檢查SQL語句拼寫是否正確和詞序。
2) 語義分析:核實所有的與資料字典不一致的表和列的名字。
3) 輪廓儲存檢查:檢查資料字典,以確定該SQL語句的輪廓是否已經存在。
4) 生成執行計劃:使用基於成本的優化規則和資料字典中的統計表來決定最佳執行計劃。
5) 建立二進位制程式碼:基於執行計劃,Oracle生成二進位制執行程式碼。
一旦為執行準備好了SQL語句,以後的執行將很快發生,因為Oracle認可同一個SQL語句,並且重用那些語句的執行。然而,對於生成
特殊的SQL語句,或嵌入了文字變數的SQL語句的系統,SQL執行計劃的生成時間就很重要了,並且前一個執行計劃通常不能夠被重用。
對那些連線了很多表的查詢,Oracle需要花費大量的時間來檢測連線這些表的適當順序。
評估表的連線順序
在SQL語句的準備過程中,花費最多的步驟是生成執行計劃,特別是處理有多個表連線的查詢。當Oracle評估表的連線順序時,它必須
考慮到表之間所有可能的連線。例如:六個表的之間連線有720(6的階乘,或6 * 5 * 4 * 3 * 2 * 1 = 720)種可能的連線線路。
當一個查詢中含有超過10個表的連線時,排列的問題將變得更為顯著。對於15個表之間的連線,需要評估的可能查詢排列將超過1萬億
(準確的數字是1,307,674,368,000)種。
使用optimizer_search_limit引數來設定限制
通過使用optimizer_search_limit引數,你能夠指定被優化器用來評估的最大的連線組合數量。使用這個引數,我們將能夠防止優化器
消耗不定數量的時間來評估所有可能的連線組合。如果在查詢中表的數目小於optimizer_search_limit的值,優化器將檢查所有可能的
連線組合。
例如:有五個表連線的查詢將有120(5! = 5 * 4 * 3 * 2 * 1 = 120)種可能的連線組合,因此如果optimizer_search_limit等於5
(預設值),則優化器將評估所有的120種可能。optimizer_search_limit引數也控制著呼叫帶星號的連線提示的閥值。當查詢中的表的
數目比optimizer_search_limit小時,帶星號的提示將被優先考慮。
另一個工具:引數optimizer_max_permutations
初始化引數optimizer_max_permutations定義了優化器所考慮組合數目的上限,且依賴於初始引數optimizer_search_limit。
optimizer_max_permutations的預設值是80,000。
引數optimizer_search_limit和optimizer_max_permutations一起來確定優化器所考慮的組合數目的上限:除非(表或組合數目)
超過引數optimizer_search_limit 或者 optimizer_max_permutations設定的值,否則優化器將生成所有可能的連線組合。一旦優
化器停止評估表的連線組合,它將選擇成本最低的組合。
使用ordered提示指定連線順序
你能夠設定優化器所執行的評估數目的上限。但是即使採用有很高價值的排列評估,我們仍然擁有使優化器可以儘早地放棄複雜的查詢
的重要機會。回想一下含有15個連線查詢的例子,它將有超過1萬億種的連線組合。如果優化器在評估了80,000個組合後停止,那麼它才
僅僅評估了0.000006%的可能組合,而且或許還沒有為這個巨大的查詢找到最佳的連線順序。
在Oracle SQL中解決此問題的最好的方法是手工指定表的連線順序。為了儘快建立最小的解決方案集,這裡所遵循的規則是將表結合起
來,通常優先使用限制最嚴格的WHERE子句來連線表。
下面的程式碼是一個查詢執行計劃的例子,該例子在emp表的關聯查詢上強制執行了巢狀的迴圈連線。注意,我已經使用了ordered提示來
直接最優化表的評估順序,最終它們表現在WHERE子句上。
select /*+ ordered use_nl(bonus) parallel(e, 4) */
    e.ename,
   hiredate,
from
    emp e,
    bonus b
where
   e.ename = b.ename
這個例子要求優化器按順序連線在SQL語句的FROM子句中指定的表,在FROM子句中的第一個表指定了驅動表。ordered提示通常被用來與
其它的提示聯合起來來保證採用正確的順序連線多個表。它的用途更多的是在扭轉連線表數在四個以上的資料倉儲的查詢方面。
另外一個例子,下面的查詢使用ordered提示按照指定的順序來連線表:emp、dept、sal,最後是bonus。我通過指定emp到dept使用哈
希連線和sal到bonus使用巢狀迴圈連線,來進一步精煉執行計劃。  
select /*+ ordered use_hash (emp, dept) use_nl (sal, bonus) */
  
from
   emp,
    dept,
    sal,
    bonus
where . . .
  
實踐建議
實際上,更有效率的做法是在產品環境中減小optimizer_max_permutations引數的大小,並且總是使用穩定的優化計劃或儲存輪廓來
防止出現耗時的含有大量連線的查詢。一旦找到最佳的連線順序,您就可以通過增加ordered提示到當前的查詢中,並儲存它的儲存輪廓,
來為這些表手工指定連線順序,從而使其持久化。
當你打算使用優化器來穩定計劃,則可以照下面的方法使執行計劃持久化,臨時將optimizer_search_limit設定為查詢中的表的數目,
從而允許優化器考慮所有可能的連線順序。然後,通過重新編排WHERE子句中表的名字,並使用ordered提示,與儲存輪廓一起使變更
持久化,來調整查詢。在查詢中包含四個以上的表時,ordered提示和儲存輪廓將排除耗時的評估SQL連線順序解析的任務,從而提高
查詢的速度。
一旦檢測到最佳的連線順序,我們就可以使用ordered提示來過載optimizer_search_limit和optimizer_max_permutations引數。
ordered提示要求表按照它們出現在FROM子句中的順序進行連線,所以優化器沒有加入描述。
作為一個Oracle專業人員,你應該知道在SQL語句第一次進入庫快取時可能存在重大的啟動延遲。但是聰明的Oracle DBA和開發人
員能夠改變表的搜尋限制引數或者使用ordered提示來手工指定表的連線順序,從而顯著地減少優化和執行新查詢所需的時間
Oracle專家調優祕密
在過去的十年中, Oracle 已經成為世界上最專業的資料庫之一。對於 IT 專家來說,就是要確保利用 Oracle 的強大特性來提高他們公司的生產力。最有效的方法之一
是通過 Oracle 調優。它有大量的調整引數和技術來改進你的 Oracle 資料庫的效能。 Oracle 調優是一個複雜的主題。關於調優可以寫整整一本書,不過,為了改善
Oracle 資料庫的效能,有一些基本的概念是每個 Oracle DBA 都應該遵從的。
   在這篇簡介中,我們將簡要地介紹以下的 Oracle 主題:
  -- 外部調整:我們應該記住 Oracle 並不是單獨執行的。因此我們將檢視一下通過調整 Oracle 伺服器以得到高的效能。
  --Row re-sequencing 以減少磁碟 I/O :我們應該懂得 Oracle 調優最重要的目標是減少 I/O 。
  --Oracle SQL 調整。 Oracle SQL 調整是 Oracle 調整中最重要的領域之一,只要通過一些簡單的 SQL 調優規則就可以大幅度地提升 SQL 語句的效能,這是一點都
不奇怪的。
  -- 調整 Oracle 排序:排序對於 Oracle 效能也是有很大影響的。
  -- 調整 Oracle 的競爭:表和索引的引數設定對於 UPDATE 和 INSERT 的效能有很大的影響。
   我們首先從調整 Oracle 外部的環境開始。如果記憶體和 CPU 的資源不足的話,任何的 Oracle 調整都是沒有幫助的。
  外部的效能問題
  
  Oracle 並不是單獨執行的。 Oracle 資料庫的效能和外部的環境有很大的關係。這些外部的條件包括有:
   .CPU--CPU 資源的不足令查詢變慢。當查詢超過了 Oracle 伺服器的 CPU 效能時,你的資料庫效能就受到 CPU 的限制。
   .記憶體 -- 可用於 Oralce 的記憶體數量也會影響 SQL 的效能,特別是在資料緩衝和記憶體排序方面。
   .網路 -- 大量的 Net8 通訊令 SQL 的效能變慢。
   許多新手都錯誤的認為應該首先調整 Oracle 資料庫,而不是先確認外部資源是否足夠。實際上,如果外部環境出現瓶頸,再多的 Oracle 調整都是沒有幫助的。
   在檢查 Oracle 的外部環境時,有兩個方面是需要注意的:
  1 、當執行佇列的數目超過伺服器的 CPU 數量時,伺服器的效能就會受到 CPU 的限制。補救的方法是為伺服器增加額外的 CPU 或者關閉需要很多處理資源的元件,
例如 Oracle Parallel Query 。
  2 、記憶體分頁。當記憶體分頁時,記憶體容量已經不足,而記憶體頁是與磁碟上的交換區進行互動的。補救的方法是增加更多的記憶體,減少 Oracle SGA 的大小,或者關閉
Oracle 的多執行緒伺服器。
   可以使用各種標準的伺服器工具來得到伺服器的統計資料,例如 vmstat,glance,top 和 sar 。 DBA 的目標是確保資料庫伺服器擁有足夠的 CPU 和記憶體資源來處理
Oracle 的請求。
   以下讓我們來看一下 Oracle 的 row-resequencing 是如何能夠極大地減少磁碟 I/O 的。
  Row-resequencing (行的重新排序)
  
  就象我們上面提到的,有經驗的 Oracle DBA 都知道 I/O 是響應時間的最大組成部分。其中磁碟 I/O 特別厲害,因為當 Oracle 由磁碟上的一個資料檔案得到一個
資料塊時,讀的程式就必須等待物理 I/O 操作完成。磁碟操作要比資料緩衝慢 10,000 倍。因此,如果可以令 I/O 最小化,或者減少由於磁碟上的檔案競爭而帶來的瓶頸
,就可以大大地改善 Oracle 資料庫的效能。如果系統響應很慢,通過減少磁碟 I/O 就可以有一個很快的改善。如果在一個事務中通過按一定的範圍搜尋 primary-key
索引來訪問表,那麼重新以 CTAS 的方法組織表將是你減少 I/O 的首要策略。通過在物理上將行排序為和 primary-key 索引一樣的順序,就可以加快獲得資料的速度。
就象磁碟的負載平衡一樣,行的重新排序也是很簡單的,而且也很快。通過與其它的 DBA 管理技巧一起使用,就可以在高 I/O 的系統中大大地減少響應的時間。 在高容量
的線上事務處理環境中( online transaction processing , OLTP ),資料是由一個 primary 索引得到的,重新排序表格的行就可以令連續塊的順序和它們的 primary
索引一樣,這樣就可以在索引驅動的表格查詢中,減少物理 I/O 並且改善響應時間。這個技巧僅在應用選擇多行的時候有用,或者在使用索引範圍搜尋和應用發出多個查詢
來得到連續的 key 時有效。對於隨機的唯一 primary-key (主鍵)的訪問將不會由行重新排序中得到好處。
   讓我們看一下它是如何工作的。考慮以下的一個 SQL 的查詢,它使用一個索引來得到 100 行:
select salary from employee where last_name like 'B%';
這個查詢將會使用 last_name_index ,搜尋其中的每一行來得到目標行。這個查詢將會至少使用 100 次物理磁碟的讀取,因為 employee 的行存放在不同的資料塊中。
   不過,如果表中的行已經重新排序為和 last_name_index 的一樣,同樣的查詢又會怎樣處理呢?我們可以看到這個查詢只需要三次的磁碟 I/O 就讀完全部 100 個
員工的資料(一次用作索引的讀取,兩次用作資料塊的讀取),減少了 97 次的塊讀取。
   重新排序帶來的效能改善的程度在於在你開始的時候行的亂序性如何,以及你需要由序列中訪問多少行。至於一個表中的行與索引的排序鍵的匹配程度,可以檢視資料
字典中的 dba_indexes 和 dba_tables 檢視得到。
   在 dba_indexes 的檢視中,檢視 clustering_factor 列。如果 clustering_factor 的值和表中的塊數目大致一樣,那麼你的表和索引的順序是一樣的。不過,如果
clustering_factor 的值接近表中的行數目,那就表明表格中的行和索引的順序是不一樣的。
   行重新排序的作用是不可以小看的。在需要進行大範圍的索引搜尋的大表中,行重新排序可以令查詢的效能提高三倍。
   一旦你已經決定重新排序表中的行,你可以使用以下的工具之一來重新組織表格。
  . 使用 Oracle 的 Create Table As Select (CTAS) 語法來拷貝表格
  . Oracle9i 自帶的表格重新組織工具
  
SQL 語句的調優
  SQL 調優
  Oracle 的 SQL 調優是一個複雜的主題,甚至是需要整本書來介紹 Oracle SQL 調優的細微差別。不過有一些基本的規則是每個
Oracle DBA 都需要跟從的,這些規則可以改善他們系統的效能。 SQL 調優的目標是簡單的:
  消除不必要的大表全表搜尋:不必要的全表搜尋導致大量不必要的 I/O ,從而拖慢整個資料庫的效能。調優專家首先會根據查詢
返回的行數目來評價 SQL 。在一個有序的表中,如果查詢返回少於 40% 的行,或者在一個無序的表中,返回少於 7% 的行,那麼這個
查詢都可以調整為使用一個索引來代替全表搜尋。對於不必要的全表搜尋來說,最常見的調優方法是增加索引。可以在表中加入標準的
B 樹索引,也可以加入 bitmap 和基於函式的索引。要決定是否消除一個全表搜尋,你可以仔細檢查索引搜尋的 I/O 開銷和全表搜尋
的開銷,它們的開銷和資料塊的讀取和可能的並行執行有關,並將兩者作對比。在一些情況下,一些不必要的全表搜尋的消除可以通過
強制使用一個index 來達到,只需要在 SQL 語句中加入一個索引的提示就可以了。
  在全表搜尋是一個最快的訪問方法時,將小表的全表搜尋放到快取中,調優專家應該確保有一個專門的資料緩衝用作行緩衝。在
Oracle7 中,你可以使用 alter table xxx cache 語句,在 Oracle8 或以上,小表可以被強制為放到 KEEP 池中緩衝。
   確保最優的索引使用 :對於改善查詢的速度,這是特別重要的。有時 Oracle 可以選擇多個索引來進行查詢,調優專家必須檢查
每個索引並且確保 Oracle 使用正確的索引。它還包括 bitmap 和基於函式的索引的使用。
  . 確保最優的 JOIN 操作:有些查詢使用 NESTED LOOP join 快一些,有些則是 HASH join 快一些,另外一些則是
sort-merge join 更快。
這些規則看來簡單,不過它們佔 SQL 調優任務的 90% ,並且它們也無需完全懂得 Oracle SQL
的內部運作。以下我們來簡單概覽以下 Oracle SQL 的優化。
   調整 Oracle 的排序操作
   排序是 SQL 語法中一個小的方面,但很重要,在 Oracle 的調整中,它常常被忽略。當使用 create index 、 ORDER BY 或者
GROUP BY 的語句時, Oracle 資料庫
將會自動執行排序的操作。通常,在以下的情況下 Oracle 會進行排序的操作:
   使用 Order by 的 SQL 語句
   使用 Group by 的 SQL 語句
   在建立索引的時候
   進行 table join 時,由於現有索引的不足而導致 SQL 優化器呼叫 MERGE SORT
   當與 Oracle 建立起一個 session 時,在記憶體中就會為該 session 分配一個私有的排序區域。如果該連線是一個專用的連線
(dedicated connection) ,那麼就會根據 init.ora 中 sort_area_size 引數的大小在記憶體中分配一個 Program Global Area (PGA)
如果連線是通過多執行緒伺服器建立的,那麼排序的空間就在 large_pool 中分配。不幸的是,對於所有的 session ,用做排序的記憶體
量都必須是一樣的,我們不能為需要更大排序的操作分配額外的排序區域。因此,設計者必須作出一個平衡,在分配足夠的排序區域以
避免發生大的排序任務時出現磁碟排序( disk sorts )的同時,對於那些並不需要進行很大排序的任務,就會出現一些浪費。當然,
當排序的空間需求超出了 sort_area_size 的大小時,這時將會在 TEMP 表空間中分頁進行磁碟排序。磁碟排序要比記憶體排序大概慢
14,000 倍。
   上面我們已經提到,私有排序區域的大小是有 init.ora 中的 sort_area_size 引數決定的。每個排序所佔用的大小由 init.ora
中的 sort_area_retained_size 引數決定。當排序不能在分配的空間中完成時,就會使用磁碟排序的方式,即在 Oracle 例項中的臨
時表空間中進行。 磁碟排序的開銷是很大的,有幾個方面的原因。首先,和記憶體排序相比較,它們特別慢;而且磁碟排序會消耗臨時
表空間中的資源。 Oracle 還必須分配緩衝池塊來保持臨時表空間中的塊。無論什麼時候,記憶體排序都比磁碟排序好,磁碟排序將會
令任務變慢,並且會影響 Oracle 例項的當前任務的執行。還有,過多的磁碟排序將會令free buffer waits 的值變高,從而令其它
任務的資料塊由緩衝中移走。
   接著,讓我們看一下 Oracle 的競爭,並且看一下表的儲存引數的設定是如何影響 SQL UPDATE 和 INSERT 語句的效能的。
調整 Oracle 的競爭
  Oracle 的其中一個優點時它可以管理每個表空間中的自由空間。 Oracle 負責處理表和索引的空間管理,這樣就可以讓我們無需
懂得 Oracle 的表和索引的內部運作。不過,對於有經驗的 Oracle 調優專家來說,他需要懂得 Oracle 是如何管理表的 extent 和空
閒的資料塊。對於調整擁有高的 insert 或者 update 的系統來說,這是非常重要的。
   要精通物件的調整,你需要懂得 freelists 和 freelist 組的行為,它們和 pctfree 及 pctused 引數的值有關。這些知識對於
企業資源計劃( ERP )的應用是特別重要的,因為在這些應用中,不正確的表設定通常是 DML 語句執行慢的原因。
   對於初學者來說,最常見的錯誤是認為預設的 Oracle 引數對於所有的物件都是最佳的。除非磁碟的消耗不是一個問題,否則在設
置表的 pctfree 和 pctused 引數時,就必須考慮平均的行長和資料庫的塊大小,這樣空的塊才會被有效地放到 freelists 中。當這些
設定不正確時,那些得到的 freelists 也是 "dead" 塊,因為它們沒有足夠的空間來儲存一行,這樣將會導致明顯的處理延遲。
Freelists 對於有效地重新使用 Oracle 表空間中的空間是很重要的,它和 pctfree 及 pctused 這兩個儲存引數的設定直接相關。
通過將 pctused 設定為一個高的值,這時資料庫就會盡快地重新使用塊。不過,高效能和有效地重新使用表的塊是對立的。在調整
Oracle 的表格和索引時,需要認真考慮究竟需要高效能還是有效的空間重用,並且據此來設定表的引數。以下我們來看一下這些
freelists 是如何影響 Oracle 的效能的。
  當有一個請求需要插入一行到表格中時, Oracle 就會到 freelist 中尋找一個有足夠的空間來容納一行的塊。你也許知道,
freelist 串是放在表格或者索引的第一個塊中,這個塊也被稱為段頭( segment header )。 pctfree 和 pctused 引數的唯一目的就
是為了控制塊如何在 freelists 中進出。雖然 freelist link 和 unlink 是簡單的 Oracle 功能,不過設定 freelist link (pctused)
和 unlink (pctfree) 對 Oracle 的效能確實有影響。
   由 DBA 的基本知識知道, pctfree 引數是控制 freelist un-links 的(即將塊由 freelists 中移除)。設定 pctfree=10
意味著每個塊都保留 10% 的空間用作行擴充套件。 pctused 引數是控制 freelist re-links 的。設定 pctused=40 意味著只有在塊的
使用低於 40% 時才會回到表格的 freelists 中。
   許多新手對於一個塊重新回到 freelists 後的處理都有些誤解。其實,一旦由於一個刪除的操作而令塊被重新加入到 freelist
中,它將會一直保留在 freelist 中即使空間的使用超過了 60% ,只有在到達 pctfree 時才會將資料塊由 freelist 中移走。
   表格和索引儲存引數設定的要求總結
   以下的一些規則是用來設定 freelists, freelist groups, pctfree 和 pctused 儲存引數的。你也知道, pctused 和 pctfree
的值是可以很容易地通過 alter table 命令修改的,一個好的 DBA 應該知道如何設定這些引數的最佳值。
   有效地使用空間和高效能之間是有矛盾的,而表格的儲存引數就是控制這個方面的矛盾:
. 對於需要有效地重新使用空間,可以設定一個高的 pctused 值,不過副作用是需要額外的 I/O 。一個高的 pctused 值意味著相對滿
的塊都會放到 freelist 中。因此,這些塊在再次滿之前只可以接受幾行記錄,從而導致更多的 I/O 。
. 追求高效能的話,可以將 pctused 設定為一個低的值,這意味著 Oracle 不會將資料塊放到 freelists 中直到它幾乎是空的。
那麼塊將可以在滿之前接收更多的行,
因此可以減少插入操作的 I/O 。要記住 Oracle 擴充套件新塊的效能要比重新使用現有的塊高。對於 Oracle 來說,擴充套件一個表比管理
freelists 消耗更少的資源。
   讓我們來回顧一下設定物件儲存引數的一些常見規則:
   .經常將 pctused 設定為可以接收一條新行。對於不能接受一行的 free blocks 對於我們來說是沒有用的。如果這樣做,
將會令 Oracle 的效能變慢,因為
Oracle 將在擴充套件表來得到一個空的塊之前,企圖讀取 5 個 "dead" 的 free block 。
   .表格中 chained rows 的出現意味著 pctfree 太低或者是 db_block_size 太少。在很多情況下, RAW 和 LONG RAW 列都很巨
大,以至超過了 Oracle 的最大塊的大小,這時 chained rows 是不可以避免的。
   .如果一個表有同時插入的 SQL 語句,那麼它需要有同時刪除的語句。執行單一個一個清除的工作將會把全部的空閒塊放到一個
freelist 中,而沒有其它包含有任何空閒塊的 freelists 出現。
   . freelist 引數應該設定為表格同時更新的最大值。例如,如果在任何時候,某個表最多有 20 個使用者執行插入的操作,那麼
該表的引數應該設定為 freelists=20 。
   應記住的是 freelist groups 引數的值只是對於 Oracle Parallel Server 和 Real Application Clusters 才是有用的。對於這
類 Oracle , freelist groups
應該設定為訪問該表格的 Oracle Parallel Server 例項的數目。
---------------------------------------------------------------------------------------------------------------------------------------------------
  
  --在rbo中,連線表的順序,和predicate的順序都是有要求的,一般會按照從右往左來訪問FROM clause的表,從下往上來訪問where clause的predicates,
選擇小表作驅動表是關鍵的。
  --cbo中,則主要是根據表的統計資訊來覺得訪問路徑,所以保持表的統計資訊準確性就很有必要了。
  --在optimizer_mode=choose時候,系統首先會判斷FROM clause中的表是否有統計資訊,如果有,則根據CBO來執行sql,否則根據rbo來執行。也可以通過
hints來改變sql的訪問路徑。
  --ORACLE檢索順序有關--自底向上,所以小表放在最後面可以縮小檢索範圍,資料量大的放在最左邊(以FROM作參考)
優化器模式
  rule模式
  
  。總忽略CBO和統計資訊而基於規則
  choose模式
  
  。Oracle根據情況選擇rule or first_rows or all_rows
  first_rows 模式
  
  。基於成本,以最快的速度返回記錄,會造成總體查詢速度的下降或消耗更多的資源,傾向索引掃描,適合OLTP系統
  all_rows模式
  
  。基於成本,確保總體查詢時間最短,傾向並行全表掃描
  
  例如:
  Select last_name from customer order by last_name;用first_rows時,迅速返回記錄,但I/O量大,用all_rows時,返回記錄慢,但使用資源少。
  
  調整SQL表訪問
  
  全表掃描
  
  。返回記錄:未排序表>40%,排序表>7%,建議採用並行機制來提高訪問速度,DDS;
  
  索引訪問
  
  。最常用的方法,包括索引唯一掃描和索引範圍掃描,OLTP;
  
  快速完全索引掃描
  
  。訪問索引中所有資料塊,結果相當於全表掃描,可以用索引掃描代替全表掃描,例如:
  
  Select serv_id,count(* ) from tg_cdr01 group by serv_id;
  
  評估全表掃描的合法性
  
  如何實現並行掃描
  
  。永久並行化(不推薦)
  alter table customer parallel degree 8;
  
  。單個查詢並行化
  select /*+ full(emp) parallel(emp,8)*/ * from emp;
  
  分割槽表效果明顯
  
  優化SQL語句排序
  
  排序的操作:
  
  。order by 子句
  。group by 子句
  。select distinct子句
  。建立索引時
  。union或minus
  。排序合併連線
  
  如何避免排序
  
  。新增索引
  。在索引中使用distinct子句
  。避免排序合併連線
  
  使用提示進行調整
  
  使用提示的原則
  
  。語法:/*+ hint */
  。使用表別名:select /*+ index(e dept_idx)*/ * from emp e
  。檢驗提示
  
  常用的提示
  
  。rule (基於規則)
  。all_rows
  。first_rows
  。use_nl
  。use_hash
  。use_merge
  。index
  。index_asc
  。no_index
  。index_desc(常用於使用max內建函式)
  。index_combine(強制使用點陣圖索引)
  。index_ffs(索引快速完全掃描)
  。use_concat(將查詢中所有or條件使用union all)
  。parallel
  。noparallel
  。full
  。ordered(基於成本)
  
5.調整表連線
  
  表連線的型別
  
  。等連線
  where 條件中用等式連線;
  。外部連線(左、右連線)
  
  在where條件子句的等式謂詞放置一個(+)來實現,例如:
  
  該語句返回所有emp表的記錄;
  。自連線
   Select a.value total, B.value hard, (A.value - b.value) soft ,
  Round((b.value/a.value)*100,1) perc
  From v$sysstat a,v$sysstat b
  Where a.statistic# = 179
  and B.statistic# = 180;
  
  反連線
  
  反連線常用於not in or not exists中,是指在查詢中找到的任何記錄都不包含在結果集中的子查詢;不建議使用not in or not exists;
  
  。半連線
  
  查詢中使用exists,含義:即使在子查詢中返回多條重複的記錄,外部查詢也只返回一條記錄。
  
  巢狀迴圈連線
  
  。被連線表中存在索引的情況下使用;
  。使用use_nl。
  
  hash連線
  
  。Hash連線將驅動表載入在記憶體中,並使用hash技術連線第二個表,提高等連線速度。
  。適合於大表和小表連線;
  。使用use_hash。
  
  排序合併連線
  
  。排序合併連線不使用索引
  。使用原則:
  
  連線表子段中不存在可用索引;
  
  查詢返回兩個表中大部分的資料快;
  
  CBO認為全表掃描比索引掃描執行的更快。
  
  。使用use_merge
  
  使用臨時/中間表
  
  多個大表關聯時,可以分別把滿足條件的結果集存放到中間表,然後用中間表關聯;
  
  SQL子查詢的調整
  
  關聯與非關聯子查詢
  
  。關聯:子查詢的內部引用的是外部表,每行執行一次;
  。非關聯:子查詢只執行一次,存放在記憶體中。
  
  調整not in 和not exists語句
  
  。可以使用外部連線優化not in子句,例如:
  select ename from emp where dept_no not in
  (select dept_no from dept where dept_name =‘Math’);
  
  改為:
  select ename from emp,dept
  where emp.dept_no=dept.dept_no
  and dept.dept_name is null;
  
6.使用索引調整SQL
  
  Oracle 為什麼不使用索引
  
  。檢查被索引的列或組合索引的首列是否出現在PL/SQL語句的WHERE子句中,這是“執行計劃”能用到相關索引的必要條件。
  
  。看採用了哪種型別的連線方式。ORACLE的共有Sort Merge Join(SMJ)、Hash Join(HJ)和Nested Loop Join(NL)。
在兩張表連線,且內表的目標列上建有索引時,只有Nested Loop才能有效地利用到該索引。SMJ即使相關列上建有索引,最多
只能因索引的存在,避免資料排序過程。HJ由於須做HASH運算,索引的存在對資料查詢速度幾乎沒有影響。
  
  。看連線順序是否允許使用相關索引。假設表emp的deptno列上有索引,表dept的列deptno上無索引,WHERE語句有
emp.deptno=dept.deptno條件。在做NL連線時,emp做為外表,先被訪問,由於連線機制原因,外表的資料訪問方式是全表掃描,
emp.deptno上的索引顯然是用不上,最多在其上做索引全掃描或索引快速全掃描。
  
  。是否用到系統資料字典表或檢視。由於系統資料字典表都未被分析過,可能導致極差的“執行計劃”。但是不要擅自對資料
字典表做分析,否則可能導致死鎖,或系統效能下降。
  
  。索引列是否函式的引數。如是,索引在查詢時用不上。
  
  。是否存在潛在的資料型別轉換。如將字元型資料與數值型資料比較,ORACLE會自動將字元型用to_number()函式進行轉換,
從而導致上一種現象的發生。
  
  。是否為表和相關的索引蒐集足夠的統計資料。對資料經常有增、刪、改的表最好定期對錶和索引進行分析,可用SQL語句
“analyze table xxxx compute statistics for all indexes;”。ORACLE掌握了充分反映實際的統計資料,才有可能做出正確的選擇。
  
  。索引列的選擇性不高。   我們假設典型情況,有表emp,共有一百萬行資料,但其中的emp.deptno列,資料只有4種不同的值,
如10、20、30、40。雖然emp資料行有很多,ORACLE預設認定表中列的值是在所有資料行均勻分佈的,也就是說每種deptno值各有25萬
資料行與之對應。假設SQL搜尋條件DEPTNO=10,利用deptno列上的索引進行資料搜尋效率,往往不比全表掃描的高。
  
  。索引列值是否可為空(NULL)。如果索引列值可以是空值,在SQL語句中那些要返回NULL值的操作,將不會用到索引,如COUNT(*)
,而是用全表掃描。這是因為索引中儲存值不能為全空。
  
  。看是否有用到並行查詢(PQO)。並行查詢將不會用到索引。
  
  。如果從以上幾個方面都查不出原因的話,我們只好用採用在語句中加hint的方式強制ORACLE使用最優的“執行計劃”。  hint採用注
釋的方式,有行註釋和段註釋兩種方式。  如我們想要用到A表的IND_COL1索引的話,可採用以下方式:
 SELECT /*+ INDEX(A IND_COL1)*/ * FROM A WHERE COL1 = XXX;
  
  如何遮蔽索引
  
  語句的執行計劃中有不良索引時,可以人為地遮蔽該索引,方法:
  
  。數值型:在索引欄位上加0,例如
  select * from emp where emp_no+0 = v_emp_no;
  
  。字元型:在索引欄位上加‘’,例如
  select * from tg_cdr01 where msisdn||’’=v_msisdn;
---------------------------------------------------------------------------------------------------------------------------------------------------
(1)RBO&CBO。
Oracle有兩種執行優化器,一種是RBO(Rule Based Optimizer)基於規則的優化器,這種優化器是基於sql語句寫法選擇執行路徑的;
另一種是CBO(Cost Based Optimizer)基於規則的優化器,這種優化器是Oracle根據統計分析資訊來選擇執行路徑,如果表和索引
沒有進行分析,Oracle將會使用RBO代替CBO;如果表和索引很久未分析,CBO也有可能選擇錯誤執行路徑,不過CBO是Oracle發展的
方向,自8i版本來已經逐漸取代RBO.
(2)AUTOTRACE。
要看索引是否被使用我們要藉助Oracle的一個叫做AUTOTRACE功能,它顯示了sql語句的執行路徑,我們能看到Oracle內部是怎麼執行
sql的,這是一個非常好的輔助工具,在sql調優裡廣泛被運用。我們來看一下怎麼運用AUTOTRACE:
① 由於AUTOTRACE自動為使用者指定了Execution Plan,因此該使用者使用AUTOTRACE前必須已經建立了PLAN_TABLE。如果沒有的話,請
執行utlxplan.sql指令碼(它在$ORACLE_HOME/rdbms/admin目錄中)。
② AUTOTRACE可以通過執行plustrce.sql指令碼(它在$ORACLE_HOME/sqlplus/admin目錄中)來設定,用sys使用者登陸然後執行
plustrce.sql後會建立一個PLUSTRACE角色,然後給相關使用者授予PLUSTRACE角色,然後這些使用者就可以使用AUTOTRACE功能了。
③ AUTOTRACE的預設使用方法是set autotrace on,但是這方法不總是適合各種場合,特別當返回行數很多的時候。
Set autotrace traceonly提供了只檢視統計資訊而不查詢資料的功能。
    SQL> set autotrace on
SQL> select * from test;
         A
----------
         1
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   TABLE ACCESS (FULL) OF 'TEST'
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          0  consistent gets
          0  physical reads
          0  redo size
          0  bytes sent via SQL*Net to client
          0  bytes received via SQL*Net from client
          0  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
rows processed
SQL> set autotrace traceonly
SQL> select * from test.test;
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   TABLE ACCESS (FULL) OF 'TEST'
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          0  consistent gets
          0  physical reads
          0  redo size
          0  bytes sent via SQL*Net to client
          0  bytes received via SQL*Net from client
          0  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
rows processed
Hints是Oracle提供的一個輔助用法,按字面理解就是‘提示’的意思,確實它起得作用也是提示優化器按它所提供的關鍵字來
選擇執行路徑,特別適用於sql調整的時候。使用方法如下:
{DELETE|INSERT|SELECT|UPDATE} /*+ hint [text] [hint[text]]... */
具體可參考Oracle SQL Reference。
有了前面這些知識點,接下來讓我們來看一下什麼時候索引是不起作用的。以下列出幾種情況。
(1)型別不匹配時。
SQL> create table test.testindex (a varchar(2),b number);
表已建立。
SQL> create index ind_cola on test.testindex(a);
索引已建立。
SQL> insert into test.testindex values('1',1);
已建立 1 行。
SQL> commit;
提交完成。
SQL> analyze table test.testindex compute statistics for all indexes;
表已分析。
SQL> set autotrace on;
SQL> select /*+RULE */* FROM test.testindex where a='1';(使用基於rule的優化器,資料型別匹配的情況下)
A           B
-- ----------
1           1
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=HINT: RULE
   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'TESTINDEX'
   2    1     INDEX (RANGE SCAN) OF 'IND_COLA' (NON-UNIQUE)(使用了索引ind_cola)
――――――――――――――――――――――――――――――――――
SQL> select /*+RULE */* FROM test.testindex where a=1;(資料型別不匹配的情況)
A           B
-- ----------
1           1
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=HINT: RULE
   1    0   TABLE ACCESS (FULL) OF 'TESTINDEX'(優化器選擇了全表掃描)
(2)條件列包含函式但沒有建立函式索引。
SQL> select /*+ RULE */*  FROM test.testindex where upper(a)= 'A';(使用了函式upper()在列a上);
A           B
-- ----------
a           2
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=HINT: RULE
   1    0   TABLE ACCESS (FULL) OF 'TESTINDEX'(優化器選擇全表掃描)
----------------------------------------------------------
建立基於函式的索引
SQL> create index test.ind_fun on test.testindex(upper(a));
索引已建立。
SQL> insert into testindex values('a',2);
已建立1行。
SQL> commit;
提交完成。
SQL> select /*+ RULE*/*  FROM test.testindex where upper(a)='A';
A           B
-- ----------
a           2
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=HINT: RULE
   1    0   TABLE ACCESS (FULL) OF 'TESTINDEX'
(在RULE優化器下忽略了函式索引選擇了全表掃描)
-----------------------------------------------------------
SQL> select *  FROM test.testindex where upper(a)
='A';
A           B
-- ----------
a           2
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=5)
   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'TESTINDEX' (Cost=2 Card=
          1 Bytes=5)
   2    1     INDEX (RANGE SCAN) OF 'IND_FUN' (NON-UNIQUE) (Cost=1 Car
          d=1)(CBO優化器使用了ind_fun索引)
(3)複合索引中的前導列沒有被作為查詢條件。
建立一個複合索引
SQL> create index ind_com on test.testindex(a,b);
索引已建立。
SQL> select /*+ RULE*/* from test.testindex where a='1';
A           B
-- ----------
1           2
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=HINT: RULE
   1    0   INDEX (RANGE SCAN) OF 'IND_COM' (NON-UNIQUE)(條件列表包含前導列時使用索引ind_com)
SQL> select /*+ RULE*/* from test.testindex where b=1;
未選定行
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=HINT: RULE
   1    0   TABLE ACCESS (FULL) OF 'TESTINDEX'(條件列表不包括前導列是選擇全表掃描)
-----------------------------------------------------------
(4)CBO模式下選擇的行數比例過大,優化器採取了全表掃描。
SQL> select * from test.testindex where a='1';
A           B
-- ----------
1           2
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=1 Bytes=5)
   1    0   TABLE ACCESS (FULL) OF 'TESTINDEX' (Cost=1 Card=1 Bytes=5)
(表一共2行,選擇比例為50%,所以優化器選擇了全表掃描)
――――――――――――――――――――――――――――――――――
下面增加錶行數
SQL> declare i number;
  2  begin
  3  for i in 1 .. 100 loop
  4  insert into test.testindex values (to_char(i),i);
  5  end loop;
  6  end;
  7  /
PL/SQL 過程已成功完成。
SQL> commit;
提交完成。
SQL> select count(*) from test.testindex;
  COUNT(*)
----------
102
SQL> select * from test.testindex where a='1';
A             B
---- ----------
1             1
1             2
Execution Plan
SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=1 Bytes=5)
1    0   INDEX (RANGE SCAN) OF 'IND_COM' (NON-UNIQUE) (Cost=1 Card=1 Bytes=5)
(表一共102行,選擇比例為2/102=2%,所以優化器選擇了索引掃描)
(5)CBO模式下表很久沒分析,表的增長明顯,優化器採取了全表掃描。
SQL> select * from test.testindex where a like '1%';
A             B
---- ----------
1             2
1             1
10           10
11           11
12           12
13           13
14           14
15           15
16           16
17           17
18           18
19           19
100         100
已選擇13行。
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=13 Bytes=52)
   1   0  TABLE ACCESS (FULL) OF 'TESTINDEX' (Cost=1 Card=13 Bytes=52)
(表一共102行,選擇比例為13/102>10%,優化器選擇了全表掃描)
――――――――――――――――――――――――――――――――――
增加錶行數
SQL> declare i number;
  2  begin
  3  for i in 200 .. 1000 loop
  4  insert into test.testindex values (to_char(i),i);
  5  end loop;
  6  end;
  7  /
PL/SQL 過程已成功完成。
SQL> commit;
提交完成。
SQL> select count(*) from test.testindex;
  COUNT(*)
----------
903
SQL> select * from test.testindex where a like '1%';
A             B
----  ----------
1             2
1             1
10           10
11           11
12           12
13           13
14           14
15           15
16           16
17           17
18           18
19           19
100          100
1000         1000
已選擇14行。
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=13 Bytes=52)
   1  0  TABLE ACCESS (FULL) OF 'TESTINDEX' (Cost=1 Card=13 Bytes=52)
  (表一共903行,選擇比例為14/903<5%,優化器選擇了全表掃描,選擇路徑是錯誤的)
―――――――――――――――――――――――――――――
給表做分析
SQL> analyze table test.testindex compute statistics for table for all indexed c
olumns for all indexes;
表已分析。
SQL> select * from test.testindex where a like '1%';
A             B
---- ----------
1             2
1             1
10           10
100         100
1000       1000
11           11
12           12
13           13
14           14
15           15
16           16
17           17
18           18
19           19
已選擇14行。
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=4 Card=24 Bytes=120)
   1   0  TABLE ACCESS (BY INDEX ROWID) OF 'TESTINDEX' (Cost=4 Card=
          24 Bytes=120)
   2  1  INDEX (RANGE SCAN) OF 'IND_COLA' (NON-UNIQUE) (Cost=2 Ca
          rd=24)
(經過分析後優化器選擇了正確的路徑,使用了ind_cola索引)
---------------------------------------------------------------------------------------------------------------------------------------------------
--PCTFREE和PCTUSED調整
PCTFREE儲存引數
  PCTFREE儲存引數告訴ORACLE什麼時候應該將資料塊從物件的空閒列表中移出。ORACLE的預設引數是
PCTFREE=10;也就是說,一旦一個INSERT操作使得資料塊的90%被使用,這個資料塊就從空閒列表(free
list)中移出。
PCTUSED儲存引數
  PCTUSED儲存引數告訴ORACLE什麼時候將以前滿的資料塊加到空閒列表中。當記錄從資料表中刪除時,
資料庫的資料塊就有空間接受新的記錄,但只有當填充的空間降到PCTUSED值以下時,該資料塊才被連線
到空閒列表中,才可以往其中插入資料。PCTUSED的預設值是PCTUSED=40。
  儲存引數規則小結
  (1)PCTUSED較高意味著相對較滿的資料塊會被放置到空閒列表中,從而有效的重複使用資料塊的空間,
但會導致I/O消耗。PCTUSED低意味著在一個資料塊快空的時候才被放置到空閒列表中,資料塊一次能接受很多
的記錄,因此可以減少I/O消耗,提高效能。
  (2)PCTFREE的值較大意味著資料塊沒有被利用多少就從空閒列表中斷開連線,不利於資料塊的充分使用。
PCTFREE過小的結果是,在更新時可能會出現資料記錄遷移(Migration)的情況。(注:資料記錄遷移(Migration
)是指記錄在是UPDATE操作擴充套件了一個VARCHAR2型別的列或BLOB列後,PCTFREE引數所指定的空間不夠擴充套件,從而
記錄被ORACLE強制遷移到新的資料塊,發生這種情況將較嚴重的影響ORACLE的效能,出現更新緩慢)。
  (3)在批量的插入、刪除或者更新操作之前,先刪除該表上的索引,在操作完畢之後在重新建立,這樣有
助於提高批量操作的整體速度,並且保證B樹索引在操作之後有良好的效能。
--同優化器下的調整;
  基於成本優化器(CBO):
  (1)ORACLE 8i 以上版本更多地使用成本優化器,因為它更加智慧;
  (2)通過optimizer_mode=all_rows 或 first_rows來選擇CBO;通過
alter session set optimizer_goal=all_rows 或 first_rows來選擇CBO;通過新增hint來選擇CBO;
  (3)使用基於成本優化的一個關鍵是:存在表和索引的統計資料。通過analyze table 獲得表
的統計資料;通過analyze index獲得索引的統計資料。
  (4)對於超過5個表的連線的查詢,建議不要使用成本優化器,而是在SQL語句中通過新增
/* + rule */提示或者通過指定的執行計劃來避免可能會在20分鐘以上的SQL解析時間。
  基於規則優化器(RBO):
  (1)ORACLE 8i以及ORACLE的以前版本主要用(RBO),並且比較有效;
  (2)通過optimizer_mode=rule來選擇RBO;通過alter session set optimizer_goal=rule來選擇
RBO; 通過新增/* + rule */來選擇RBO;
  (3)在RBO中,from 子句的表的順序決定表的連線順序。From 子句的最後一個表是驅動表,這個
表應該是最小的表。
  (4)限定性最強的布林表示式放在最底層。
--跟蹤、優化SQL語句的方法
  保證在例項級將TIMED_STATISTICS設定為TRUE(在 INIT.ORA中永久的設定它或執行 ALTER SYSTEM命
令臨時設定它);
  保證將MAX_DUMP_FILE_SIZE設定的較高。此引數控制跟蹤檔案的大小。
  決定USER_DUMP_DEST所指向的位置,並保證有足夠的磁碟空間。這是放置跟蹤檔案的位置。
  在應用系統執行時,開啟所懷疑的回話的SQL_TRACE.(在 INIT.ORA中通過SQL_TRACE=TRUE永久的設定
令臨時設定它)
  執行業務相關操作;
跟蹤檔案中的資訊不全,因為可能有一部分還在快取中);
  定位跟蹤檔案;
  對步驟6的跟蹤檔案進行TKPROF,生成報告檔案;
  研究此報告檔案,可以看到CPU、DISK、 QUERY、 COUNT等引數和execution plan(執行計劃),優化開
銷最大的SQL;
---------------------------------------------------------------------------------------------------------------------------------------------------
這是因為當進行index full scan的時候 oracle定位到索引的root block,然後到branch block(如果有的話),
再定位到第一個leaf block, 然後根據leaf block的雙向連結串列順序讀取。它所讀取的塊都是有順序的,也是經過排序的。
而index fast full scan則不同,它是從段頭開始,讀取包含點陣圖塊,root block,所有的branch block, leaf block,
讀取的順序完全有物理儲存位置決定,並採取多塊讀,沒次讀取db_file_multiblock_read_count個塊。
索引是提高資料查詢最有效的方法,也是最難全面掌握的技術,因為正確的索引可能使效率提高10000倍,而無效的索引
可能是浪費了資料庫空間,甚至大大降低查詢效能。
  索引的管理成本
  1、 儲存索引的磁碟空間
  2、 執行資料修改操作(INSERT、UPDATE、DELETE)產生的索引維護
  3、 在資料處理時回需額外的回退空間。
  實際資料修改測試:
  一個表有欄位A、B、C,同時進行插入10000行記錄測試
  在沒有建索引時平均完成時間是2.9秒
  在對A欄位建索引後平均完成時間是6.7秒
  在對A欄位和B欄位建索引後平均完成時間是10.3秒
  在對A欄位、B欄位和C欄位都建索引後平均完成時間是11.7秒
  從以上測試結果可以明顯看出索引對資料修改產生的影響
  索引按儲存方法分類
  B*樹索引
  B*樹索引是最常用的索引,其儲存結構類似書的索引結構,
有分支和葉兩種型別的儲存資料塊,分支塊相當於書的大目錄,葉塊相當於索引到的具體的書頁。一般索引及唯一約束索引都使用B*樹索引。
  點陣圖索引
  點陣圖索引儲存主要用來節省空間,減少ORACLE對資料塊的訪問,它採用點陣圖偏移方式來與表的行ID號對應,採用點陣圖索引一般是重複值
太多的表欄位。點陣圖索引在實際密集型OLTP(資料事務處理)中用得比較少,因為OLTP會對錶進行大量的刪除、修改、新建操作,ORACLE每次
進行操作都會對要操作的資料塊加鎖,所以多人操作很容易產生資料塊鎖等待甚至死鎖現象。在OLAP(資料分析處理)中應用點陣圖有優勢,因
為OLAP中大部分是對資料庫的查詢操作,而且一般採用資料倉儲技術,所以大量資料採用點陣圖索引節省空間比較明顯。
  索引按功能分類
  唯一索引
  唯一索引有兩個作用,一個是資料約束,一個是資料索引,其中資料約束主要用來保證資料的完整性,唯一索引產生的索引記錄中每一
條記錄都對應一個唯一的ROWID。
  主關鍵字索引
  主關鍵字索引產生的索引同唯一索引,只不過它是在資料庫建立主關鍵字時系統自動建立的。
  一般索引
  一般索引不產生資料約束作用,其功能主要是對欄位建立索引表,以提高資料查詢速度。
  索引按索引物件分類
  單列索引(表單個欄位的索引)
  多列索引(表多個欄位的索引)
  函式索引(對欄位進行函式運算的索引)
  建立函式索引的方法:
  create index 收費日期索引 on GC_DFSS(trunc(sk_rq))
  create index 完全客戶編號索引 on yhzl(qc_bh||kh_bh)
  在對函式進行了索引後,如果當前會話要引用應設定當前會話的query_rewrite_enabled為TRUE。
  alter session set query_rewrite_enabled=true
  注:如果對使用者函式進行索引的話,那使用者函式應加上 deterministic引數,意思是函式在輸入值固定的情況下返回值也固定。例:
  create or replace function trunc_add(input_date date)return date deterministic
  as
  begin
  return trunc(input_date+1);
  end trunc_add;
  應用索引的掃描分類
  INDEX UNIQUE SCAN(按索引唯一值掃描)
  select * from zl_yhjbqk where hbs_bh='5420016000'
  INDEX RANGE SCAN(按索引值範圍掃描)
  select * from zl_yhjbqk where hbs_bh>'5420016000'
  select * from zl_yhjbqk where qc_bh>'7001'
  INDEX FAST FULL SCAN(按索引值快速全部掃描)
  select hbs_bh from zl_yhjbqk order by hbs_bh
  select count(*) from zl_yhjbqk
  select qc_bh from zl_yhjbqk group by qc_bh
  什麼情況下應該建立索引
  表的主關鍵字
  自動建立唯一索引
  如zl_yhjbqk(使用者基本情況)中的hbs_bh(戶標識編號)
  表的欄位唯一約束
  ORACLE利用索引來保證資料的完整性
  如lc_hj(流程環節)中的lc_bh+hj_sx(流程編號+環節順序)
  直接條件查詢的欄位
  在SQL中用於條件約束的欄位
  如zl_yhjbqk(使用者基本情況)中的qc_bh(區冊編號)
  select * from zl_yhjbqk where qc_bh=’7001’
  查詢中與其它表關聯的欄位
  欄位常常建立了外來鍵關係
  如zl_ydcf(用電成份)中的jldb_bh(計量點表編號)
  select * from zl_ydcf a,zl_yhdb b where a.jldb_bh=b.jldb_bh and b.jldb_bh=’540100214511’
  查詢中排序的欄位
  排序的欄位如果通過索引去訪問那將大大提高排序速度
  select * from zl_yhjbqk order by qc_bh(建立qc_bh索引)
  select * from zl_yhjbqk where qc_bh='7001' order by cb_sx(建立qc_bh+cb_sx索引,注:只是一個索引,其中包括qc_bh和cb_sx欄位)
  查詢中統計或分組統計的欄位
  select max(hbs_bh) from zl_yhjbqk
  select qc_bh,count(*) from zl_yhjbqk group by qc_bh
  什麼情況下應不建或少建索引
  表記錄太少
  如果一個表只有5條記錄,採用索引去訪問記錄的話,那首先需訪問索引表,再通過索引表訪問資料表,一般索引表與資料表不在同一個
資料塊,這種情況下ORACLE至少要往返讀取資料塊兩次。而不用索引的情況下ORACLE會將所有的資料一次讀出,處理速度顯然會比用索引快。
  如表zl_sybm(使用部門)一般只有幾條記錄,除了主關鍵字外對任何一個欄位建索引都不會產生效能優化,實際上如果對這個表進行了統
計分析後ORACLE也不會用你建的索引,而是自動執行全表訪問。如:
  select * from zl_sybm where sydw_bh='5401'(對sydw_bh建立索引不會產生效能優化)
  經常插入、刪除、修改的表
  對一些經常處理的業務表應在查詢允許的情況下儘量減少索引,如zl_yhbm,gc_dfss,gc_dfys,gc_fpdy等業務表。
  資料重複且分佈平均的表欄位
  假如一個表有10萬行記錄,有一個欄位A只有T和F兩種值,且每個值的分佈概率大約為50%,那麼對這種表A欄位建索引一般不會提高資料庫
的查詢速度。
  經常和主欄位一塊查詢但主欄位索引值比較多的表欄位
  如gc_dfss(電費實收)表經常按收費序號、戶標識編號、抄表日期、電費發生年月、操作標誌來具體查詢某一筆收款的情況,如果將所有的
欄位都建在一個索引裡那將會增加資料的修改、插入、刪除時間,從實際上分析一筆收款如果按收費序號索引就已經將記錄減少到只有幾條,如
果再按後面的幾個欄位索引查詢將對效能不產生太大的影響。
  如何只通過索引返回結果
  一個索引一般包括單個或多個欄位,如果能不訪問表直接應用索引就返回結果那將大大提高資料庫查詢的效能。對比以下三個SQL,其中對錶
zl_yhjbqk的hbs_bh和qc_bh欄位建立了索引:
  1 select hbs_bh,qc_bh,xh_bz from zl_yhjbqk where qc_bh=’7001’
  執行路徑:
  SELECT STATEMENT, GOAL = CHOOSE 11 265 5565
  TABLE ACCESS BY INDEX ROWID DLYX ZL_YHJBQK 11 265 5565
  INDEX RANGE SCAN DLYX 區冊索引 1 265
  平均執行時間(0.078秒)
  2 select hbs_bh,qc_bh from zl_yhjbqk where qc_bh=’7001’
  執行路徑:
  SELECT STATEMENT, GOAL = CHOOSE 11 265 3710
  TABLE ACCESS BY INDEX ROWID DLYX ZL_YHJBQK 11 265 3710
  INDEX RANGE SCAN DLYX 區冊索引 1 265
  平均執行時間(0.078秒)
  3 select qc_bh from zl_yhjbqk where qc_bh=’7001’
  執行路徑:
  SELECT STATEMENT, GOAL = CHOOSE 1 265 1060
  INDEX RANGE SCAN DLYX 區冊索引 1 265 1060
  平均執行時間(0.062秒)
  從執行結果可以看出第三條SQL的效率最高。執行路徑可以看出第1、2條SQL都多執行了TABLE ACCESS BY INDEX ROWID(通過ROWID訪問表)
這個步驟,因為返回的結果列中包括當前使用索引(qc_bh)中未索引的列(hbs_bh,xh_bz),而第3條SQL直接通過QC_BH返回了結果,這就是通過
索引直接返回結果的方法。
  如何重建索引
  alter index 表電量結果表主鍵 rebuild
  如何快速新建大資料量表的索引
  如果一個表的記錄達到100萬以上的話,要對其中一個欄位建索引可能要花很長的時間,甚至導致伺服器資料庫當機,因為在建索引的時候
ORACLE要將索引欄位所有的內容取出並進行全面排序,資料量大的話可能導致伺服器排序記憶體不足而引用磁碟交換空間進行,這將嚴重影響服
務器資料庫的工作。解決方法是增大資料庫啟動初始化中的排序記憶體引數,如果要進行大量的索引修改可以設定10M以上的排序記憶體(ORACLE預設
大小為64K),在索引建立完成後應將引數修改回來,因為在實際OLTP資料庫應用中一般不會用到這麼大的排序記憶體。
通過分析SQL語句的執行計劃優化SQL (三)
  第4章 ORACLE的優化器
  優化器有時也被稱為查詢優化器,這是因為查詢是影響資料庫效能最主要的部分,不要以為只有SELECT語句是查詢。實際上,帶有任何
WHERE條件的DML(INSERT、UPDATE、DELETE)語句中都包含查詢要求,在後面的文章中,當說到查詢時,不一定只是指SELECT語句,也有可能
指DML語句中的查詢部分。優化器是所有關聯式資料庫引擎中的最神祕、最富挑戰性的部件之一,從效能的角度看也是最重要的部分,它效能的
高低直接關係到資料庫效能的好壞。
  我們知道,SQL語句同其它語言(如C語言)的語句不一樣,它是非過程化(non-procedural)的語句,即當你要取資料時,不需要告訴資料
庫通過何種途徑去取資料,如到底是通過索引取資料,還是應該將表中的每行資料都取出來,然後再通過一一比較的方式取資料(即全表掃描),
這是由資料庫的優化器決定的,這就是非過程化的含義,也就是說,如何取資料是由優化器決定,而不是應用開發者通過程式設計決定。在處理SQL
的SELECT、UPDATE、INSERT或DELETE語句時,Oracle 必須訪問語句所涉及的資料,Oracle的優化器部分用來決定訪問資料的有效路徑,使得語
句執行所需的I/O和處理時間最小。
  為了實現一個查詢,核心必須為每個查詢定製一個查詢策略,或為取出符合條件的資料生成一個執行計劃(execution plan)。典型的,對於
同一個查詢,可能有幾個執行計劃都符合要求,都能得到符合條件的資料。例如,參與連線的表可以有多種不同的連線方法,這取決於連線條件
和優化器採用的連線方法。為了在多個執行計劃中選擇最優的執行計劃,優化器必須使用一些實際的指標來衡量每個執行計劃使用的資源(I/0次
數、CPU等),這些資源也就是我們所說的代價(cost)。如果一個執行計劃使用的資源多,我們就說使用執行計劃的代價大。以執行計劃的代價大
小作為衡量標準,優化器選擇代價最小的執行計劃作為真正執行該查詢的執行計劃,並拋棄其它的執行計劃。
  在ORACLE的發展過程中,一共開發過2種型別的優化器:基於規則的優化器和基於代價的優化器。這2種優化器的不同之處關鍵在於:取得代
價的方法與衡量代價的大小不同。現對每種優化器做一下簡單的介紹:
  基於規則的優化器 -- Rule Based (Heuristic) Optimization(簡稱RBO):
  在ORACLE7之前,主要是使用基於規則的優化器。ORACLE在基於規則的優化器中採用啟發式的方法(Heuristic Approach)或規則(Rules)來生
成執行計劃。例如,如果一個查詢的where條件(where clause)包含一個謂詞(predicate,其實就是一個判斷條件,如”=”, “>”, ”<”等),而且
該謂詞上引用的列上有有效索引,那麼優化器將使用索引訪問這個表,而不考慮其它因素,如表中資料的多少、表中資料的易變性、索引的可選
擇性等。此時資料庫中沒有關於表與索引資料的統計性描述,如表中有多上行,每行的可選擇性等。優化器也不考慮例項引數,如multi block
i/o、可用排序記憶體的大小等,所以優化器有時就選擇了次優化的計劃作為真正的執行計劃,導致系統效能不高。
  如,對於select * from emp where deptno = 10這個查詢來說,如果是使用基於規則的優化器,而且deptno列上有有效的索引,則會通過
deptno列上的索引來訪問emp表。在絕大多數情況下,這是比較高效的,但是在一些特殊情況下,使用索引訪問也有比較低效的時候,現舉例說
明:  
  1) emp表比較小,該表的資料只存放在幾個資料塊中。此時使用全表掃描比使用索引訪問emp表反而要好。因為表比較小,極有可能資料全
在記憶體中,所以此時做全表掃描是最快的。而如果使用索引掃描,需要先從索引中找到符合條件記錄的rowid,然後再一一根據這些rowid從emp
中將資料取出來,在這種條件下,效率就會比全表掃描的效率要差一些。
  2) emp表比較大時,而且deptno = 10條件能查詢出表中大部分的資料如(50%)。如該表共有4000萬行資料,共放在有500000個資料塊中,
每個資料塊為8k,則該表共有約4G,則這麼多的資料不可能全放在記憶體中,絕大多數需要放在硬碟上。此時如果該查詢通過索引查詢,則是你夢
魘的開始。db_file_multiblock_read_count引數的值200。如果採用全表掃描,則需要500000/db_file_multiblock_read_count=500000/200=
2500次I/O。但是如果採用索引掃描,假設deptno列上的索引都已經cache到記憶體中,所以可以將訪問索引的開銷忽略不計。因為要讀出4000萬x
50% = 2000萬資料,假設在讀這2000萬資料時,有99.9%的命中率,則還是需要20000次I/O,比上面的全表掃描需要的2500次多多了,所以在這種
情況下,用索引掃描反而效能會差很多。在這樣的情況下,用全表掃描的時間是固定的,但是用索引掃描的時間會隨著選出資料的增多使查詢時
間相應的延長。
  上面是枯燥的假設資料,現在以具體的例項給予驗證:
  環境: oracle 817 + Linux + 陣列櫃,表SWD_BILLDETAIL有3200多萬資料;
  表的id列、cn列上都有索引
  經檢視執行計劃,發現執行select count(id) from SWD_BILLDETAIL;使用全表掃描,執行完用了大約1.50分鐘(4次執行取平均,每次分別為
1.45 1.51 2.00 1.46)。而執行select count(id) from SWD_BILLDETAIL where cn <'6';卻用了2個小時還沒有執行完,經分析該語句使用了cn列
上的索引,然後利用查詢出的rowid再從表中查詢資料。我為什麼不使用select count(cn) from SWD_BILLDETAIL where cn <'6';呢?
後面在分析執行路徑的索引掃描時時會給出說明。
  下面就是基於規則的優化器使用的執行路徑與各個路徑對應的等級:
  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認為越往下執行的代價越大,即等級越低。在RBO生成執行計劃時,如果它發現有等級高的執行路徑可用,則肯定
會使用等級高的路徑,而不管任何其它影響效能的元素,即RBO通過上面的路徑的等級決定執行路徑的代價,執行路徑的等級越高,則使用該執
行路徑的代價越小。如上面2個例子所述,如果使用RBO,則肯定使用索引訪問表,也就是選擇了比較差的執行計劃,這樣會給資料庫效能帶來很
大的負面影響。為了解決這個問題,從ORACLE 7開始oracle引入了基於代價的優化器,下面給出了介紹。
基於代價的優化器 -- Cost Based Optimization(簡稱CBO)
  Oracle把一個代價引擎(Cost Engine)整合到資料庫核心中,用來估計每個執行計劃需要的代價,該代價將每個執行計劃所耗費的資源進行
量化,從而CBO可以根據這個代價選擇出最優的執行計劃。一個查詢耗費的資源可以被分成3個基本組成部分:I/O代價、CPU代價、network代價。
I/O代價是將資料從磁碟讀入記憶體所需的代價。訪問資料包括將資料檔案中資料塊的內容讀入到SGA的資料快取記憶體中,在一般情況下,該代價是
處理一個查詢所需要的最主要代價,所以我們在優化時,一個基本原則就是降低查詢所產生的I/O總次數。CPU代價是處理在記憶體中資料所需要的
代價,如一旦資料被讀入記憶體,則我們在識別出我們需要的資料後,在這些資料上執行排序(sort)或連線(join)操作,這需要耗費CPU資源。
  對於需要訪問跨節點(即通常說的伺服器)資料庫上資料的查詢來說,存在network代價,用來量化傳輸操作耗費的資源。查詢遠端表的查詢
或執行分散式連線的查詢會在network代價方面花費比較大。
  在使用CBO時,需要有表和索引的統計資料(分析資料)作為基礎資料,有了這些資料,CBO才能為各個執行計劃計算出相對準確的代價,從而
使CBO選擇最佳的執行計劃。所以定期的對錶、索引進行分析是絕對必要的,這樣才能使統計資料反映資料庫中的真實情況。否則就會使CBO選擇
較差的執行計劃,影響資料庫的效能。分析操作不必做的太頻繁,一般來說,每星期一次就足夠了。切記如果想使用CBO,則必須定期對錶和索引
進行分析。
  對於分析用的命令,隨著資料庫版本的升級,用的命令也發生了變換,在oracle 8i以前,主要是用ANALYZE命令。在ORACLE 8I以後,又引入
了DBMS_STATS儲存包來進行分析。幸運的是從ORACLE 10G以後,分析工作變成自動的了,這減輕的DBA的負擔,不過在一些特殊情況下,還需要一
些手工分析。
  如果採用了CBO優化器,而沒有對錶和索引進行分析,沒有統計資料,則ORACLE使用預設的統計資料(至少在ORACLE 9I中是這樣),這可以從
oracle的文件上找到。使用的預設值肯定與系統的實際統計值不一致,這可能會導致優化器選擇錯誤的執行計劃,影響資料庫的效能。
  要注意的是:雖然CBO的功能隨著ORACLE新版本的推出,功能越來越強,但它不是能包治百病的神藥,否則就不再需要DBA了,那我就慘了
實際上任何一個語句,隨著硬體環境與應用資料的不同,該語句的執行計劃可能需要隨之發生變化,這樣才能取得最好的效能。所以有時候不
在具體的環境下而進行SQL效能調整是徒勞的。
  在ORACLE8I推出的時候,ORACLE極力建議大家使用CBO,說CBO有種種好處,但是在那是ORACLE開發的應用系統還是使用基於規則的優化器,
從這件事上我們可以得出這樣的結論:
  
  1) 如果團隊的資料庫水平很高而且都熟悉應用資料的特點,RBO也可以取得很好的效能。
  
  2)CBO不是很穩定,但是一個比較有前途的優化器,Oracle極力建議大家用是為了讓大家儘快發現它的BUG,以便進一步改善,但是
ORACLE為了對自己開發的應用系統負責,他們還是使用了比較熟悉而且成熟的RBO。從這個事情上給我們的啟發就是:我們在以後的開發中,
應該儘量採用我們熟悉並且成熟的技術,而不要一味的採用新技術,一味採用新技術並不一定能開發出好的產品。幸運的是從ORACLE 10G後,
CBO已經足夠的強大與智慧,大家可以放心的使用該技術,因為ORACLE 10G後,Oracle自己開發的應用系統也使用CBO優化器了。而且ORACLE
規定,從ORACLE 10G開始,開始廢棄RBO優化器。這句話並不是指在ORACLE 10G中不能使用RBO,而是從ORACLE 10G開始開始,不再為RBO的
BUG提供修補服務。
  在上面的第2個例子中,如果採用CBO優化器,它就會考慮emp表的行數,deptno列的統計資料,發現對該列做查詢會查詢出過多的資料,
並且考慮db_file_multiblock_read_count引數的設定,發現用全表掃描的代價比用索引掃描的代價要小,從而使用全表掃描從而取得良好
的執行效能。
  判斷當前資料庫使用何種優化器:
  主要是由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新引入的。
  從ORACLE V7以來,optimizer_mode引數的預設設定應是"choose",即如果對已分析的表查詢的話選擇CBO,否則選擇RBO。在此種設定中,
如果採用了CBO,則預設為CBO中的all_rows模式。
  注意:即使指定資料庫使用RBO優化器,但有時ORACLE資料庫還是會採用CBO優化器,這並不是ORACLE的BUG,主要是由於從ORACLE 8I後引
入的許多新特性都必須在CBO下才能使用,而你的SQL語句可能正好使用了這些新特性,此時資料庫會自動轉為使用CBO優化器執行這些語句。
  什麼是優化
  優化是選擇最有效的執行計劃來執行SQL語句的過程,這是在處理任何資料的語句(SELECT,INSERT,UPDATE或DELETE)中的一個重要步驟。
對Oracle來說,執行這樣的語句有許多不同的方法,譬如說,將隨著以什麼順序訪問哪些表或索引的不同而不同。所使用的執行計劃可以決定
語句能執行得有多快。Oracle中稱之為優化器(Optimizer)的元件用來選擇這種它認為最有效的執行計劃。
  由於一系列因素都會會影響語句的執行,優化器綜合權衡各個因素,在眾多的執行計劃中選擇認為是最佳的執行計劃。然而,應用設計人
員通常比優化器更知道關於特定應用的資料特點。無論優化器多麼智慧,在某些情況下開發人員能選擇出比優化器選擇的最優執行計劃還要好
的執行計劃。這是需要人工干預資料庫優化的主要原因。事實表明,在某些情況下,確實需要DBA對某些語句進行手工優化。
  注:從Oracle的一個版本到另一個版本,優化器可能對同一語句生成不同的執行計劃。在將來的Oracle 版本中,優化器可能會基於它可以
用的更好、更理想的資訊,作出更優的決策,從而導致為語句產生更優的執行計劃。
在物理層,oracle讀取資料,一次讀取的最小單位為資料庫塊(由多個連續的作業系統塊組成),一次讀取的最大值由作業系統一次I/O的最大值
與multiblock引數共同決定,所以即使只需要一行資料,也是將該行所在的資料庫塊讀入記憶體。邏輯上,oracle用如下存取方法訪問資料:
  1) 全表掃描(Full Table Scans, FTS)
  為實現全表掃描,Oracle讀取表中所有的行,並檢查每一行是否滿足語句的WHERE限制條件。Oracle順序地讀取分配給表的每個資料塊,直
到讀到表的最高水線處(high water mark, HWM,標識表的最後一個資料塊)。一個多塊讀操作可以使一次I/O能讀取多塊資料塊
(db_block_multiblock_read_count引數設定),而不是隻讀取一個資料塊,這極大的減少了I/O總次數,提高了系統的吞吐量,所以利用多塊讀
的方法可以十分高效地實現全表掃描,而且只有在全表掃描的情況下才能使用多塊讀操作。在這種訪問模式下,每個資料塊只被讀一次。由於
HWM標識最後一塊被讀入的資料,而delete操作不影響HWM值,所以一個表的所有資料被delete後,其全表掃描的時間不會有改善,一般我們需要
使用truncate命令來使HWM值歸為0。幸運的是oracle 10G後,可以人工收縮HWM的值。
  由FTS模式讀入的資料被放到快取記憶體的Least Recently Used (LRU)列表的尾部,這樣可以使其快速交換出記憶體,從而不使記憶體重要的資料
被交換出記憶體。使用FTS的前提條件:在較大的表上不建議使用全表掃描,除非取出資料的比較多,超過總量的5% -- 10%,或你想使用並行查詢
功能時。
  使用全表掃描的例子:
  ~~~~~~~~~~~~~~~~~~~~~~~~
SQL> explain plan for select * from dual;
Query Plan
-----------------------------------------
SELECT STATEMENT [CHOOSE] Cost=
TABLE ACCESS FULL DUAL
  2) 通過ROWID的表存取(Table Access by ROWID或rowid lookup)
  行的ROWID指出了該行所在的資料檔案、資料塊以及行在該塊中的位置,所以通過ROWID來存取資料可以快速定位到目標資料上,是Oracle存取
單行資料的最快方法。為了通過ROWID存取表,Oracle 首先要獲取被選擇行的ROWID,或者從語句的WHERE子句中得到,或者通過表的一個或多個索
引的索引掃描得到。Oracle然後以得到的ROWID為依據定位每個被選擇的行。
  這種存取方法不會用到多塊讀操作,一次I/O只能讀取一個資料塊。我們會經常在執行計劃中看到該存取方法,如通過索引查詢資料。
  使用ROWID存取的方法:
SQL> explain plan for select * from dept where rowid = 'AAAAyGAADAAAAATAAF';
Query Plan
------------------------------------
SELECT STATEMENT [CHOOSE] Cost=1
TABLE ACCESS BY ROWID DEPT [ANALYZED]
3)索引掃描(Index Scan或index lookup)
  我們先通過index查詢到資料對應的rowid值(對於非唯一索引可能返回多個rowid值),然後根據rowid直接從表中得到具體的資料,這種查詢方式
稱為索引掃描或索引查詢(index lookup)。一個rowid唯一的表示一行資料,該行對應的資料塊是通過一次i/o得到的,在此情況下該次i/o只會讀取
一個資料庫塊。
  在索引中,除了儲存每個索引的值外,索引還儲存具有此值的行對應的ROWID值。索引掃描可以由2步組成:
  (1) 掃描索引得到對應的rowid值。
  (2) 通過找到的rowid從表中讀出具體的資料。每步都是單獨的一次I/O,但是對於索引,由於經常使用,絕大多數都已經CACHE到記憶體中,所以第
1步的I/O經常是邏輯I/O,即資料可以從記憶體中得到。但是對於第2步來說,如果表比較大,則其資料不可能全在記憶體中,所以其I/O很有可能是物理I/O,
這是一個機械操作,相對邏輯I/O來說,是極其費時間的。所以如果多大表進行索引掃描,取出的資料如果大於總量的5% -- 10%,使用索引掃描會效率
下降很多。
  如下列所示:
SQL> explain plan for select empno, ename from emp where empno=10;
Query Plan
------------------------------------
SELECT STATEMENT [CHOOSE] Cost=1
TABLE ACCESS BY ROWID EMP [ANALYZED]
INDEX UNIQUE SCAN EMP_I1
  注意TABLE ACCESS BY ROWID EMP部分,這表明這不是通過FTS存取路徑訪問資料,而是通過rowid lookup存取路徑訪問資料的。在此例中,所需要
的rowid是由於在索引查詢empno列的值得到的,這種方式是INDEX UNIQUE SCAN查詢,後面給予介紹,EMP_I1為使用的進行索引查詢的索引名字。
  但是如果查詢的資料能全在索引中找到,就可以避免進行第2步操作,避免了不必要的I/O,此時即使通過索引掃描取出的資料比較多,效率還是很
高的,因為這隻會在索引中讀取。所以上面我在介紹基於規則的優化器時,使用了select count(id) from SWD_BILLDETAIL where cn <'6',而沒有使
用select count(cn) from SWD_BILLDETAIL where cn <'6'。因為在實際情況中,只查詢被索引列的值的情況極為少,所以,如果我在查詢中使用count
(cn),則不具有代表性。
SQL> explain plan for select empno from emp where empno=10; -- 只查詢empno列值
Query Plan
------------------------------------
SELECT STATEMENT [CHOOSE] Cost=1
INDEX UNIQUE SCAN EMP_I1
  進一步講,如果sql語句中對索引列進行排序,因為索引已經預先排序好了,所以在執行計劃中不需要再對索引列進行排序
SQL> explain plan for select empno, ename from emp
where empno > 7876 order by empno;
Query Plan
--------------------------------------------------------------------------------
SELECT STATEMENT [CHOOSE] Cost=1
TABLE ACCESS BY ROWID EMP [ANALYZED]
INDEX RANGE SCAN EMP_I1 [ANALYZED]
  從這個例子中可以看到:因為索引是已經排序了的,所以將按照索引的順序查詢出符合條件的行,因此避免了進一步排序操作。
  根據索引的型別與where限制條件的不同,有4種型別的索引掃描:
  索引唯一掃描(index unique scan)
  索引範圍掃描(index range scan)
  索引全掃描(index full scan)
  索引快速掃描(index fast full scan)
  (1) 索引唯一掃描(index unique scan)
  通過唯一索引查詢一個數值經常返回單個ROWID。如果該唯一索引有多個列組成(即組合索引),則至少要有組合索引的引導列參
與到該查詢中,如建立一個索引:
create index idx_test on emp(ename, deptno, loc)。則select ename from emp where ename = 'JACK' and deptno = 'DEV'語
句可以使用該索引。如果該語句只返回一行,則存取方法稱為索引唯一掃描。而select ename from emp where deptno = 'DEV'語句
則不會使用該索引,因為where子句種沒有引導列。如果存在UNIQUE 或PRIMARY KEY 約束(它保證了語句只存取單行)的話,
Oracle經常實現唯一性掃描。
  使用唯一性約束的例子:
SQL> explain plan for
select empno,ename from emp where empno=10;
Query Plan
------------------------------------
SELECT STATEMENT [CHOOSE] Cost=1
TABLE ACCESS BY ROWID EMP [ANALYZED]
INDEX UNIQUE SCAN EMP_I1
  (2) 索引範圍掃描(index range scan)
  使用一個索引存取多行資料,同上面一樣,如果索引是組合索引,如(1)所示,
而且select ename from emp where ename = 'JACK' and deptno = 'DEV'語句返回多行資料,雖然該語句還是使用該組合索引進行查
詢,可此時的存取方法稱為索引範圍掃描。在唯一索引上使用索引範圍掃描的典型情況下是在謂詞(where限制條件)中使用了範圍操作
符(如>、<、<>、>=、<=、between)
  使用索引範圍掃描的例子:
SQL> explain plan for select empno,ename from emp
where empno > 7876 order by empno;
Query Plan
--------------------------------------------------------------------------------
SELECT STATEMENT [CHOOSE] Cost=1
TABLE ACCESS BY ROWID EMP [ANALYZED]
INDEX RANGE SCAN EMP_I1 [ANALYZED]
  在非唯一索引上,謂詞col = 5可能返回多行資料,所以在非唯一索引上都使用索引範圍掃描。
  使用index rang scan的3種情況:
  (a) 在唯一索引列上使用了range操作符(> < <> >= <= between)
  (b) 在組合索引上,只使用部分列進行查詢,導致查詢出多行
  (c) 對非唯一索引列上進行的任何查詢。
  (3) 索引全掃描(index full scan)
  與全表掃描對應,也有相應的全索引掃描。在某些情況下,可能進行全索引掃描而不是範圍掃描,需要注意的是全索引掃描只在
CBO模式下才有效。CBO根據統計數值得知進行全索引掃描比進行全表掃描更有效時,才進行全索引掃描,而且此時查詢出的資料都必
須從索引中可以直接得到。
  全索引掃描的例子:
An Index full scan will not perform single block i/o s and so it may prove to be inefficient.
e.g.
Index BE_IX is a concatenated index on big_emp (empno, ename)
SQL> explain plan for select empno, ename from big_emp order by empno,ename;
Query Plan
--------------------------------------------------------------------------------
SELECT STATEMENT [CHOOSE] Cost=26
INDEX FULL SCAN BE_IX [ANALYZED]
  (4) 索引快速掃描(index fast full scan)
  掃描索引中的所有的資料塊,與 index full scan很類似,但是一個顯著的區別就是它不對查詢出的資料進行排序,即資料不是
以排序順序被返回。在這種存取方法中,可以使用多塊讀功能,也可以使用並行讀入,以便獲得最大吞吐量與縮短執行時間。
  索引快速掃描的例子:
  BE_IX索引是一個多列索引:big_emp (empno,ename)
SQL> explain plan for select empno,ename from big_emp;
Query Plan
------------------------------------------
SELECT STATEMENT [CHOOSE] Cost=1
INDEX FAST FULL SCAN BE_IX [ANALYZED]
  只選擇多列索引的第2列:
SQL> explain plan for select ename from big_emp;
Query Plan
------------------------------------------
SELECT STATEMENT [CHOOSE] Cost=1
INDEX FAST FULL SCAN BE_IX [ANALYZED]
表之間的連線
  Join是一種試圖將兩個表結合在一起的謂詞,一次只能連線2個表,表連線也可以被稱為表關聯。在後面的敘述中,我們將會使用”row
source”來代替”表”,因為使用row source更嚴謹一些,並且將參與連線的2個row source分別稱為row source1和row source 2。Join過程
的各個步驟經常是序列操作,即使相關的row source可以被並行訪問,即可以並行的讀取做join連線的兩個row source的資料,但是在將表中
符合限制條件的資料讀入到記憶體形成row source後,join的其它步驟一般是序列的。有多種方法可以將2個表連線起來,當然每種方法都有自己
的優缺點,每種連線型別只有在特定的條件下才會發揮出其最大優勢。
  row source(表)之間的連線順序對於查詢的效率有非常大的影響。通過首先存取特定的表,即將該表作為驅動表,這樣可以先應用某些限
制條件,從而得到一個較小的row source,使連線的效率較高,這也就是我們常說的要先執行限制條件的原因。一般是在將表讀入記憶體時,應
用where子句中對該表的限制條件。
  根據2個row source的連線條件的中操作符的不同,可以將連線分為等值連線(如WHERE A.COL3 = B.COL4)、非等值連線(WHERE A.COL3 >
B.COL4)、外連線(WHERE A.COL3 = B.COL4(+))。上面的各個連線的連線原理都基本一樣,所以為了簡單期間,下面以等值連線為例進行介紹。
在後面的介紹中,都已:
  SELECT A.COL1, B.COL2
  FROM A, B
  WHERE A.COL3 = B.COL4;
  為例進行說明,假設A表為Row Soruce1,則其對應的連線操作關聯列為COL 3;B表為Row Soruce2,則其對應的連線操作關聯列為COL 4;
  連線型別:
  目前為止,無論連線操作符如何,典型的連線型別共有3種:
  排序 - - 合併連線(Sort Merge Join (SMJ) )
  巢狀迴圈(Nested Loops (NL) )
  雜湊連線(Hash Join)
  排序 - - 合併連線(Sort Merge Join, SMJ)
  內部連線過程:
  3) 最後兩邊已排序的行被放在一起執行合併操作,即將2個row source按照連線條件連線起來
  下面是連線步驟的圖形表示:
             MERGE
            /    \
           SORT     SORT
           |      |
       Row Source 1    Row Source 2
  如果row source已經在連線關聯列上被排序,則該連線操作就不需要再進行sort操作,這樣可以大大提高這種連線操作的連線速度,因為
經在前面的步驟中被排序了。儘管合併兩個row source的過程是序列的,但是可以並行訪問這兩個row source(如並行讀入資料,並行排序).
  SMJ連線的例子:
SQL> explain plan for
select /*+ ordered */ e.deptno, d.deptno
from emp e, dept d
where e.deptno = d.deptno
order by e.deptno, d.deptno;
Query Plan
-------------------------------------
SELECT STATEMENT [CHOOSE] Cost=17
MERGE JOIN
SORT JOIN
TABLE ACCESS FULL EMP [ANALYZED]
SORT JOIN
TABLE ACCESS FULL DEPT [ANALYZED]
  排序是一個費時、費資源的操作,特別對於大表。基於這個原因,SMJ經常不是一個特別有效的連線方法,但是如果2個row source都已經
預先排序,則這種連線方法的效率也是蠻高的。
  巢狀迴圈(Nested Loops, NL)
  這個連線方法有驅動表(外部表)的概念。其實,該連線過程就是一個2層巢狀迴圈,所以外層迴圈的次數越少越好,這也就是我們為什麼將
小表或返回較小row source的表作為驅動表(用於外層迴圈)的理論依據。但是這個理論只是一般指導原則,因為遵循這個理論並不能總保證使
語句產生的I/O次數最少。有時不遵守這個理論依據,反而會獲得更好的效率。如果使用這種方法,決定使用哪個表作為驅動表很重要。有時如
果驅動表選擇不正確,將會導致語句的效能很差、很差。
   內部連線過程:
  Row source1的Row 1 -------------- -- Probe -> Row source 2
  Row source1的Row 2 -------------- -- Probe -> Row source 2
  Row source1的Row 3 -------------- -- Probe -> Row source 2
  …….
  Row source1的Row n -------------- -- Probe -> Row source 2
  從內部連線過程來看,需要用row source1中的每一行,去匹配row source2中的所有行,所以此時保持row source1儘可能的小與高效的訪
問row source2(一般通過索引實現)是影響這個連線效率的關鍵問題。這只是理論指導原則,目的是使整個連線操作產生最少的物理I/O次數,
而且如果遵守這個原則,一般也會使總的物理I/O數最少。但是如果不遵從這個指導原則,反而能用更少的物理I/O實現連線操作,那儘管違反
指導原則吧!因為最少的物理I/O次數才是我們應該遵從的真正的指導原。
  在上面的連線過程中,我們稱Row source1為驅動表或外部表。Row Source2被稱為被探查表或內部表。
  在NESTED LOOPS連線中,Oracle讀取row source1中的每一行,然後在row sourc2中檢查是否有匹配的行,所有被匹配的行都被放到結果集
中,然後處理row source1中的下一行。這個過程一直繼續,直到row source1中的所有行都被處理。這是從連線操作中可以得到第一個匹配行
的最快的方法之一,這種型別的連線可以用在需要快速響應的語句中,以響應速度為主要目標。
  如果driving row source(外部表)比較小,並且在inner row source(內部表)上有唯一索引,或有高選擇性非唯一索引時,使用這種方法
可以得到較好的效率。NESTED LOOPS有其它連線方法沒有的的一個優點是:可以先返回已經連線的行,而不必等待所有的連線操作處理完才返
回資料,這可以實現快速的響應時間。
  如果不使用並行操作,最好的驅動表是那些應用了where 限制條件後,可以返回較少行資料的的表,所以大表也可能稱為驅動表,關鍵看
限制條件。對於並行查詢,我們經常選擇大表作為驅動表,因為大表可以充分利用並行功能。當然,有時對查詢使用並行操作並不一定會比查
詢不使用並行操作效率高,因為最後可能每個表只有很少的行符合限制條件,而且還要看你的硬體配置是否可以支援並行(如是否有多個CPU,
多個硬碟控制器),所以要具體問題具體對待。
  NL連線的例子:
SQL> explain plan for
select a.dname,b.sql
from dept a,emp b
where a.deptno = b.deptno;
Query Plan
-------------------------
SELECT STATEMENT [CHOOSE] Cost=5
NESTED LOOPS
TABLE ACCESS FULL DEPT [ANALYZED]
TABLE ACCESS FULL EMP [ANALYZED]
  雜湊連線(Hash Join, HJ)
  這種連線是在oracle 7.3以後引入的,從理論上來說比NL與SMJ更高效,而且只用在CBO優化器中。較小的row source被用來構建hash
table與bitmap,第2個row source被用來被hansed,並與第一個row source生成的hash table進行匹配,以便進行進一步的連線。Bitmap被用
來作為一種比較快的查詢方法,來檢查在hash table中是否有匹配的行。特別的,當hash table比較大而不能全部容納在記憶體中時,這種查詢
方法更為有用。這種連線方法也有NL連線中所謂的驅動表的概念,被構建為hash table與bitmap的表為驅動表,當被構建的hash table與
bitmap能被容納在記憶體中時,這種連線方式的效率極高。
  HASH連線的例子:
SQL> explain plan for
select /*+ use_hash(emp) */ empno
from emp, dept
where emp.deptno = dept.deptno;
Query Plan
----------------------------
SELECT STATEMENT [CHOOSE] Cost=3
HASH JOIN
TABLE ACCESS FULL DEPT
TABLE ACCESS FULL EMP
  要使雜湊連線有效,需要設定HASH_JOIN_ENABLED=TRUE,預設情況下該引數為TRUE,另外,不要忘了還要設定hash_area_size引數,以使
雜湊連線高效執行,因為雜湊連線會在該引數指定大小的記憶體中執行,過小的引數會使雜湊連線的效能比其他連線方式還要低。
  總結一下,在哪種情況下用哪種連線方法比較好:
  排序 - - 合併連線(Sort Merge Join, SMJ):
  a) 對於非等值連線,這種連線方式的效率是比較高的。
  b) 如果在關聯的列上都有索引,效果更好。
  c) 對於將2個較大的row source做連線,該連線方法比NL連線要好一些。
  d) 但是如果sort merge返回的row source過大,則又會導致使用過多的rowid在表中查詢資料時,資料庫效能下降,因為過多的I/O。
  巢狀迴圈(Nested Loops, NL):
  a) 如果driving row source(外部表)比較小,並且在inner row source(內部表)上有唯一索引,或有高選擇性非唯一索引時,使用這種方
法可以得到較好的效率。
  b) NESTED LOOPS有其它連線方法沒有的的一個優點是:可以先返回已經連線的行,而不必等待所有的連線操作處理完才返回資料,這可以
實現快速的響應時間。
  雜湊連線(Hash Join, HJ):
  a) 這種方法是在oracle7後來引入的,使用了比較先進的連線理論,一般來說,其效率應該好於其它2種連線,但是這種連線只能用在CBO
優化器中,而且需要設定合適的hash_area_size引數,才能取得較好的效能。
  b) 在2個較大的row source之間連線時會取得相對較好的效率,在一個row source較小時則能取得更好的效率。
  c) 只能用於等值連線中
  笛卡兒乘積(Cartesian Product)
  當兩個row source做連線,但是它們之間沒有關聯條件時,就會在兩個row source中做笛卡兒乘積,這通常由編寫程式碼疏漏造成(即程式設計師
忘了寫關聯條件)。笛卡爾乘積是一個表的每一行依次與另一個表中的所有行匹配。在特殊情況下我們可以使用笛卡兒乘積,如在星形連線中,
除此之外,我們要儘量使用笛卡兒乘積,否則,自己想結果是什麼吧!
  注意在下面的語句中,在2個表之間沒有連線。
SQL> explain plan for
select emp.deptno,dept,deptno
from emp,dept
Query Plan
------------------------------
SLECT STATEMENT [CHOOSE] Cost=5
MERGE JOIN CARTESIAN
TABLE ACCESS FULL DEPT
SORT JOIN
TABLE ACCESS FULL EMP
  CARTESIAN關鍵字指出了在2個表之間做笛卡爾乘積。假如表emp有n行,dept表有m行,笛卡爾乘積的結果就是得到n * m行結果。
使用全套的hints:
  當使用hints時,在某些情況下,為了確保讓優化器產生最優的執行計劃,我們可能指定全套的hints。例如,如果有一個複雜的查詢,
包含多個表連線,如果你只為某個表指定了INDEX提示(指示存取路徑在該表上使用索引),優化器需要來決定其它應該使用的訪問路徑和相
應的連線方法。因此,即使你給出了一個INDEX提示,優化器可能覺得沒有必要使用該提示。這是由於我們讓優化器選擇了其它連線方法和
存取路徑,而基於這些連線方法和存取路徑,優化器認為使用者給出的INDEX提示無用。為了防止這種情況,我們要使用全套的hints,如不
但指定要使用的索引,而且也指定連線的方法與連線的順序等。
  
使用全套hints的例子,ORDERED提示指出了連線的順序,而且為不同的表指定了連線方法:
SELECT /*+ ORDERED INDEX (b, jl_br_balances_n1) USE_NL (j b)
USE_NL (glcc glf) USE_MERGE (gp gsb) */
b.personnel_id, p.vendor_id Personnel,
FROM jl_br_journals j, jl_br_balances b,
gl_code_combinations glcc, fnd_flex_values_vl glf,
gl_periods gp, gl_sets_of_books gsb, po_vendors p
WHERE ...
  指示優化器的方法與目標的hints:
ALL_ROWS -- 基於代價的優化器,以吞吐量為目標
FIRST_ROWS(n) -- 基於代價的優化器,以響應時間為目標
CHOOSE -- 根據是否有統計資訊,選擇不同的優化器
RULE -- 使用基於規則的優化器
  例子:
SELECT /*+ FIRST_ROWS(10) */ employee_id, last_name, salary, job_id
FROM employees
WHERE department_id = 20;
SELECT /*+ CHOOSE */ employee_id, last_name, salary, job_id
FROM employees
WHERE employee_id = 7566;
SELECT /*+ RULE */ employee_id, last_name, salary, job_id
FROM employees
WHERE employee_id = 7566;
  指示儲存路徑的hints:
FULL   /*+ FULL ( table ) */
     指定該表使用全表掃描
ROWID  /*+ ROWID ( table ) */
     指定對該表使用rowid存取方法,該提示用的較少
INDEX  /*+ INDEX ( table [index]) */
     使用該表上指定的索引對錶進行索引掃描
INDEX_FFS /*+ INDEX_FFS ( table [index]) */
     使用快速全表掃描
NO_INDEX /*+ NO_INDEX ( table [index]) */
     不使用該表上指定的索引進行存取,仍然可以使用其它的索引進行索引掃描
SELECT /*+ FULL(e) */ employee_id, last_name
FROM employees e
WHERE last_name LIKE :b1;
SELECT /*+ROWID(employees)*/ *
FROM employees
WHERE rowid > 'AAAAtkAABAAAFNTAAA' AND employee_id = 155;
SELECT /*+ INDEX(A sex_index) use sex_index because there are few
male patients */ A.name, A.height, A.weight
FROM patients A
SELECT /*+NO_INDEX(employees emp_empid)*/ employee_id
FROM employees
WHERE employee_id > 200;
  指示連線順序的hints:
ORDERED  /*+ ORDERED */
     按from 字句中表的順序從左到右的連線
STAR   /*+ STAR */
     指示優化器使用星型查詢
SELECT /*+ORDERED */ o.order_id, c.customer_id, l.unit_price * l.quantity
FROM customers c, order_items l, orders o
WHERE c.cust_last_name = :b1
AND o.customer_id = c.customer_id
AND o.order_id = l.order_id;
/*+ ORDERED USE_NL(FACTS) INDEX(facts fact_concat) */
指示連線型別的hints:
USE_NL  /*+ USE_NL ( table [,table, ...] ) */
     使用巢狀連線
USE_MERGE /*+ USE_MERGE ( table [,table, ...]) */
     使用排序- -合併連線
USE_HASH /*+ USE_HASH ( table [,table, ...]) */
     使用HASH連線
注意:如果表有alias(別名),則上面的table指的是表的別名,而不是真實的表名
oracle最重要的9個動態效能檢視!
v$session + v$session_wait
v$process
v$sql
v$sqltext
v$bh (更寧願是x$bh)
v$lock
v$latch_children
v$sysstat
v$system_event
按組分的幾組重要的效能檢視
1。System 的 over view
v$sysstat , v$system_event , v$parameter
2。某個session 的當前情況
v$process , v$session , v$session_wait ,v$session_event , v$sesstat
3。SQL 的情況
v$sql , v$sqlarea , v$SQL_PLAN , V$SQL_PLAN_STATISTICS, v$sqltext_with_newlines
3. Latch / lock /ENQUEUE
v$latch , v$latch_children , v$latch_holder , v$lock ,V$ENQUEUE_STAT ,V$ENQUEUE_LOCK
4. IO 方面的
v$segstat , v$filestat , v$tempstat ,v$datafile , v$tempfile
5.shared pool / Library cache
v$Librarycache , v$rowcache , x$ksmsp
6.幾個advice也不錯
v$db_cache_advice , v$PGA_TARGET_ADVICE, v$SHARED_POOL_ADVICE

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/196700/viewspace-2135262/,如需轉載,請註明出處,否則將追究法律責任。

相關文章