Oracle优化08-并行执行
思維導圖
概述
在討論Oracle的性能問題時,通常要假設(shè)一個前提,那就是這個系統(tǒng)是OLTP還是OLAP(或者說數(shù)據(jù)倉庫系統(tǒng))。 只有在這個前提下,討論一些性能問題才有意義,因為這兩類系統(tǒng)太不一樣了,甚至很多技術(shù)是相悖的。
舉個例子 我們說綁定變量,這是一個在OLTP系統(tǒng)上有意義的話題,而對于OLAP系統(tǒng)卻完全沒有意義,設(shè)置不需要它。 再比如說內(nèi)存命中率,OLTP系統(tǒng)中這個指標非常重要,因為OLTP系統(tǒng)中內(nèi)存的效率決定了數(shù)據(jù)庫的效率;而OLAP系統(tǒng)中卻不太需要關(guān)注它。 在OLAP系統(tǒng),SQL語句的執(zhí)行效率決定了數(shù)據(jù)庫的效率,而OLTP系統(tǒng)中,SQL語句的執(zhí)行效率通常是很高的。
并行和OLAP系統(tǒng)
如果討論數(shù)據(jù)庫性能方面的問題,這個技術(shù)就不應該忽略,如果要把并行也像上面劃一個使用范圍的話,我認為應該是OLAP系統(tǒng)的一個重要的技術(shù)。
我們下看下并行執(zhí)行的處理模型舉例:
首先,Oracle 會創(chuàng)建一個進程用于協(xié)調(diào)并行服務進程之間的信息傳遞,這個協(xié)調(diào)進程將需要操作的數(shù)據(jù)集(比如表的數(shù)據(jù)塊)分割成很多部分,稱為并行處理單元,然后并行協(xié)調(diào)進程給每個并行進程分配一個數(shù)據(jù)單元。
比如有四個并行服務進程,他們就會同時處理各自分配的單元,當一個并行服務進程處理完畢后,協(xié)調(diào)進程就會給它們分配另外的單元,如此反復,直到表上的數(shù)據(jù)都處理完畢,最后協(xié)調(diào)進程負責將每個小的集合合并為一個大集合作為最終的執(zhí)行結(jié)果,返回給用戶。
并行處理的機制實際上就是把一個要掃描的數(shù)據(jù)集分成很多小數(shù)據(jù)集,Oracle 會啟動幾個并行服務進程同時處理這些小數(shù)據(jù)集,最后將這些結(jié)果匯總,作為最終的處理結(jié)果返回給用戶。
這種數(shù)據(jù)并行處理方式在OLAP系統(tǒng)中非常有用,OLAP系統(tǒng)的表通常來說都是非常大,如果系統(tǒng)的CPU比較多,讓所有的CPU共同來處理這些數(shù)據(jù),效果就會比串行執(zhí)行要高的多。
然而對于OLTP系統(tǒng),通常來講,并行并不合適,原因是OLTP系統(tǒng)上幾乎在所有的SQL操作中,數(shù)據(jù)訪問路勁基本上以索引訪問為主,并且返回結(jié)果集非常小,這樣的SQL 操作的處理速度一般非常快,不需要啟用并行。
話不多說,上案例:
注意 alter table t parallel 4;
Connected to Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 Connected as xxx@xgjSQL> create table t as select object_id ,object_name from dba_objects;Table createdSQL> create index ind_t on t(object_id);Index createdSQL> exec dbms_stats.gather_table_stats(user,'t',cascade => true);PL/SQL procedure successfully completedSQL> alter table t parallel 4;Table altered我們查看下下面SQL的執(zhí)行計劃
SQL> select * from t where t.object_id=10433; OBJECT_ID OBJECT_NAME ---------- -------------------------------------------------------------------------------- 10433 KU$_PFHTABPROP_VIEW SQL> select a.SQL_ID ,a.CHILD_NUMBER from v$sql a where a.SQL_TEXT like 'select * from t where t.object_id=10433%';SQL_ID CHILD_NUMBER ------------- ------------ 1afhswsarn20q 0 SQL> select * from table(dbms_xplan.display_cursor('1afhswsarn20q',0));PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- SQL_ID 1afhswsarn20q, child number 0 ------------------------------------- select * from t where t.object_id=10433 Plan hash value: 4013845416 -------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time -------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 2 (100)| | 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 24 | 2 (0)| 00:00 |* 2 | INDEX RANGE SCAN | IND_T | 1 | | 1 (0)| 00:00 -------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("T"."OBJECT_ID"=10433)19 rows selectedSQL>上面的SQL是OLTP系統(tǒng)中比較典型的SQL,盡管在表上啟用了并行屬性,但是CBO并沒有選擇啟用并行,原因是T表object_id字段重復率非常的低,這種情況下訪問索引的代價非常小,可能只有幾個邏輯讀,所以沒有必要啟用并行.
在看下下面的SQL
SQL>select object_name ,count(1) from t group by object_name; ....省略輸出 SQL> select a.SQL_ID ,a.CHILD_NUMBER from v$sql a where a.SQL_TEXT like 'select object_name ,count(1) from t group by object_name%';SQL_ID CHILD_NUMBER ------------- ------------ 0vk7jbup3dxbh 0 SQL> select * from table(dbms_xplan.display_cursor('0vk7jbup3dxbh',0));PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- SQL_ID 0vk7jbup3dxbh, child number 0 ------------------------------------- select object_name ,count(1) from t group by object_name Plan hash value: 552882851 -------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time -------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 13 (100)| | 1 | PX COORDINATOR | | | | | | 2 | PX SEND QC (RANDOM) | :TQ10001 | 19568 | 363K| 13 (8)| 00:00: | 3 | HASH GROUP BY | | 19568 | 363K| 13 (8)| 00:00: | 4 | PX RECEIVE | | 35249 | 654K| 12 (0)| 00:00: | 5 | PX SEND HASH | :TQ10000 | 35249 | 654K| 12 (0)| 00:00: | 6 | PX BLOCK ITERATOR | | 35249 | 654K| 12 (0)| 00:00: |* 7 | TABLE ACCESS FULL| T | 35249 | 654K| 12 (0)| 00:00: -------------------------------------------------------------------------------- Predicate Information (identified by operation id):PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- ---------------------------------------------------7 - access(:Z>=:Z AND :Z<=:Z)24 rows selectedSQL>上面的SQL是OLAP系統(tǒng)比較典型的SQL,它的特點是需要對SQL做并行處理。 因為處理的數(shù)據(jù)量很大,所以這種情況使用了并行將更能提高效率。
上述就是并行執(zhí)行的執(zhí)行計劃,如果還原成1
SQL> alter table t parallel 1;Table altered執(zhí)行計劃如下,區(qū)別還是很明顯的。
并行處理的機制
當Oracle 數(shù)據(jù)庫啟動的時候,實例會根據(jù)初始化參數(shù):
PARALLEL_MIN_SERVERS=n的值來預先分配n個并行服務進程,當一條SQL 被CBO判斷為需要并行執(zhí)行時發(fā)出SQL的會話進程變成并行協(xié)助進程,它按照并行執(zhí)行度的值來分配進程服務器進程。
首先協(xié)調(diào)進程會使用ORACLE 啟動時根據(jù)參數(shù): parallel_min_servers=n的值啟動相應的并行服務進程,如果啟動的并行服務器進程數(shù)不足以滿足并行度要求的并行服務進程數(shù),則并行協(xié)調(diào)進程將額外啟動并行服務進程以提供更多的并行服務進程來滿足執(zhí)行的需求。
然后并行協(xié)調(diào)進程將要處理的對象劃分成小數(shù)據(jù)片,分給并行服務進程處理;并行服務進程處理完畢后將結(jié)果發(fā)送給并行協(xié)調(diào)進程,然后由并行協(xié)調(diào)進程將處理結(jié)果匯總并發(fā)送給用戶。
以上是一個并行處理的基本流程。 實際上,在一個并行執(zhí)行的過程中,還存在著并行服務進程之間的通信問題。
在一個并行服務進程需要做兩件事情的時候,它會再啟用一個進程來配和當前的進程完成一個工作,比如這樣的一條SQL語句:
Select * from employees order by last_name;假設(shè)employees表中l(wèi)ast_name 列上沒有索引,并且并行度為4,此時并行協(xié)調(diào)進程會分配4個并行服務進程對表employees進行全表掃描操作,因為需要對結(jié)果集進行排序,所以并行協(xié)調(diào)進程會額外啟用4個并行服務進程,用于處理4個進程傳送過來的數(shù)據(jù),這新啟用的用戶處理傳遞過來數(shù)據(jù)的進程稱為父進程,用戶傳出數(shù)據(jù)(最初的4個并行服務進程)成為子進程,這樣整個并行處理過程就啟用了8個并行服務進程。
其中每個單獨的并行服務進程的行為叫作并行的內(nèi)部操作,而并行服務進程之間的數(shù)據(jù)交流叫做并行的交互操作。
這也是有時我們發(fā)現(xiàn)并行服務進程數(shù)量是并行度的2倍,就是因為啟動了并行服務父進程操作的緣故。
讀懂一個并行處理的執(zhí)行計劃
搞清楚了并行執(zhí)行的內(nèi)部機制,就很容易讀懂一個并行處理的執(zhí)行計劃了。
縮進最深的首先執(zhí)行,依次類推
執(zhí)行步驟:
(1)并行服務進程對t表進行全表掃描。
(2)并行服務進程以ITERATOR(迭代)方式訪問數(shù)據(jù)塊,也就是并行協(xié)調(diào)進程分給每個并行服務進程一個數(shù)據(jù)片,在這個數(shù)據(jù)片上,并行服務進程順序地訪問每個數(shù)據(jù)塊(Iterator),所有的并行服務進程將掃描的數(shù)據(jù)塊傳給另一組并行服務進程(父進程)用于做Hash Group操作。
(3)并行服務父進程對子進程傳遞過來的數(shù)據(jù)做Hash Group操作。
(4)并行服務進程(子進程)將處理完的數(shù)據(jù)發(fā)送出去。
(5)并行服務進程(父進程)接收到處理過的數(shù)據(jù)。
(6)合并處理過的數(shù)據(jù),按照隨即的順序發(fā)給并行協(xié)調(diào)進程(QC:Query Conordinator)。
(7)并行協(xié)調(diào)進程將處理結(jié)果發(fā)給用戶。
PX:Parallel Execution (并行執(zhí)行)
當使用了并行執(zhí)行,SQL的執(zhí)行計劃中就會多出一列:in-out。 該列幫助我們理解數(shù)據(jù)流的執(zhí)行方法.
常見值的含義:
- Parallel to Serial(P->S):
表示一個并行操作發(fā)送數(shù)據(jù)給一個串行操作,通常是并行incheng將數(shù)據(jù)發(fā)送給并行調(diào)度進程。
- Parallel to Parallel(P->P):表示一個并行操作向另一個并行操作發(fā)送數(shù)據(jù),疆場是兩個從屬進程之間的數(shù)據(jù)交流。
- Parallel Combined with parent(PCWP): 同一個從屬進程執(zhí)行的并行操作,同時父操作也是并行的。
- Parallel Combined with Child(PCWC): 同一個從屬進程執(zhí)行的并行操作,子操作也是并行的。
- Serial to Parallel(S->P): 一個串行操作發(fā)送數(shù)據(jù)給并行操作,如果select 部分是串行操作,就會出現(xiàn)這個情況。
一個很常見的并行執(zhí)行等待事件
在做并行執(zhí)行方面的性能優(yōu)化的時候,可能會遇到如下等待時間:
PX Deq Credit: send blkd這是一個有并行環(huán)境的數(shù)據(jù)庫中,從statspack 或者AWR中經(jīng)常可以看到的等待事件.
比如在一個statspack的報告中有如下的信息:
QL> show param parallelNAME TYPE VALUE ------------------------------------ ----------- --------------------- fast_start_parallel_rollback string LOW parallel_adaptive_multi_user boolean TRUE parallel_automatic_tuning boolean FALSE parallel_degree_limit string CPU parallel_degree_policy string MANUAL parallel_execution_message_size integer 16384 parallel_force_local boolean FALSE parallel_instance_group string parallel_io_cap_enabled boolean FALSE parallel_max_servers integer 480 parallel_min_percent integer 0 parallel_min_servers integer 0 parallel_min_time_threshold string AUTO parallel_server boolean FALSE parallel_server_instances integer 1 parallel_servers_target integer 192 parallel_threads_per_cpu integer 2 recovery_parallelism integer 0SQL>在Oracle 9i 里面, 這個等待事件被列入空閑等待。一般來說空閑等待可以忽略它,但是實際上空閑等待也是需要關(guān)注的,因為一個空閑的等待,它反映的是另外的資源已經(jīng)超負荷運行了。 基于這個原因,在Oracle 10g里已經(jīng)把PX Deq Credit: send blkd等待時間不在視為空閑等待,而是列入了Others 等待事件范圍。
PX Deq Credit: send blkd 等待事件的意思是: 當并行服務進程向并行協(xié)調(diào)進程QC(也可能是上一層的并行服務進程)發(fā)送消息時,同一時間只有一個并行服務進程可以向上層進程發(fā)送消息,這時候如果有其他的并行服務進程也要發(fā)送消息,就只能等待。直到獲得一個發(fā)送消息的信用信息(Credit),這時候會觸發(fā)這個等待事件,這個等待事件的超時時間為2秒鐘。
如果我們啟動了太多的并行進程,實際上系統(tǒng)資源(CPU)或者QC 無法即時處理并行服務發(fā)送的數(shù)據(jù),那么等待將不可避免。 對于這種情況,我們就需要降低并行處理的并行度。
我們通過show param parallel查看數(shù)據(jù)庫中最多可以啟動480個并行進程。
parallel_max_servers 480報告中同時也可以看到大量的direct path read ,direct path temp ,db file scattered read
一般direct path read 或者 db file scattered read ,通常來講都是使用并行操作。
當出現(xiàn)PX Deq Credit:send blkd等待的時間很長時,我們可以通過平均等待時間來判斷等待事件是不是下層的并行服務進程空閑造成的。
該等待事件的超時時間是2秒,如果平均等待時間也差不多是2秒,就說明是下層的并行進程“無事所做”,處于空閑狀態(tài)。
如果和2秒的差距很大,就說明不是下層并行服務超時導致的空閑等待,而是并行服務之間的競爭導致的,因為這個平均等待事件非常短,說明并行服務進程在很短時間的等待之后就可以獲取資源來處理數(shù)據(jù)。
所以對于非下層的并行進程造成的等待,解決的方法就是降低每個并行執(zhí)行的并行度,比如對象(表,索引)上預設(shè)的并行度或者查詢Hint 指定的并行度。
并行執(zhí)行的適用范圍
Oracle的并行技術(shù)在下面的場景中可以使用:
- Parallel Query(并行查詢)
- Parallel DDL(并行DDL操作,如建表,建索引等)
- Parallel DML(并行DML操作,如insert,update,delete等)
并行查詢
并行查詢可以在查詢語句,子查詢語句中使用,但是不可以使用在一個遠程引用的對象上(如DBLINK).
一個查詢能夠并行執(zhí)行,需要滿足一下條件:
(1) SQL語句中有Hint提示,比如Parallel 或者 Parallel_index.
(2) SQL語句中引用的對象被設(shè)置了并行屬性。
(3) 多表關(guān)聯(lián)中,至少有一個表執(zhí)行全表掃描(Full table scan)或者跨分區(qū)的Index range SCAN。
如:
select /*+parallel(t 4) * from t;并行DDL 操作
表操作的并行執(zhí)行
以下表操作可以使用并行執(zhí)行:
CREATE TABLE … AS SELECT
ALTER TABLE … move partition
Alter table … split partition
Alter table … coalesce partition
DDL操作,我們可以通過trace 文件來查看它的執(zhí)行過程。
查看當前的trace 文件:
SELECT u_dump.VALUE|| '/'|| db_name.VALUE|| '_ora_'|| v$process.spid|| NVL2 (v$process.traceid, '_' || v$process.traceid, NULL)|| '.trc'"Trace File"FROM v$parameter u_dumpCROSS JOINv$parameter db_nameCROSS JOINv$processJOINv$sessionON v$process.addr = v$session.paddrWHERE u_dump.name = 'user_dump_dest'AND db_name.name = 'db_name'AND v$session.audsid = SYS_CONTEXT ('userenv', 'sessionid');然后用tkprof分析。
創(chuàng)建索引的并行執(zhí)行
創(chuàng)建索引時使用并行方式在系統(tǒng)資源充足的時候會使性能得到很大的提高,特別是在OLAP系統(tǒng)上對一些很大的表創(chuàng)建索引時更是如此。
以下的創(chuàng)建和更改索引的操作都可以使用并行:
Create index
Alter index … rebuild
Alter index … rebuild partition
Alter index … split partition
一個簡單的語法:
create index t_ind on t(id) parallel 4;監(jiān)控這個過程和上面一樣,需要通過10046事件。
使用并行方式,不論是創(chuàng)建表,修改表,創(chuàng)建索引,重建索引,他們的機制都是一樣的,那就是Oracle 給每個并行服務進程分配一塊空間,每個進程在自己的空間里處理數(shù)據(jù),最后將處理完畢的數(shù)據(jù)匯總,完成SQL的操作。
并行DML 操作
Oracle 可以對DML操作使用并行執(zhí)行,但是有很多限制。 如果我們要讓DML 操作使用并行執(zhí)行,必須顯示地在會話里執(zhí)行如下命令:
SQL> alter session enable parallel dml;只有執(zhí)行了這個操作,Oracle 才會對之后符合并行條件的DML操作并行執(zhí)行,如果沒有這個設(shè)定,即使SQL中指定了并行執(zhí)行,Oracle也會忽略它。
delete,update和merge 操作
Oracle 對Delete,update,merge的操作限制在,只有操作的對象是分區(qū)表示,Oracle 才會啟動并行操作。原因在于,對于分區(qū)表,Oracle 會對每個分區(qū)啟用一個并行服務進程同時進行數(shù)據(jù)處理,這對于非分區(qū)表來說是沒有意義的。
insert 的并行操作
實際上只有對于insert into … select … 這樣的SQL語句啟用并行才有意義。 對于insert into .. values… 并行沒有意義,因為這條語句本身就是一個單條記錄的操作。
Insert 并行常用的語法是:
insert /*+parallel(t 2) */ into t select /*+parallel(t1 2) */ * from t1;這條SQL 語句中,可以讓兩個操作insert 和select 分別使用并行,這兩個并行是相互獨立,互不干涉的,也可以單獨使用其中的一個并行。
并行執(zhí)行的設(shè)定
并行相關(guān)的初始化參數(shù)
parallel_min_servers=n
在初始化參數(shù)中設(shè)置了這個值,Oracle 在啟動的時候就會預先啟動N個并行服務進程,當SQL執(zhí)行并行操作時,并行協(xié)調(diào)進程首先根據(jù)并行度的值,在當前已經(jīng)啟動的并行服務中條用n個并行服務進程,當并行度大于n時,Oracle將啟動額外的并行服務進程以滿足并行度要求的并行服務進程數(shù)量。
parallel_max_servers=n
如果并行度的值大于parallel_min_servers或者當前可用的并行服務進程不能滿足SQL的并行執(zhí)行要求,Oracle將額外創(chuàng)建新的并行服務進程,當前實例總共啟動的并行服務進程不能超過這個參數(shù)的設(shè)定值。
parallel_adaptive_multi_user=true|false
Oracle 10g R2下,并行執(zhí)行默認是啟用的。 這個參數(shù)的默認值為true,它讓Oracle根據(jù)SQL執(zhí)行時系統(tǒng)的負載情況,動態(tài)地調(diào)整SQL的并行度,以取得最好的SQL 執(zhí)行性能。
parallel_min_percent
這個參數(shù)指定并行執(zhí)行時,申請并行服務進程的最小值,它是一個百分比,比如我們設(shè)定這個值為50. 當一個SQL需要申請20個并行進程時,如果當前并行服務進程不足,按照這個參數(shù)的要求,這個SQL比如申請到20*50%=10個并行服務進程,如果不能夠申請到這個數(shù)量的并行服務,SQL 將報出一個ORA-12827的錯誤。
當這個值設(shè)為Null時,表示所有的SQL在做并行執(zhí)行時,至少要獲得兩個并行服務進程。
并行度的設(shè)定
并行度可以通過以下三種方式來設(shè)定:
- 使用Hint 指定并行度。
- 使用alter session force parallel 設(shè)定并行度。
- 使用SQL中引用的表或者索引上設(shè)定的并行度,原則上Oracle 使用這些對象中并行度最高的那個值作為當前執(zhí)行的并行度。
Oracle 默認并行度計算方式
Oracle 根據(jù)CPU的個數(shù),RAC實例的個數(shù)以及參數(shù)parallel_threads_per_cpu的值,計算出一個并行度。
對于并行訪問分區(qū)操作,取需要訪問的分區(qū)數(shù)為并行度。
并行度的優(yōu)先級別從高到低:
Hint->alter session force parallel->表,索引上的設(shè)定-> 系統(tǒng)參數(shù)
實際上,并行只有才系統(tǒng)資源比較充足的情況下,才會取得很好的性能,如果系統(tǒng)負擔很重,不恰當?shù)脑O(shè)置并行,反而會使性能大幅下降。
直接加載
在執(zhí)行數(shù)據(jù)插入或者數(shù)據(jù)加載的時候,可以通過append hint的方式進行數(shù)據(jù)的直接加載。
在insert 的SQL中使用APPEND,如:
insert /*+append */ into t select * from t1;還可以在SQL*LOADER里面使用直接加載:
Sqlldr userid=user/pwd control=load.ctl direct=trueOracle 執(zhí)行直接加載時,數(shù)據(jù)直接追加到數(shù)據(jù)段的最后,不需要花費時間在段中需找空間,數(shù)據(jù)不經(jīng)過data buffer直接寫到數(shù)據(jù)文件中,效率要比傳統(tǒng)的加載方式高。
總結(jié)
以上是生活随笔為你收集整理的Oracle优化08-并行执行的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Oracle优化07-分析及动态采样-直
- 下一篇: Oracle优化07-分析及动态采样-D