並行(Parallel)

perfychi發表於2013-08-28
並行(Parallel)
並行執行的使用範圍
Oracle的並行技術在下面的場景中可以使用:
(1)       Parallel Query(並行查詢)
(2)       Parallel DDL(並行DDL操作,如建表,建索引等)
(3)       Parallel DML(並行DML操作,如insert,update,delete等)

5.1 並行查詢
並行查詢可以在查詢語句,子查詢語句中使用,但是不可以使用在一個遠端引用的物件上(如DBLINK)。
一個查詢能夠並行執行,需要滿足一下條件:
(1)       SQL語句中有Hint提示,比如Parallel 或者 Parallel_index.
(2)       SQL語句中引用的物件被設定了並行屬性。
(3)       多表關聯中,至少有一個表執行全表掃描(Full table scan)或者跨分割槽的Index range SCAN。
如: select  /*+ parallel(t 4) */ from t;
當執行對錶test的查詢沒有為查詢執行並行度,oracle使用4作為並行度的預設值。查詢時可以為parallel指定一個新值:
select /*+ parallel (test,6)*/ * from test;
也可以關閉一張表上給定查詢中的並行操作:
select /*+ no_parallel (test)*/ * from test;

5.2 並行DDL 操作
5.2.1 表操作的並行執行
以下表操作可以使用並行執行:
CREATE TABLE … AS SELECT
ALTER TABLE … move partition
Alter table … split partition
Alter table … coalesce partition


例如:create table test parallel 4 as select * from dba_objects;
建立表時,可以指定該表可以使用的並行度DOP(degree of  parallelism):

create table test(
testID NUMBER(12) not null,
testDate DATE
)parallel 4;
alter table test parallel(degree 4);



5.2.2 建立索引的並行執行
建立索引時使用並行方式在系統資源充足的時候會使效能得到很大的提高,特別是在OLAP系統上對一些很大的表建立索引時更是如此。 以下的建立和更改索引的操作都可以使用並行:
Create index
Alter index … rebuild
Alter index … rebuild partition
Alter index … split partition
一個簡單的語法:create index t_ind on t(id) parallel 4;
create index TMP_ACCT_INFO_CURR_BAL_001 on B_S_ACCT_INFO_CURR_BAL (BACCT_TYPE) parallel(DEGREE 10) NOLOGGING;


5.3 並行DML 操作
Oracle 可以對DML操作使用並行執行,但是有很多限制。 如果我們要讓DML 操作使用並行執行,必須顯示地在會話裡執行如下命令:
SQL> alter session enable parallel dml;

會話已更改。
只有執行了這個操作,Oracle 才會對之後符合並行條件的DML操作並行執行,如果沒有這個設定,即使SQL中指定了並行執行,Oracle也會忽略它。

5.3.1 delete,update和merge 操作
Oracle 對Delete,update,merge的操作限制在,只有操作的物件是分割槽表示,Oracle 才會啟動並行操作。原因在於,對於分割槽表,Oracle 會對每個分割槽啟用一個並行服務程式同時進行資料處理,這對於非分割槽表來說是沒有意義的。
5.3.2 Insert 的並行操作
實際上只有對於insert into … select … 這樣的SQL語句啟用並行才有意義。 對於insert into .. values… 並行沒有意義,因為這條語句本身就是一個單條記錄的操作。
Insert 並行常用的語法是:
Insert /*+parallel(t 2) */ into t select /*+parallel(t1 2) */ * from t1;
這條SQL 語句中,可以讓兩個操作insert 和select 分別使用並行,這兩個並行是相互獨立,互補干涉的,也可以單獨使用其中的一個並行。


六. 並行執行的設定
6.1 並行相關的初始話引數


七. 直接載入
6.1.1 parallel_min_servers=n                                                                                                                                                                                                                                                             在執行資料插入或者資料載入的時候,可以透過append hint的方式進行資料的直接載入。

在初始化引數中設定了這個值,Oracle 在啟動的時候就會預先啟動N個並行服務程式,當SQL執行並行操作時,並行協調程式首先根據並行度的值,在當前已經啟動的並行服務中條用n個並行服務程式,當並行度大於n時,Oracle將啟動額外的並行服務程式以滿足並行度要求的並行服務程式數量。
在insert 的SQL中使用APPEND,如:
Insert /*+ append */ into t select * from t1;

6.1.2 parallel_max_servers=n
insert /*+append parallel(t,2) */ into t select * from t1;
如果並行度的值大於parallel_min_servers或者當前可用的並行服務程式不能滿足SQL的並行執行要求,Oracle將額外建立新的並行服務程式,當前例項總共啟動的並行服務程式不能超過這個引數的設定值。
注:在對insert 使用並行時,Oracle自動使用直接載入的方式進行資料載入,所以在這種情況下append是可以省略的。

這個地方有出入,在並行insert 的時候,如果不加append, 資料是不會從高水位線插入的。所以並行insert ,想要在高水位線以上載入資料,不能省略append;

6.1.3 parallel_adaptive_multi_user=true|false

Oracle 10g R2下,並行執行預設是啟用的。 這個引數的預設值為true,它讓Oracle根據SQL執行時系統的負載情況,動態地調整SQL的並行度,以取得最好的SQL    執行效能。



6.1.4 parallel_min_percent

這個引數指定並行執行時,申請並行服務程式的最小值,它是一個百分比,比如我們設定這個值為50. 當一個SQL需要申請20個並行程式時,如果當前並行服務程式不足,按照這個引數的要求,這個SQL比如申請到20*50%=10個並行服務程式,如果不能夠申請到這個數量的並行服務,SQL 將報出一個ORA-12827的錯誤。

當這個值設為Null時,表示所有的SQL在做並行執行時,至少要獲得兩個並行服務程式。





6.2 並行度的設定

並行度可以透過以下三種方式來設定:

(1)使用Hint 指定並行度。

(2)使用alter session force parallel 設定並行度。

(3)使用SQL中引用的表或者索引上設定的並行度,原則上Oracle 使用這些物件中並行度最高的那個值作為當前執行的並行度。





示例:

SQL>Select /*+parallel(t 4) */ count(*) from t;

SQL>Alter table t parallel 4;

SQL>Alter session force parallel query parallel 4;



Oracle 預設並行度計算方式:

(1)Oracle 根據CPU的個數,RAC例項的個數以及引數parallel_threads_per_cpu的值,計算出一個並行度。

(2)對於並行訪問分割槽操作,取需要訪問的分割槽數為並行度。



並行度的優先順序別從高到低:

Hint->alter session force parallel->表,索引上的設定-> 系統引數





實際上,並行只有才系統資源比較充足的情況下,才會取得很好的效能,如果系統負擔很重,不恰當的設定並行,反而會使效能大幅下降。



七. 直接載入

在執行資料插入或者資料載入的時候,可以透過append hint的方式進行資料的直接載入。


在insert 的SQL中使用APPEND,如:

Insert /*+append */ into t select * from t1;



還可以在SQL*LOADER裡面使用直接載入:

Sqlldr userid=user/pwd control=load.ctl direct=true



Oracle 執行直接載入時,資料直接追加到資料段的最後,不需要花費時間在段中需找空間,資料不經過data buffer直接寫到資料檔案中,效率要比傳統的載入方式高。



示例:

SQL> create table t as select * from user_tables;

表已建立。

SQL> select segment_name,extent_id,bytes from user_extents where segment_name='T';

SEGMENT_NA  EXTENT_ID      BYTES

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

T                   0      65536

T                   1      65536

T                   2      65536

T                   3      65536

T                   4      65536



這裡我們建立了一張表,分配了5個extents。



SQL> delete from t;

已刪除979行。

SQL> select segment_name,extent_id,bytes from user_extents where segment_name='T';

SEGMENT_NA  EXTENT_ID      BYTES

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

T                   0      65536

T                   1      65536

T                   2      65536

T                   3      65536

T                   4      65536



這裡刪除了表裡的資料,但是查詢,依然佔據5個extents。因為delete不會收縮表空間,不能降低高水位。



SQL> insert into t select * from user_tables;

已建立980行。

SQL> commit;

提交完成。



SQL> select segment_name,extent_id,bytes from user_extents where segment_name='T';

SEGMENT_NA  EXTENT_ID      BYTES

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

T                   0      65536

T                   1      65536

T                   2      65536

T                   3      65536

T                   4      65536



用傳統方式插入,資料被分配到已有的空閒空間裡。





SQL> delete from t;

已刪除980行。

SQL> commit;

提交完成。

SQL> select segment_name,extent_id,bytes from user_extents where segment_name='T';

SEGMENT_NA  EXTENT_ID      BYTES

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

T                   0      65536

T                   1      65536

T                   2      65536

T                   3      65536

T                   4      65536



刪除資料,用append直接插入看一下。



SQL> insert /*+append */ into t select * from user_tables;

已建立980行。

SQL> commit;

提交完成。

SQL> select segment_name,extent_id,bytes from user_extents where segment_name='T';

SEGMENT_NA  EXTENT_ID      BYTES

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

T                   0      65536

T                   1      65536

T                   2      65536

T                   3      65536

T                   4      65536

T                   5      65536

T                   6      65536

T                   7      65536

T                   8      65536

T                   9      65536

已選擇10行。



從結果可以看出,直接載入方式時,雖然表中有很多空的資料塊,Oracle 仍然會額外的分配4個extent用於直接載入資料。

直接載入的資料放在表的高水位(High water Mark:hwm)以上,當直接載入完成後,Oracle 將表的高水位線移到新加入的資料之後,這樣新的資料就可以被使用者使用了。



Oracle 高水位(HWM)

http://blog.csdn.net/tianlesoftware/archive/2009/10/22/4707900.aspx





7.1 直接載入和REDO

直接載入在logging模式下,與傳統載入方式產生的redo 日誌差別不大,因為當一個表有logging屬性時,即使使用直接載入,所有改變的資料依然要產生redo,實際上是所有修改的資料塊全部記錄redo,以便於以後的恢復,這時候直接載入並沒有太大的優勢。



直接載入最常見的是和nologging一起使用,這時候可以有效地減少redo 的生成量。 注意的是,在這種情況下,直接載入的資料塊是不產生redo的,只有一些其他改變的資料產生一些redo,比如表空間分配需要修改字典表或者修改段頭資料塊,這些修改會產生少量的redo。



實際上,對於nologging 方式的直接載入,undo 的資料量也產生的很少,因為直接載入的資料並不會在回滾段中記錄,這些記錄位於高水位之上,在事務提交之前,對於其他使用者來說是不可見的,所以不需要產生undo,事務提交時,Oracle 將表的高水位線移到新的資料之後,如果事務回滾,只需要保持高水位線不動即可,就好像什麼都沒有發生一樣。



注意,由於在nologging模式下,redo 不記錄資料修改的資訊,所以直接載入完後,需要立即進行相關的備份操作,因為這些資料沒有記錄在歸檔日誌中,一旦資料損壞,只能用備份來恢復,而不能使用歸檔恢復。



Logging模式下示例:

SQL> set autot trace stat;

SQL> insert /*+ append */ into t select * from user_tables;

已建立980行。

統計資訊

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

132  recursive calls

87  db block gets

8967  consistent gets

0  physical reads

286572  redo size

911  bytes sent via SQL*Net to client

1017  bytes received via SQL*Net from client

4  SQL*Net roundtrips to/from client

2  sorts (memory)

0  sorts (disk)

980  rows processed

SQL> rollback;

回退已完成。

SQL> insert into t select * from user_tables;

已建立980行。

統計資訊

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

0  recursive calls

144  db block gets

9027  consistent gets

0  physical reads

267448  redo size

927  bytes sent via SQL*Net to client

1004  bytes received via SQL*Net from client

4  SQL*Net roundtrips to/from client

2  sorts (memory)

0  sorts (disk)

980  rows processed



Nologging模式下示例:

SQL> alter table t nologging;

表已更改。

SQL> insert into t select * from user_tables;

已建立980行。

統計資訊

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

239  recursive calls

132  db block gets

9061  consistent gets

0  physical reads

262896  redo size

927  bytes sent via SQL*Net to client

1004  bytes received via SQL*Net from client

4  SQL*Net roundtrips to/from client

7  sorts (memory)

0  sorts (disk)

980  rows processed

SQL> rollback;

回退已完成。

SQL> insert /*+append */ into t select * from user_tables;

已建立980行。

統計資訊

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

8  recursive calls

40  db block gets

8938  consistent gets

0  physical reads

340  redo size  -- redo 減少很多

911  bytes sent via SQL*Net to client

1017  bytes received via SQL*Net from client

4  SQL*Net roundtrips to/from client

2  sorts (memory)

0  sorts (disk)

980  rows processed



這部分內容也可參考Blog:

Oracle DML NOLOGGING

http://blog.csdn.net/tianlesoftware/archive/2010/07/11/5701596.aspx





7.2 直接載入和索引

如果直接載入的表上有索引,Oracle不會像載入資料的方式那樣來處理索引的資料,但是它同樣需要維護一個索引,這個成本很高,同時會生成很多的redo。

所以當使用直接載入時,通常是針對一些資料量非常大的表。如果這些表存在索引,將會帶來很大的效能影響,這時可以考慮先將索引disable或者drop掉,等載入資料後,之後在重新建立索引。



nologging示例:



SQL> insert /*+append */ into t select * from user_tables;

已建立980行。



統計資訊

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

0  recursive calls

40  db block gets

8936  consistent gets

0  physical reads

384  redo size

911  bytes sent via SQL*Net to client

1017  bytes received via SQL*Net from client

4  SQL*Net roundtrips to/from client

2  sorts (memory)

0  sorts (disk)

980  rows processed



SQL> rollback;

回退已完成。

SQL> create index t_ind on t(table_name);

索引已建立。

SQL> insert /*+append */ into t select * from user_tables;

已建立980行。



統計資訊

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

40  recursive calls

170  db block gets

8955  consistent gets

4  physical reads

149424  redo size

911  bytes sent via SQL*Net to client

1017  bytes received via SQL*Net from client

4  SQL*Net roundtrips to/from client

3  sorts (memory)

0  sorts (disk)

980  rows processed

SQL> rollback;

回退已完成。

SQL> insert  into t select * from user_tables;

已建立980行。



統計資訊

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

8  recursive calls

828  db block gets

9037  consistent gets

0  physical reads

382832  redo size

927  bytes sent via SQL*Net to client

1005  bytes received via SQL*Net from client

4  SQL*Net roundtrips to/from client

2  sorts (memory)

0  sorts (disk)

980  rows processed

SQL> rollback;

回退已完成。





7.3 直接載入和並行

直接載入可以和並行執行一同使用,這樣可以並行地向表中插入資料。 如:



SQL>alter session enable parallel dml;  -- 這裡必須顯示的申明

SQL>insert /*+append parallel(t,2) */ into t select * from t1;

SQL>insert /*+append */ into t select * from t1;



注:在對insert 使用並行時,Oracle自動使用直接載入的方式進行資料載入,所以在這種情況下append是可以省略的。



當使用並行載入時,Oracle 會按照並行度啟動相應數量的並行服務程式,像序列執行的直接載入的方式一樣,每個並行服務程式都單獨分配額外的空間用於載入資料,實際上Oracle 為每個並行服務程式分配了一個臨時段,每個並行服務程式將資料首先載入到各自的臨時段上,當所有的並行程式執行完畢後,將各自的資料塊合併到一起,放到高水位之後,如果事務提交,則將高水位移到新載入的資料之後。





7.4 直接載入和SQL*LOADER

在SQL*LOADER中也可以使用直接載入,它比傳統方式效率更高,因為它繞開了SQL的解析和資料緩衝區,直接將資料載入到資料檔案,這對OLAP或者資料倉儲系統非常有用。



指定載入:

Sqlldr userid=user/pwd control=control.ctl direct=true



指定並行和載入:

Sqlldr userid=user/pwd control=control.ctl direct=true     parallel=true


註釋:parallel=true的時候,sqlldr匯入方式只能選擇append.如果是truncate方式會報錯。




SQL*LOADER直接載入對索引的影響:

(1)索引為非約束性,直接載入可以在載入完畢後維護索引的完整性。

(2)索引為約束性索引,比如主鍵,直接載入仍然會將資料載入入庫,但是會將索引置為unusable.

註釋:這個地方有出入,當為約束性索引的時候,直接載入, 索引仍然為valid,而不是unusable.只有匯入的資料違背約束性要求,比如唯一性的時候,索引狀態才是unusable.





如果使用SQL*LOADER的並行直接載入選項,並且表上有索引,將導致載入失敗,這是我們可以在sqlloader中指定skip_index_maintenance=true, 來允許載入完成,但是索引狀態會變成unusable,需要手工rebuild.



關於SQL*LOADER的更多內容,參考blog:

Oracle SQL Loader

http://blog.csdn.net/tianlesoftware/archive/2009/10/16/4674063.aspx




本文來自CSDN部落格,轉載請標明出處:http://blog.csdn.net/tianlesoftware/archive/2010/09/01/5854583.aspx

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

相關文章