1. 程式人生 > >oracle資料庫優化總結

oracle資料庫優化總結

1.      資料庫優化基本知識

I/O

資料庫的基本作用就是實現對資料的管理與查詢。隨之而來的就是大量的IO操作, 在海量資料的情況下,資料庫的效能問題有80%以上和IO有關。優化ORACLE資料庫的I/O效能一般有兩個方面,一是減少處理時間,二是減少等待事件。

資料塊

oracle每次執行i/o時候就是以oracle塊為單位。資料庫的邏輯結構包括:資料塊,區,段,表空間,資料塊是資料庫儲存基礎,是資料庫的最小邏輯單元。預設oracle塊大小是8k。oracle塊是處理update、insert、select資料事務的最小單位,當用戶從表中選擇資料時,將在oracle塊上讀取資料。

資料塊結構

1、基本組成

塊頭:存放一些基本資訊,如物理位置,塊所屬的段型別(資料段、索引段、回滾段等)

表目錄:如果塊中儲存的資料為表資料,則表目錄中儲存這個表的相關資訊

行目錄:如果塊中儲存的資料為表資料,則行目錄中儲存資料行的相關資訊。

行記錄:真正存放資料的區域,這部分空間已被使用。

自由空間:未使用的區域,用於新行的插入或者已經存在行的更新。

2、自由空間(free space) (主要用於insert、update時使用)

Insert、update的時候塊的自由空間也會減少。當使用DELETE語句刪除塊中的記錄或者使用UPDATE語句把列的值更改成一個更小值的時候,Oracle會釋放出一部分自由空間。當insert或update的值超出了自由空間的大小則會發生行遷移或者行連線。

行連結

第一次插入行時,由於行太長而不能容納在一個數據塊中時,就會發生行連結。在這種情況下,oracle會先把自由空間使用與該塊連結的一塊或多塊資料塊來容納該行的資料。行連線經常在插入比較大的行時才會發生,如包含long, long row, lob等型別的資料。在這些情況下行連結是不可避免的。導致了在一次讀取過程中要讀取多個數據塊,引起I/O效能下降。

行遷移

update後的行長度大於修改前的行長度,並且該資料塊中的自由空間已經比較小而不能完全容納該行的資料時,就會發生行遷移。在這種情況下,Oracle會將整行的資料遷移到一個新的資料塊上,而將該行原先的空間只放一個指標,指向該行的新的位置,注意,即使發生了行遷移,發生了行遷移的行的rowid 還是不會變化,這也是行遷移會引起資料庫I/O效能降低的原因。

analyze分析發生行遷移、行連線的行。

3、自由空間的管理

對於塊中的自由空間,Oracle提供兩種管理方式:自動管理,手動管理。

1)Oracle使用點陣圖(bitmap)來管理和跟蹤資料塊,這種塊的空間管理方式叫“自動管理”。自動管理有下面的好處:

◆易於使用

◆更好地利用空間

◆可以對空間進行實時調整

2)塊中自由空間的手動管理(手動管理比較複雜)

使用者可以通過PCTFREE, PCTUSED來調整塊中空間的使用,這種管理方式叫手動管理。一般調PCTFREE,相對於自動管理,手動管理方式比較麻煩,不容易掌握,容易造成塊中空間的浪費。

PCTFREE引數用於指定塊中必須保留的最小空閒空間百分例。之所以要預留這樣的空間,是因為UPDATE時,需要這些空間。如果UPDATE時,沒有空餘空間,Oracle就會分配一個新的塊,這會產生行遷移(Row Migrating)。

Select table_name,pct_free fromuser_tables

PCTUSED也是用於設定一個百分比,當塊中已使用的空間的比例小於這個百分比的時候,這個塊才被標識為有效狀態。只有有效的塊才被允許插入資料。

sql 的解析過程

1)    運用HASH演算法,得到一個HASH值,這個值可以通過V$SQLAREA.HASH_VALUE 檢視

2)    到shared pool 中的 library cache 中查詢是否有相同的HASH值,如果存在,則無需硬解析,進行軟解析

3)    如果shared pool不存在此HASH值,則進行語法檢查,檢視是否有語法錯誤

4)    如果沒有語法錯誤,就進行語義檢查,檢查該SQL引用的物件是否存在,該使用者是否具有訪問該物件的許可權

5)    如果沒有語義錯誤,對該SQL進行解析,生成解析樹,執行計劃

6)    生成ORACLE能執行的二進位制程式碼,執行該程式碼並且返回結果給使用者

硬解析和軟解析都在第5步進行

硬解析通常是昂貴的操作,大約佔整個SQL解析過程的70%左右的時間,硬解析會生成執行樹,執行計劃,等等。

當再次執行同一條SQL語句的時候,由於發現library cache中有相同的HASH值,這個時候不會硬解析,而會軟解析, 其實軟解析就是跳過了生成解析樹,生成執行計劃這個耗時又耗CPU的操作,直接利用生成的執行計劃執行該SQL語句。

執行以下4個sql,觀察生成硬解析與執行計劃的情況

Sql1:select *from emp where empno=7788;

Sql2:select *from Emp where empno=7788;

Sql3:select *from emp  where empno=7788;

Sql4:select *from emp where empno=7788;

執行結果如下,可以看到4句sql由於1與4完全相同,則發生了一次硬解析一次軟解析,執行次數為2並且使用了同一個執行計劃。2與3由於一點細微的改變則各自硬解析了一次並且各自重新生成了一個執行計劃,即便執行計劃是相同的。

SQL_TEXT

SQL_ID

PARSE_CALLS

HASH_VALUE

EXECUTIONS

PLAN_ADDRESS

PLAN_HASH_VALUE

select * from Emp where empno=7788

gmjqmqnsyufus

1

837630808

1

31EDE2C8

2113372157

select * from emp where empno=7788

2cv6qqj01b9wu

2

1075160986

2

31EEE1AC

2113372157

select * from emp  where empno=7788

3kpy13pt6qy2f

1

1919645774

1

31EDEA10

2113372157

總結

降低HWM:儘量用TRUNCATE代替DELETE、重構表

減少因記憶體不足導致的等待:減少union、distinct、減少orderby,這些佔記憶體

減少網路傳輸等待:減少dblink,將要訪問的遠端的表接過來一次,以後直接訪問這個表

2.      執行計劃(資料庫訪問資料的路徑)

執行計劃是一個很複雜的課題,這裡根據本輪優化來簡單介紹一下如何使用執行計劃進行優化。

執行計劃簡單的講就是資料庫如何訪問資料的路徑,從資料庫訪問到一條資料的方法有多種,執行計劃會從眾多方案中通過各種比較選出開銷最小(CBO模式)一個訪問路徑,它會因很多因素的改變受到影響。

oracle訪問資料的方法

1)  全表掃描(Full Table Scans,FTS)  (掃描表的所有塊)

為實現全表掃描,Oracle讀取表中所有的行,並檢查每一行是否滿足語句的WHERE限制條件一個多塊讀操作可以使一次I/O能讀取多塊資料塊(db_block_multiblock_read_count引數設定),而不是隻讀取一個數據塊,這極大的減 少了I/O總次數,提高了系統的吞吐量,所以利用多塊讀的方法可以十分高效地實現全表掃描,而且只有在全表掃描的情況下才能使用多塊讀操作。在這種訪問模式下,每個資料塊只被讀一次。  使用FTS的前提條件:在較大的表上不建議使用全表掃描,除非取出資料的比較多,超過總量的5% —— 10%,或你想使用並行查詢功能時。

2)   通過ROWID的表存取(Table Access by ROWID或rowid lookup)

行的ROWID指出了該行所在的資料檔案、資料塊以及行在該塊中的位置,所以通過ROWID來存取資料可以快速定位到目標資料上,是Oracle存取單行資料的最快方法。這種存取方法不會用到多塊讀操作,一次I/O只能讀取一個數據塊。我們會經常在執行計劃中看到該存取方法,如通過索引查詢資料。

3)   索引掃描(Index Scan或index lookup)(索引也是放在資料塊上的,執行索引時先從資料塊上找到索引,再根據索引找到資料所在的資料塊,比 ROWID多了一步

我們先通過index查詢到資料對應的rowid值(對於非唯一索引可能返回多個rowid值),然後根據rowid直接從表中得到具體的資料,這 種查詢方式稱為索引掃描或索引查詢(index lookup)。一個rowid唯一的表示一行資料,該行對應的資料塊是通過一次i/o得到的,在此情況下該次i/o只會讀取一個數據庫塊。  在索引中,除了儲存每個索引的值外,索引還儲存具有此值的行對應的ROWID值。有4種類型的索引掃描

(1)索引唯一掃描(index unique scan)

(2)索引範圍掃描(index range scan):在非唯一索引上都使用索引範圍掃描。使用index rang scan的3種情況:

(a)在唯一索引列上使用了range操作符(><<>>=<= between)

(b)在組合索引上,只使用部分列進行查詢,導致查詢出多行

(c)對非唯一索引列上進行的任何查詢。  

(3)索引全掃描(index full scan)(count某一列時)

(4)索引快速掃描(index fast full scan)

每種訪問方式都有其特定使用場景,如果在一個場景下出現了不適合的訪問方式很有可能會引起效率的下降,這也是優化中主要的優化原則。

Oracle連線方法

連線發生在一對錶或資料行源之間,當在from子句中存在多張表時,優化器將決定哪種連線運算對於每一張表來說效率最高。

常見連線方法:巢狀迴圈連線、雜湊連線、排序-合併及笛卡爾,每種方法都有一定的適合條件。每個連線方式都有兩個分支,訪問的第一張表叫驅動表,訪問的第二張表叫被驅動表,通常優化器預估返回行最小的表通常作為驅動表。(連線示意圖)

巢狀迴圈用在一個表大一個表小的情況,雜湊迴圈用在等值連線的情況,merge join用在不等值連線。

nested loop(巢狀迴圈):(關聯條件的列要有索引,驅動表的資料遠小於被驅動表)

存在著兩個迴圈,一個是外部迴圈,提取驅動表中符合條件的每條記錄。另外一個是內部迴圈,根據外迴圈中提取的每條記錄對內部表進行連線查詢相應的記錄。由於這兩個迴圈是巢狀進行的,故此種連線方法稱為巢狀迴圈連線。

特點:

1.一個大表和一個小表(驅動表)連線,連線方式可以是等值或者是不等值

2.驅動表資料較小或者內部表已連線的列有唯一性索引或者高度可選的非唯一性索引,效率很高

3.能快速讀取結果集中第一批記錄而不必等待整個結果集完全確定下來

巢狀迴圈連線返回前幾行的記錄是非常快的,這是因為使用了巢狀迴圈後,不需要等到全部迴圈結束再返回結果集,而是不斷地將查詢出來的結果集返回。在這種情況下,終端使用者將會快速地得到返回的首批記錄,且同時等待Oracle內部處理其他記錄並返回。如果查詢的驅動表的記錄數非常多,或者被驅動表的連線列上無索引或索引不是高度可選的情況,巢狀迴圈連線的效率是非常低的。

hash join(雜湊連線)(只適合出現在等值連線的情況下):

雜湊連線分為兩個階段,如下。

1、 構建階段:優化器首先選擇一張相對較小的表做為驅動表,運用雜湊函式對連線列進行計算產生一張雜湊表。通常這個步驟是在記憶體(hash_area_size)裡面進行的,因此運算很快。

2、 探測階段:優化器對被驅動表的連線列運用同樣的雜湊函式計算得到的結果與前面形成的雜湊表進行探測返回符合條件的記錄。這個階段中如果被驅動表的連線列的值沒有與驅動表連線列的值相等的話,那麼這些記錄將會被丟棄而不進行探測

特點:

1.一般兩張相同大小的表連線,初始引數hash_join_enable=true

2.只能是等價連線,只能是CBO模式

3.只有一張源表需要排序,可能比merge join更快,因為只需要對一張源表排序;

也可能比nested loop更快,因為處理記憶體中的hash表比處理b-tree索引更快

4.可能會使用到臨時表空間,所以最好pag_aggregate_target設定的比較大

雜湊連線比較適用於返回大資料量結果集的連線。

merge join(排序-合併):兩個互相連線的表按連線列的值先排序,排序完後形成的結果集再互相進行合併連線提取符合條件的記錄(用在>、>=、<=等情況下的連線)

特點:

1.首先對2張表的連線列進行排序後再連線

2.當缺乏資料選擇性或者有效索引時,或者2個表都比較龐大,可能比nested loop更有效

3.只能是等值連線,可能使用temp表空間

排序合併連線比較適用於返回大資料量的結果。 

排序合併連線在資料表預先排序好的情況下效率是非常高的,也比較適用於非等值連線的情況,比如>、>=、<=等情況下的連線(雜湊連線只適用於等值連線)

笛卡爾積

笛卡爾積連線發生在當一張表的所有行與另一張表的所有行連線的時候,因此這種連線的結果集等於兩個表的資料行數相乘。在實際應用中不使用或者避免出現這種連線。

執行計劃中每個欄位的意義

operation:執行的操作型別,如Table access或sort。

options:操作的附加資訊。例如,以TABLE SCAN為例,選項可能是full或by rowid。

object_node:如果是分散式查詢,這一列表示用於引用物件的資料庫連結。對於並行查詢,它的值可能對應一個臨時的結果集

object_name:物件名稱。

object_type:物件的型別(表,索引等)

cost:優化器估算出來的此操作的相對成本。

cardinality:優化器預期這一步將返回的記錄數。

bytes:預計這一步將返回的位元組數。

partition_start:如果要執行分割槽消除(partitionelimination),這一列表明要訪問的分割槽範圍的起始分割槽。它也可能包含關鍵字key或者row location,表明要訪問的分割槽將在執行時確定。

partition_end:表明將要訪問的分割槽範圍的結束分割槽。

cpu_cost:估算出來的操作的cpu成本。

io_cost:估算出來的操作的io成本。

temp_space:估算出來的這一步操作所使用的臨時儲存的空間大小(如用來排序的記憶體或磁碟空間)。

access_predicates【訪問條件】:sql語句中,確定如何在當前步驟中提取記錄的子句。它可以包含提供給索引檢索或表連線的子句。在這塊把資料給過濾掉,一般會用到索引

filter_predicates【過濾條件】:sql語句中確定如何對記錄進行過濾的子句,如where子句在非索引列上的條件。一般不用索引,都是全表掃描,可以作為優化重點關注的地方!

time:優化器為這一步執行估算的時間消耗。

根據執行計劃進行優化的一般步驟

將瓶頸sql塊單獨取出,檢視其執行計劃,

首先檢查其中使用了全表掃描的物件,判斷其是否合適。引發錯誤使用的情形通常是:

1)  沒有建立合適的索引列導致全表掃描

2)  非函式索引列使用了函式引發全表掃描

3)   物件統計資訊過舊或未收集導致全表掃描、

4)  對於點陣圖索引,直方圖資訊的缺失有時也會導致錯誤的全表掃描

然後檢查其中無用的表,確認無用的表可以直接去掉減少訪問步驟減小系統開銷。

接著檢查其中是否有重複訪問的表,檢視是否可以減少訪問次數一般可以通過使用with或者建立中間表來優化。

優化訪問路徑,資料庫優化器也有不那麼聰明的時候,有時候它生成的訪問路徑可能並不是最優的,可嘗試使用hint來改變訪問路徑進行優化

常用hint:

/*+full( )*/表明對錶選擇全域性掃描的方法.

/*+use_nl( )*/在多表關聯查詢中,指定使用nest loops方式進行多表關聯。

/*+use_hast( )*/在多表關聯查詢中,指定使用hash join方式進行多表關聯。

/*+index( )*/ 使用指定表的指定索引

/*+ append */ 以直接載入的方式將資料載入入庫

/*+leading( )*/在多表關聯查詢中,指定哪個表作為驅動表,即告訴優化器首先要訪問哪個表上的資料。

/*+ parallel() */ 在sql中指定執行的並行度,這個值將會覆蓋自身的並行度

看Description的基數(執行結果返回的資料的行數)和自己預估的基數是否大致一直,不一致則考慮其它影響效率的情況,如統計資訊比較舊等。

3.      統計資訊

10g的統計資訊自動收集策略

Oracle10g中統計資訊預設可以自動收集,由GATHER_STATS_JOB作業收集得到,只有當資料庫物件沒有統計資訊或者統計資訊已經過期(Oracle 10G中是否過期的標準是資料庫物件被修改的記錄行數超過10%,該資訊由Modification Monitoring來追蹤完成)時才對該物件進行資訊統計,該作業在資料庫建立或升級時由Scheduler自動建立,

這些作業可以從檢視DBA_SCHEDULER_JOBS中查到。

SELECT * FROM DBA_SCHEDULER_JOBS

通過以下包設定job的開啟與關閉

Begin

       Dbms_scheduler.enable(‘GATHER_STATS_JOB’);

END;

Begin

       Dbms_scheduler.disable(‘GATHER_STATS_JOB’);

END;

通過以下表檢視job執行日誌

Select * from dba_scheduler_job_run_detailswhere job_name=’ GATHER_STATS_JOB’

預設情況下,Scheduler在維護視窗(maintenance window,目前227上預設啟動時間為每晚上10:00至次日早上6:00及整個週六週日)開啟時執行GATHER_STATS_JOB作業,作業GATHER_STATS_JOB則是通過呼叫系統內部過程DBMS_STATS.GATHER_DATABASE_STATS_JOB_PROC來完成資訊統計的,該過程可根據資料庫物件統計資訊需求的優先順序(即資料庫物件被修改的多少)按先後順序來完成統計資訊收集任務。

可以通過以下表檢視視窗資訊

Select * from dbs_scheduler_windows

可以通過以下包修改視窗資訊(例如將SUNDAY_WINDOW的開始時間修改為早上4點)

begin

 dbms_scheduler.set_attribute

(

'SUNDAY_WINDOW','repeat_interval','freq=daily;byday=SUN;byhour=4;byminute=0; bysecond=0'

);

end;

Select * from dbs_scheduler_windows–檢視是否修改成功

GATHER_STATS_JOB作業是否隨維護視窗的關閉而關閉則由屬性stop_on_window_close決定,stop_on_window_close的預設值為TRUE,此時GATHER_STATS_JOB作業隨維護視窗的關閉而關閉。

可以通過以下匿名塊檢視stop_on_window_close屬性

declarevalueboolean;
begindbms_scheduler.get_attribute('gather_stats_job','stop_on_window_close',value);
dbms_output.PUT_LINE('Check Result: '||casevaluewhentruethen'True'else'False'end);
end;

可以通過以下包修改屬性

execdbms_scheduler.set_attribute(' gather_stats_job

','stop_on_window_close',TRUE);

非預設情況時,Oracle10g可通過設定初始化引數 STATISTIC_LEVEL(STATISTICS_LEVEL = {ALL | TYPICAL | BASIC}),來控制是否啟用統計資訊自動收集功能

當其為預設值TYPICAL時,系統將自動收集所有主要的有關自身管理的資訊以使系統提供最優效能,該值適合於絕大多數情況;

當取值ALL時,相對TYPICAL值系統增加timedOS statistics和plan execution statistics兩項資訊統計;

當取值 BASIC時:有關係統特性和功能的許多資訊統計功能都將被關閉

檢視當前值

Show parameter statistics_level

修改語法

ALTER SESSION SET statistics_level=all–修改當前會話

ALTER SYSTEM SET statistics_level=all–修改整個系統

10g的統計資訊手動收集方法

除系統自動收集統計資訊外,還可以通過手動呼叫包來收集統計資訊

常用的包如下

dbms_stats.gather_table_stats();--收集指定表的統計資訊

常用引數

Tabname–表名稱

Partname—分割槽名稱

estimate_percent—統計的樣品比例,預設oracle自動選擇

method_opt–統計方式,預設FOR ALL COLUMNS SIZE AUTO.

degree –並行度

cascade—是否級聯收集索引資訊,預設是不收集的

dbms_stats.gather_index_stats();--收集指定索引的統計資訊

另外還有一個收集統計資訊的命令analyze

analyzetable t1 computestatistics–收集表統計資訊

以上包或命令常用在對單個物件收集資訊,當物件較多或者需要對某個使用者下的某類物件進行收集資訊就要用到下面的包

dbms_stats.gather_schema_stats();--收集指定使用者的統計資訊

常用引數

Ownname–使用者名稱稱

estimate_percent—抽樣比例,預設oracle自動選擇

method_opt--統計方式,預設FOR ALL COLUMNSSIZE AUTO.

Degree–並行度

Granularity—收集統計資訊的級別預設auto

--    'AUTO' – 由過程自動決定收集的級別

--    'GLOBAL AND PARTITION' – 收集全域性以及分割槽資訊

--    'SUBPARTITION' – 收集子分割槽資訊

--    'PARTITION' – 收集分割槽資訊

--     'GLOBAL' – 收集全域性資訊

--     'ALL' - 收集全域性、分割槽、子分割槽資訊     

Cascade—是否收集索引資訊,預設由oracle決定

options – 指定收集的物件

--      'GATHER' – 收集使用者的所有物件資訊

--      'GATHER AUTO' – 由oracle決定收集哪些物件

--      'GATHER STALE' – 收集檢視user_tab_modifications中的物件資訊與系統自動收集的策略一致

--      'GATHER EMPTY' – 收集當前統計資訊為空的物件資訊

在自動收集策略中也提到了一個收集統計資訊的包

DBMS_STATS.GATHER_DATABASE_STATS_JOB_PRO()

這個包的統計原理是統計資料庫中統計資訊過久或者缺失或者物件在統計時間段內資料量變化大於10%(可以累積)

監控物件修改資料量的檢視

select*fromuser_tab_modifications

物件中的資料發生變化後並不會立即進入到user_tab_modifications,可以利用以下包手工重新整理

begin

 Dbms_Stats.flush_database_monitoring_info();

end;

手動呼叫收集統計資訊

begin

 dbms_stats.gather_database_stats_job_proc();--通過實驗發現對於分割槽表,只收集分割槽表的資訊,全表的不收集(需要通過修改時間觸發自動收集job才會收集全表的資訊)

 end;

當分割槽表修改資料量達到10%但沒到全表的10%,則只收集分割槽表的資訊,不收集全表的資訊,當達到全表的10%則會收集全表的資訊。

修改量可以累積當累積到10%後也會觸發收集。

dbms_stats.gather_schema_stats()中的引數‘GATHER STALE'也是利用通過監控這個檢視中的資料來決定收集的物件。

在收集統計資訊時可能出現原來的優化方法在收集統計資訊之前一直工作良好,但是在此之後,由於新收集的統計資訊產生了不良計劃,導致查詢突然出錯或效率降低。為避免這種情況,統計資訊的收集作業在收集新資訊之前儲存當前的統計資訊。如果出現問題,則可以返回到原有的統計資訊,或者通過歷史統計檢查二者之間的不同之處,以解決問題。

查詢系統儲存統計資訊時長

select DBMS_STATS.GET_STATS_HISTORY_RETENTIONfrom dual;

查詢最早可用的統計資訊時間

selectDBMS_STATS.GET_STATS_HISTORY_AVAILABILITY from dual;

還原統計使用者統計資訊

begin

   dbms_stats.restore_schema_stats

(user,'09-6月 -16 10.26.32.927000000 下午 +08:00');

end;

在利用系統自動儲存的資訊進行還原的同時也可以利用命令手動匯入匯出使用者統計資訊

--建立收集統計資訊的表stattab

begin

 dbms_stats.create_stat_table('scott','stattab','users');

end;

--將使用者統計資訊匯出到統計資訊表stattab

begin

dbms_stats.export_schema_stats

(

ownname=>'scott',stattab=>'stattab',statown=>'scott'

);

end;

--收集使用者的統計資訊

begin

dbms_stats.gather_schema_stats

(

'scott',method_opt=>'forall columns',degree=>4

);

end;

--將使用者統計資訊還原

begin

 dbms_stats.import_schema_stats

(ownname=>'scott',STATTAB=>'stattab',statown=>'scott');

end; 

SQL動態取樣

動態取樣是為謂詞和表/索引統計收集更加精確的資訊從而提高伺服器效能,資訊越精確產生的效能更好。

可以使用動態取樣的情況:

  1.) 在收集的統計不能使用或會導致嚴重的估計錯誤時估計單表的謂詞選擇性;

  2.) 估計沒有統計的表/索引的統計;

  3.) 估計統計過期的表和索引的統計;

動態取樣特徵由引數OPTIMIZER_DYNAMIC_SAMPLING控制,預設級別為2。

動態取樣是在解析的時候對錶進行取樣收集統計資訊,但不會寫入user_tables

動態取樣可以通過hint開啟無論OPTIMIZER_DYNAMIC_SAMPLING目前的值是多少

取樣級別範圍從1..10

1級:滿足以下條件則取樣所有沒被分析的表:

 (1)查詢中至少有一個未分析表;

 (2)這個未分析表被關聯另外一個表或者出現在子查詢或非merge檢視中;

 (3)這個未分析表有索引;

 (4)這個未分析表有多餘動態取樣預設的資料塊數(預設是32塊)。

2級:對所有未分析表進行動態取樣。取樣資料塊數量是預設數量的2倍。

3級:在2級基礎上加上那些使用了猜想選擇消除表,取樣資料塊數量等於預設數量。對於未分析表,取樣數量2倍於預設數量。

4級:在3級基礎上加上那些有單表謂詞關聯2個或多個列,取樣資料塊數量等於預設數量。對於未分析表,取樣數量2倍於預設數量。

5,6,7,8,9級在4級基礎上分別使用2,4,8,32,128倍於預設動態取樣資料塊數量。

10級:在9級基礎上對錶中所有資料塊進行取樣。

       新建一個t2表,不對其收集統計資訊,查詢其中一個數據

SQL> select /*+dynamic_sampling(t 0)*/ * from t2 where id=30101;

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

| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |

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

|   0 | SELECT STATEMENT  |     |   127 |  3302 |   44   (0)| 00:00:01 |

|*  1 | TABLE ACCESS FULL| T2   |   127 | 3302 |    44   (0)| 00:00:01 |

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

SQL> select/*+dynamic_sampling(t 3)*/ * from t2 t where id=30101;

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

| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |

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

|   0 | SELECT STATEMENT  |     |     1 |    26 |   45   (3)| 00:00:01 |

|*  1 | TABLE ACCESS FULL| T2   |     1 |   26 |    45   (3)| 00:00:01 |

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

Note

-----

   - dynamic sampling used for this statement (level=0)–這個值需單獨設定

通過在sql使用hint開啟動態取樣級別能夠發現開啟後訪問的行數明顯下降

4.      分割槽表

1.建立的分割槽表資訊清單(資料取樣時間201603)

表英文名

表中文名

總的資料量大小

總的資料量條數

每月的資料量大小

每月的資料量條數

f_ac_inter

內部戶事實表

9.14G

35099569

0.23G

896098

F_AC_JYMX_ALL

交易明細中間表

2.7G

11630307

0.06G

272207

f_ac_jymx_d

交易明細日表

3.6G

11563443

0.08G

240419

F_ac_ledger

日總賬

10.1G

27214046

0.24G

523420

T05_LOAN_PROVISION

1.4G

11094224

0.07G

592680

T09_SUB_ACCU_BALANCE_H

科目賬戶餘額表

2.28G

18127253

0.1G

904879

T05_DEPOSIT_PROVISION

1.7G

14865031

0.08g

801128

t03_inter_acun_dtl

內部戶賬務明細

6.09G

33099597

0.15G

896098

t05_core_acun_dtl

核心賬務明細

4.78G

24347280

0.14G

686731

T05_CORE_TRAD_DTL

核心交易明細

23G

27190137

0.6G

682783

2.建立分割槽表前的分析

從表格統計資訊,我們看到這些表都屬於大表;其次在分析後臺指令碼後發現,這些表都是前臺和後臺指令碼中用的比較頻繁的表,同時,這些表在指令碼查詢中大都使用的是當月資料,因此,

我們把這些表改為以work_dt為分割槽列的月分割槽表

3.分割槽表型別

在本次的專案優化中,我們所使用的分割槽表型別是範圍分割槽(range).

由於我們本次的生產資料庫版本是ORACLE10g,因此,我們列舉出了ORACLE10g的幾種分割槽型別如下:

分割槽表型別

簡述

RANGE(範圍)分割槽

例如查詢某個月的資料

LIST(列表)分割槽

例如查詢某個地區的資料

HASH(雜湊)分割槽

資料分配到每個分割槽的量是均衡的

組合分割槽

分為(range-list,range-hash)

4.分割槽表優點與缺點

優點

缺點

1.      分割槽消除:減少訪問路徑

2.             記錄清理:刪除記錄高效(truncate)

3.             分割槽轉移(exchange partition 分割槽名 with table tabname);將某分割槽資料與表互換

1.分割槽表的缺點:分割槽過多,會加大Oracle對段的管理。同時內部會產生大量的遞迴呼叫。一般資料記錄在100萬以下的不

建議建立分割槽表。

5.分割槽表的常見使用方式

a)     truncate分割槽資料

alter table range_part_tab truncatepartition p1  ;

b)      drop分割槽

alter table range_part_tab droppartition p_max;

c)     新增分割槽

alter table range_part_tab add partitionp2013_02 values less than(to_date('2013-03-01','yyyy-m

m-dd'));

d)     常用檢視

user_tab_partitions  (查詢一個分割槽表的各個分割槽名)

6.分割槽表的維護

       分割槽的增加:在10G中沒有自動增加分割槽的功能因此本輪優化中採取了手動維護的方式,通過指令碼在每月1號增加下個月的分割槽。

       分割槽統計資訊收集:分割槽表的統計資訊主要是通過資料庫自動收集統計資訊的策略實現的,但在日常執行過程中出現了每月2號(sysdate時間)引用了部分分割槽表的sql執行緩慢的情況,是因為新增的分割槽的統計資訊是空的,因此在分割槽插入資料後導致了優化器沒有準確的統計資訊可供使用出現執行計劃變差。目前採取的補救方案是每月3號早上在自動收集統計資訊完成後將分割槽的統計資訊備份一份,在下月2號之前恢復給新建分割槽上。使優化器有統計資訊可以使用避免了執行計劃變差。

       分割槽資料接入:採取分割槽表後可以對分割槽進行截斷操作即減少了執行時間也能避免之前delete而產生的高水位線。因此在對按月接入資料的分割槽表採取截斷操作後再接入資料。

5.      索引:一種資料庫結構,用來快速查詢資料,一般包括根節點,葉節點。葉子節點儲存索引條目,一般條目裡包含索引鍵值(單列索引是一個值多列索引是多個值組合)與rowid

B樹索引

目前資料庫中最常用的索引,構造類似於二叉樹,能根據鍵值提供一行或一個行集的快速訪問,其中的’B’代表平衡, 通常使用在頻繁使用查詢謂詞的列上,一般這類列的選擇度都較高。

使用場景

1、當我們希望從表中只返回少量的資料(佔比很小,這個比例通常經驗值是5%,不過根據表的不同也有不用,一個瘦表(通常只有幾列)可能在20%-30%,一個胖表(列很多或列很寬)可能在2%-3%)時會使用索引。如下例:

T1表插入從1到10w的數字只有一列,t3_2利用下例中的t3建表,建表sql

createtable t3_2asselectid,name,name||name||name||name c3,name||name||name||name||name c4 fromt3

分別在id列建立索引,收集統計資訊

以上兩個查詢中的數值分別是全表掃描與索引範圍查詢的臨界值

2、當我們想要查詢大量資料,但是隻要返回索引的列或者只通過索引列就能得到結果的話,索引也會起到作用

3、資料在磁碟上的物理組織也會對索引的使用有影響,如以下的例子,我們建立兩個實驗表t2/t3,向t2中順序的插入10w條資料,同時生成一組隨機資料。將t2按照隨機數排序插入到t3中,目的是打亂資料的物理儲存位置。

begin

for  i in1..100000loop

insertinto t2 values(i,rpad(dbms_random.random,75,'*'));

endloop;

commit;

end;

createtable t3 asselect*from t2 orderbyname

createindex idx_1on t2(id)

createindex idx_2 on t3(id)

begin

 dbms_stats.gather_table_stats

('scott','t2');

begin

 dbms_stats.gather_table_stats

('scott','t3');

end;

end;

在對錶進行收集統計資訊後,分別對t2/t3查詢相同範圍的資料

select*from t2 whereidbetween87and1000

select*from t3 whereidbetween87and1000

以上三個查詢的結果是相同的,但是相同的查詢資料庫的開銷與io上升的差異十分明顯。

原因是當向一個表中填充資料時如果按照行主鍵或者建立索引的列順序填充,序號相鄰的行儲存位置一般也會相鄰,當你發出一個範圍查詢的時候你想要的行通常也在同樣的塊上,即使你要查詢大量的行,通過索引範圍掃描的讀取的塊裡也許就包含了你想要的行。如下圖,相同數量的行數在未打亂順序的t2中分佈在11個數據塊,在順序被打亂的t3中分佈在631個塊。

如果行被分散的儲存在不同位置上,此時強制使用索引範圍掃描就會是個災難,使用全表掃描反而更好。

總結

1、通過索引訪問表中的資料佔比越少越有效

2、如果能使用索引列回答問題(只用到索引列不用訪問表)那麼返回資料佔比很大索引也是有效的

3、資料的物理組織有時未按照索引列或主鍵列有序的填充表,會影響索引的使用

4、空值會影響索引的使用,在有空值的列上通過與虛擬列建立組合索引,可以使優化器選擇索引。而且索引的大小並沒有明顯變化

createtable t5 asselect u.OBJECT_NAME,u.DATA_OBJECT_IDfrom user_objects u

createindex