[Oracle10G_R2]04.數(shù)據(jù)倉(cāng)庫(kù)和集成特性
?
??? 用于更高效管理物化視圖、查詢重寫、可傳輸表空間以及表分區(qū)的新特性使數(shù)據(jù)倉(cāng)庫(kù)變得更加強(qiáng)大且占用資源更少。
?
??? 這一部分涉及:
?
??? ● 在沒有 MV 日志的情況下進(jìn)行分區(qū)更改跟蹤
??? ● 使用多個(gè) MV 進(jìn)行查詢重寫
??? ● 通過備份實(shí)現(xiàn)可傳輸表空間
??? ● 對(duì)已分區(qū)的按索引組織的表進(jìn)行快速的分區(qū)分割
??? ● 通過聯(lián)機(jī)重新定義進(jìn)行 LONG 到 LOB 的轉(zhuǎn)換
??? ● 聯(lián)機(jī)重新組織單個(gè)分區(qū)
??? ● 逐個(gè)分區(qū)地刪除表
??? ● 使用多個(gè) MV 進(jìn)行查詢重寫
??? ● 通過備份實(shí)現(xiàn)可傳輸表空間
??? ● 對(duì)已分區(qū)的按索引組織的表進(jìn)行快速的分區(qū)分割
??? ● 通過聯(lián)機(jī)重新定義進(jìn)行 LONG 到 LOB 的轉(zhuǎn)換
??? ● 聯(lián)機(jī)重新組織單個(gè)分區(qū)
??? ● 逐個(gè)分區(qū)地刪除表
?
?
分區(qū)更改跟蹤:不需要 MV 日志
?
??? 要了解此增強(qiáng)功能,首先必須了解物化視圖 (MV) 刷新過程中的分區(qū)修整概念。
?
??? 假設(shè)基于列 ACC_MGR_ID 對(duì)表 ACCOUNTS 進(jìn)行了分區(qū),每個(gè) ACC_MGR_ID 值一個(gè)分區(qū)。您根據(jù) ACCOUNTS 創(chuàng)建了一個(gè)名為 ACC_VIEW 的 MV,該 MV 也根據(jù)列 ACC_MGR_ID 進(jìn)行了分區(qū),每個(gè) ACC_MGR_ID 一個(gè)分區(qū),如下圖所示:
?
?
??? 假設(shè)已經(jīng)更新了表 ACCOUNTS 中的記錄,但只在分區(qū) P1 中進(jìn)行了此更新。要快速刷新此 MV,您只需刷新分區(qū) P1 而非整個(gè)表,這里正是與 ACC_MGR_ID 相關(guān)的數(shù)據(jù)所在的分區(qū)。Oracle 自動(dòng)執(zhí)行此任務(wù),通過一個(gè)名為分區(qū)更改跟蹤 (PCT) 的特性跟蹤對(duì)分區(qū)的更改。但有一個(gè)問題需稍加注意:要在快速刷新的過程中啟用 PCT,必須創(chuàng)建 MV 日志,當(dāng)表中的行發(fā)生變化會(huì)填充這些日志。發(fā)出刷新命令后,刷新進(jìn)程將讀取 MV 日志以識(shí)別這些更改。
?
??? 不用說(shuō),該要求增加了操作的總執(zhí)行時(shí)間。此外,附加的插入操作將消耗 CPU 周期和 I/O 帶寬。
?
??? 幸好,在 Oracle 數(shù)據(jù)庫(kù) 10g 第 2 版中,PCT 不需要 MV 日志即可工作。讓我們看一看它的作用方式。首先,確認(rèn)表 ACCOUNTS 中沒有 MV 日志。
?
SQL> select *
2? from dba_mview_logs
3? where master = 'ACCOUNTS';
2? from dba_mview_logs
3? where master = 'ACCOUNTS';
?
no rows selected
?
??? 現(xiàn)在,更新該表中的某個(gè)記錄。
?
update accounts set last_name = '...'
where acc_mgr_id = 3;
where acc_mgr_id = 3;
?
??? 該記錄位于分區(qū) P3 中。
?
??? 現(xiàn)在,您就可以刷新此 MV 了。但首先記錄表 ACCOUNTS 所有段的段級(jí)統(tǒng)計(jì)信息。稍后,您將使用這些統(tǒng)計(jì)信息了解使用了哪些段。
?
select SUBOBJECT_NAME, value from v$segment_statistics
where owner = 'ARUP'
and OBJECT_NAME = 'ACCOUNTS'
and STATISTIC_NAME = 'logical reads'
order by SUBOBJECT_NAME
/
where owner = 'ARUP'
and OBJECT_NAME = 'ACCOUNTS'
and STATISTIC_NAME = 'logical reads'
order by SUBOBJECT_NAME
/
?
SUBOBJECT_NAME????????????????????? VALUE
------------------------------ ----------
P1?????????????????????????????????? 8320
P10????????????????????????????????? 8624
P2????????????????????????????????? 12112
P3????????????????????????????????? 11856
P4?????????????????????????????????? 8800
P5?????????????????????????????????? 7904
P6?????????????????????????????????? 8256
P7?????????????????????????????????? 8016
P8?????????????????????????????????? 8272
P9?????????????????????????????????? 7840
PMAX????????????????????????????????? 256
------------------------------ ----------
P1?????????????????????????????????? 8320
P10????????????????????????????????? 8624
P2????????????????????????????????? 12112
P3????????????????????????????????? 11856
P4?????????????????????????????????? 8800
P5?????????????????????????????????? 7904
P6?????????????????????????????????? 8256
P7?????????????????????????????????? 8016
P8?????????????????????????????????? 8272
P9?????????????????????????????????? 7840
PMAX????????????????????????????????? 256
?
11 rows selected.
?
??? 使用快速刷新刷新物化視圖 ACC_VIEW。
?
execute dbms_mview.refresh('ACC_VIEW','F')
?
??? 'F' 參數(shù)指示快速刷新。但如果表沒有 MV 日志,它是否可以起作用?
?
??? 刷新完成后,再次檢查表 ACCOUNTS 的段統(tǒng)計(jì)信息。結(jié)果如下所示:
?
SUBOBJECT_NAME????????????????????? VALUE
------------------------------ ----------
P1?????????????????????????????????? 8320
P10????????????????????????????????? 8624
P2????????????????????????????????? 12112
P3????????????????????????????????? 14656
P4?????????????????????????????????? 8800
P5?????????????????????????????????? 7904
P6?????????????????????????????????? 8256
P7?????????????????????????????????? 8016
P8?????????????????????????????????? 8272
P9?????????????????????????????????? 7840
PMAX????????????????????????????????? 256
------------------------------ ----------
P1?????????????????????????????????? 8320
P10????????????????????????????????? 8624
P2????????????????????????????????? 12112
P3????????????????????????????????? 14656
P4?????????????????????????????????? 8800
P5?????????????????????????????????? 7904
P6?????????????????????????????????? 8256
P7?????????????????????????????????? 8016
P8?????????????????????????????????? 8272
P9?????????????????????????????????? 7840
PMAX????????????????????????????????? 256
?
??? 這些段統(tǒng)計(jì)信息顯示了在一個(gè)邏輯讀取過程中選擇的段。由于這些統(tǒng)計(jì)信息是累積的,因此您必須查看值(而非絕對(duì)值)中的更改。如果仔細(xì)查看以上值,您便會(huì)發(fā)現(xiàn)只有分區(qū) P3 的值發(fā)生了變化。因此,在刷新過程中只選擇了分區(qū) P3 而非整個(gè)表,確認(rèn) PCT 能否在表即使沒有 MV 日志的情況下工作。
即使在基表沒有 MV 日志的情況下也可以快速刷新 MV 的能力是一個(gè)強(qiáng)大而有用的特性,從而允許您可以在已分區(qū)的 MV 中執(zhí)行快速刷新而不會(huì)增加性能開銷。我認(rèn)為,該特性是 Oracle 數(shù)據(jù)庫(kù) 10g 第 2 版中最有用的數(shù)據(jù)倉(cāng)庫(kù)增強(qiáng)功能。
即使在基表沒有 MV 日志的情況下也可以快速刷新 MV 的能力是一個(gè)強(qiáng)大而有用的特性,從而允許您可以在已分區(qū)的 MV 中執(zhí)行快速刷新而不會(huì)增加性能開銷。我認(rèn)為,該特性是 Oracle 數(shù)據(jù)庫(kù) 10g 第 2 版中最有用的數(shù)據(jù)倉(cāng)庫(kù)增強(qiáng)功能。
?
使用多個(gè) MV 進(jìn)行查詢重寫
?
??? Oracle8i 中引入的查詢重寫特性在數(shù)據(jù)倉(cāng)庫(kù)開發(fā)人員和 DBA 中轟動(dòng)一時(shí)。從本質(zhì)上而言,它將用戶查詢重寫為從 MV 而非表中進(jìn)行選擇以利用現(xiàn)成的摘要。例如,請(qǐng)考慮以下一家大型連鎖酒店的數(shù)據(jù)庫(kù)中的三個(gè)表。
?
SQL> DESC HOTELS
Name????????????????????????????? Null?Type
----------------------------------------- -------- -------------
HOTEL_ID????????????????????????????????? NOT NULL NUMBER(10)
CITY?????????????????????????????????????????????? VARCHAR2(20)
STATE????????????????????????????????????????????? CHAR(2)
MANAGER_NAME?????????????????????????????????????? VARCHAR2(20)
RATE_CLASS???????????????????????????????????????? CHAR(2)
Name????????????????????????????? Null?Type
----------------------------------------- -------- -------------
HOTEL_ID????????????????????????????????? NOT NULL NUMBER(10)
CITY?????????????????????????????????????????????? VARCHAR2(20)
STATE????????????????????????????????????????????? CHAR(2)
MANAGER_NAME?????????????????????????????????????? VARCHAR2(20)
RATE_CLASS???????????????????????????????????????? CHAR(2)
?
SQL> DESC RESERVATIONS
Name????????????????????????????? Null?Type
----------------------------------------- -------- -------------
RESV_ID?????????????????????????????????? NOT NULL NUMBER(10)
HOTEL_ID?????????????????????????????????????????? NUMBER(10)
CUST_NAME????????????????????????????????????????? VARCHAR2(20)
START_DATE???????????????????????????????????????? DATE
END_DATE?????????????????????????????????????????? DATE
RATE?????????????????????????????????????????????? NUMBER(10)
Name????????????????????????????? Null?Type
----------------------------------------- -------- -------------
RESV_ID?????????????????????????????????? NOT NULL NUMBER(10)
HOTEL_ID?????????????????????????????????????????? NUMBER(10)
CUST_NAME????????????????????????????????????????? VARCHAR2(20)
START_DATE???????????????????????????????????????? DATE
END_DATE?????????????????????????????????????????? DATE
RATE?????????????????????????????????????????????? NUMBER(10)
?
SQL> DESC TRANS
Name????????????????????????????? Null?Type
----------------------------------------- -------- -------------
TRANS_ID????????????????????????????????? NOT NULL NUMBER(10)
RESV_ID?????????????????????????????????? NOT NULL NUMBER(10)
TRANS_DATE???????????????????????????????????????? DATE
ACTUAL_RATE??????????????????????????????????????? NUMBER(10)
Name????????????????????????????? Null?Type
----------------------------------------- -------- -------------
TRANS_ID????????????????????????????????? NOT NULL NUMBER(10)
RESV_ID?????????????????????????????????? NOT NULL NUMBER(10)
TRANS_DATE???????????????????????????????????????? DATE
ACTUAL_RATE??????????????????????????????????????? NUMBER(10)
?
??? 表 HOTELS 保存酒店的相關(guān)信息。當(dāng)顧客預(yù)訂酒店時(shí),將在表 RESERVATIONS(包含房間價(jià)格報(bào)價(jià))中創(chuàng)建一個(gè)記錄。當(dāng)顧客在酒店結(jié)帳時(shí),將在另一個(gè)表 TRANS 中記錄現(xiàn)金交易。
?
??? 但在結(jié)帳前,酒店可能決定根據(jù)訂房情況、升級(jí)、優(yōu)惠等因素向顧客提供不同的房?jī)r(jià)。因此,最終的房?jī)r(jià)可能與預(yù)訂時(shí)的報(bào)價(jià)不同,而且可以每天都各不相同。為正確記錄這些價(jià)格變化,表 TRANS 有一行專門用來(lái)保存每天的房?jī)r(jià)信息。
?
??? 為縮短查詢響應(yīng)時(shí)間,您可能決定根據(jù)用戶發(fā)出的不同查詢構(gòu)建 MV,如:
?
create materialized view mv_hotel_resv
refresh complete
enable query rewrite
as
select city, resv_id, cust_name
from hotels h, reservations r
where r.hotel_id = h.hotel_id;
refresh complete
enable query rewrite
as
select city, resv_id, cust_name
from hotels h, reservations r
where r.hotel_id = h.hotel_id;
??? 和
?
create materialized view mv_actual_sales
refresh complete
enable query rewrite
as
select resv_id, sum(actual_rate) from trans group by resv_id;
refresh complete
enable query rewrite
as
select resv_id, sum(actual_rate) from trans group by resv_id;
?
??? 因此,如果設(shè)置了某些參數(shù)(如 query_rewrite_enabled = true),則類似如下所示的查詢
?
select city, cust_name
from hotels h, reservations r
where r.hotel_id = h.hotel_id;
from hotels h, reservations r
where r.hotel_id = h.hotel_id;
?
??? 將重寫為
?
select city, cust_name
from mv_hotel_resv;
from mv_hotel_resv;
?
??? 您可以通過運(yùn)行該查詢并啟用自動(dòng)跟蹤來(lái)確認(rèn) MV。
?
SQL> set autot traceonly explain
SQL> select city, cust_name
2> from hotels h, reservations r
3> where r.hotel_id = h.hotel_id;
SQL> select city, cust_name
2> from hotels h, reservations r
3> where r.hotel_id = h.hotel_id;
?
Execution Plan
----------------------------------------------------------
0????? SELECT STATEMENT Optimizer=ALL_ROWS (Cost=3 Card=80 Bytes=2480)
1??? 0?? MAT_VIEW ACCESS (FULL) OF 'MV_HOTEL_RESV' (MAT_VIEW) (Cost=3 Card=80 Bytes=2480)
----------------------------------------------------------
0????? SELECT STATEMENT Optimizer=ALL_ROWS (Cost=3 Card=80 Bytes=2480)
1??? 0?? MAT_VIEW ACCESS (FULL) OF 'MV_HOTEL_RESV' (MAT_VIEW) (Cost=3 Card=80 Bytes=2480)
?
??? 注意,查詢是如何從物化視圖 MV_HOTEL_RESV 而非表 HOTELS 和 RESERVATIONS 中進(jìn)行選擇的。這正是您所需要的。同樣,當(dāng)您編寫一個(gè)查詢來(lái)匯總每個(gè)預(yù)訂編號(hào)的實(shí)際價(jià)格時(shí),將使用物化視圖 MV_ACTUAL_SALES 而非表 TRANS。
???
??? 我們來(lái)采用一個(gè)不同的查詢。如果要查明每個(gè)城市的實(shí)際銷售額,則將發(fā)出
?
select city, sum(actual_rate)
from hotels h, reservations r, trans t
where t.resv_id = r.resv_id
and r.hotel_id = h.hotel_id
group by city;
from hotels h, reservations r, trans t
where t.resv_id = r.resv_id
and r.hotel_id = h.hotel_id
group by city;
?
??? 注意此查詢結(jié)構(gòu):從 MV_ACTUAL_SALES 中,您可以獲得 RESV_ID 和預(yù)訂的總銷售額。從 MV_HOTEL_RESV 中,您可以獲得 CITY 和 RESV_ID。
?
??? 您能將這兩個(gè) MV 連接在一起嗎?當(dāng)然可以,但在 Oracle 數(shù)據(jù)庫(kù) 10g 第 2 版之前,查詢重寫機(jī)制只使用兩個(gè) MV 中的一個(gè)(而非兩個(gè))自動(dòng)重寫用戶查詢。
以下是 Oracle9i 數(shù)據(jù)庫(kù)中的執(zhí)行計(jì)劃輸出。您可以看到,只使用了 MV_HOTEL_RESV 和 TRANS 的整表掃描。
以下是 Oracle9i 數(shù)據(jù)庫(kù)中的執(zhí)行計(jì)劃輸出。您可以看到,只使用了 MV_HOTEL_RESV 和 TRANS 的整表掃描。
?
Execution Plan
----------------------------------------------------------
0????? SELECT STATEMENT Optimizer=ALL_ROWS (Cost=8 Card=6 Bytes=120)
1??? 0?? SORT (GROUP BY) (Cost=8 Card=6 Bytes=120)
2??? 1???? HASH JOIN (Cost=7 Card=516 Bytes=10320)
3??? 2?????? MAT_VIEW REWRITE ACCESS (FULL) OF 'MV_HOTEL_RESV' (MAT_VIEW REWRITE)
(Cost=3 Card=80 Bytes=1040)
4??? 2?????? TABLE ACCESS (FULL) OF 'TRANS' (TABLE)
(Cost=3 Card=516 Bytes=3612)
----------------------------------------------------------
0????? SELECT STATEMENT Optimizer=ALL_ROWS (Cost=8 Card=6 Bytes=120)
1??? 0?? SORT (GROUP BY) (Cost=8 Card=6 Bytes=120)
2??? 1???? HASH JOIN (Cost=7 Card=516 Bytes=10320)
3??? 2?????? MAT_VIEW REWRITE ACCESS (FULL) OF 'MV_HOTEL_RESV' (MAT_VIEW REWRITE)
(Cost=3 Card=80 Bytes=1040)
4??? 2?????? TABLE ACCESS (FULL) OF 'TRANS' (TABLE)
(Cost=3 Card=516 Bytes=3612)
?
??? 即使 MV 可用,該方法也將生成一個(gè)非最優(yōu)的執(zhí)行計(jì)劃。唯一的救濟(jì)就是創(chuàng)建另一個(gè)將所有三個(gè)表連接在一起的 MV。但該方法將導(dǎo)致 MV 的增多,從而大大增加刷新 MV 所需的時(shí)間。
?
??? Oracle 數(shù)據(jù)庫(kù) 10g 第 2 版解決了此問題?,F(xiàn)在,以上查詢將重寫為使用兩個(gè) MV,如執(zhí)行計(jì)劃中所示。
?
Execution Plan
----------------------------------------------------------
0????? SELECT STATEMENT Optimizer=ALL_ROWS (Cost=8 Card=6 Bytes=120)
1??? 0?? SORT (GROUP BY) (Cost=8 Card=6 Bytes=120)
2??? 1???? HASH JOIN (Cost=7 Card=80 Bytes=1600)
3??? 2?????? MAT_VIEW REWRITE ACCESS (FULL) OF 'MV_ACTUAL_SALES' (MAT_VIEW REWRITE)
(Cost=3 Card=80 Bytes=560)
4??? 2?????? MAT_VIEW REWRITE ACCESS (FULL) OF 'MV_HOTEL_RESV' (MAT_VIEW REWRITE)
(Cost=3 Card=80 Bytes=1040)
----------------------------------------------------------
0????? SELECT STATEMENT Optimizer=ALL_ROWS (Cost=8 Card=6 Bytes=120)
1??? 0?? SORT (GROUP BY) (Cost=8 Card=6 Bytes=120)
2??? 1???? HASH JOIN (Cost=7 Card=80 Bytes=1600)
3??? 2?????? MAT_VIEW REWRITE ACCESS (FULL) OF 'MV_ACTUAL_SALES' (MAT_VIEW REWRITE)
(Cost=3 Card=80 Bytes=560)
4??? 2?????? MAT_VIEW REWRITE ACCESS (FULL) OF 'MV_HOTEL_RESV' (MAT_VIEW REWRITE)
(Cost=3 Card=80 Bytes=1040)
?
??? 注意,該執(zhí)行計(jì)劃是如何只使用了 MV 而未使用任何其他基表的。
?
??? 該增強(qiáng)功能在數(shù)據(jù)倉(cāng)庫(kù)中具有顯著的優(yōu)點(diǎn),這是因?yàn)槟槐貫槊總€(gè)可能的查詢創(chuàng)建和刷新 MV。相反,你可以在關(guān)鍵地方創(chuàng)建幾個(gè)沒有太多連接和聚合的 MV,Oracle 將使用它們來(lái)重寫查詢。
通過備份實(shí)現(xiàn)可傳輸表空間
?
??? Oracle8i 中引入的可傳輸表空間為實(shí)現(xiàn)更快的跨數(shù)據(jù)庫(kù)數(shù)據(jù)傳輸提供了迫切需要的支持。使用此特性,您可以只導(dǎo)出表空間的元數(shù)據(jù)、傳輸數(shù)據(jù)文件并將轉(zhuǎn)儲(chǔ)文件導(dǎo)出到目標(biāo)數(shù)據(jù)庫(kù)主機(jī)以及導(dǎo)入元數(shù)據(jù)以將表空間“插入”到目標(biāo)數(shù)據(jù)庫(kù)中。該表空間中的數(shù)據(jù)在目標(biāo)數(shù)據(jù)庫(kù)中隨即可用。該方法解決了數(shù)據(jù)倉(cāng)庫(kù)中曾一度存在的一個(gè)很棘手的問題:快速、高效地跨數(shù)據(jù)庫(kù)移動(dòng)數(shù)據(jù)。
?
??? 但在 OLTP 數(shù)據(jù)庫(kù)中,該條件通常是不可能存在的,因此傳輸表空間也是不可能的。如果 OLTP 數(shù)據(jù)庫(kù)是數(shù)據(jù)倉(cāng)庫(kù)的數(shù)據(jù)源,則您可能始終無(wú)法使用可傳輸表空間加載它。
?
??? 在 Oracle 數(shù)據(jù)庫(kù) 10g 第 2 版中,可以傳輸表空間并從另一個(gè)數(shù)據(jù)源(即備份)中插入它。例如,如果要傳輸表空間 ACCDATA,則可以發(fā)出 RMAN 命令
?
RMAN> transport tablespace accdata
2> TABLESPACE DESTINATION = '/home/oracle'
3> auxiliary destination = '/home/oracle';
2> TABLESPACE DESTINATION = '/home/oracle'
3> auxiliary destination = '/home/oracle';
?
??? 該命令在位置 /home/oracle 中創(chuàng)建一個(gè)輔助實(shí)例,并從其中的備份恢復(fù)文件。此輔助實(shí)例的名稱是隨機(jī)生成的。創(chuàng)建實(shí)例后,該過程將基于目錄創(chuàng)建一個(gè)目錄對(duì)象,并恢復(fù)表空間 ACCDATA(我們正在傳輸?shù)谋砜臻g)的文件 - 所有操作均自動(dòng)完成,您不必發(fā)出任何命令!
?
??? 目錄 /home/oracle 將包含表空間 ACCDATA 的所有數(shù)據(jù)文件、表空間元數(shù)據(jù)的轉(zhuǎn)儲(chǔ)文件以及腳本 impscrpt.sql(最重要的)。該腳本包含將此表空間插入目標(biāo)表空間所必需的所有命令。該表空間并非由 impdp 命令進(jìn)行傳輸,而是通過對(duì) dbms_streams_tablespace_adm.attach_tablespaces 程序包的調(diào)用進(jìn)行傳輸。可以在該腳本中找到所有必要的命令。
?
??? 您可能會(huì)問,如果出現(xiàn)錯(cuò)誤該怎么辦?這種情況下,可以輕松地進(jìn)行診斷。首先,該輔助實(shí)例在 $ORACLE_HOME/rdbms/log 中創(chuàng)建警報(bào)日志文件,以便您可以檢查該日志以查明潛在的問題。其次,在提供 RMAN 命令時(shí),您可以通過發(fā)出 RMAN 命令(該命令將所有輸出置于文件 tts.log 中)將命令和輸出重定向到日志文件
rman target=/ log=tts.log
?
??? 然后,您便可以檢查該文件來(lái)查明故障的確切原因。
?
??? 最后,將把這些文件恢復(fù)到 /home/oracle 的 TSPITR_<SourceSID>_<AuxSID> 目錄中。例如,如果主數(shù)據(jù)庫(kù)的 SID 為 ACCT,RMAN 創(chuàng)建的輔助實(shí)例的 SID 為 KYED,則目錄名為 TSPITR_ACCT_KYED。該目錄還包含兩個(gè)其他子目錄:datafile(用于數(shù)據(jù)文件)和 onlinelog(用于重做日志)。在完成新表空間的創(chuàng)建之前,可以查看該目錄以了解恢復(fù)了哪些文件。(這些文件在該過程結(jié)束時(shí)會(huì)被刪除。)
?
??? 長(zhǎng)期以來(lái),DBA 一直期待著能夠通過 RMAN 備份創(chuàng)建一個(gè)可傳輸?shù)谋砜臻g。但請(qǐng)注意,您是從備份(而不是從聯(lián)機(jī)表空間)中插入傳輸?shù)谋砜臻g。因此,它將不是最新的。
對(duì)已分區(qū)的按索引組織的表實(shí)現(xiàn)快速的分區(qū)分割
?
??? 考慮這樣一種情況:假設(shè)您擁有一個(gè)已分區(qū)的表。月末到了,但您忘了為下一個(gè)月定義分區(qū)。您現(xiàn)在有哪些選擇呢?
?
??? 您唯一的救濟(jì)方法就是將最大值分區(qū)分割為兩個(gè)部分:一個(gè)用于新月份的分區(qū)和一個(gè)新的最大值分區(qū)。但將該方法用于已分區(qū)的按索引組織的表時(shí)將遇到一個(gè)小問題。這種情況下,將先創(chuàng)建物理分區(qū),并將行從最大值分區(qū)移動(dòng)到該分區(qū),這樣將消耗 I/O 和 CPU 周期。
?
??? 在 Oracle 數(shù)據(jù)庫(kù) 10g 第 2 版中,該過程得到顯著簡(jiǎn)化。如下圖所示,假設(shè)您將分區(qū)一直定義到 5 月份,然后已經(jīng)將 PMAX 分區(qū)定義為一個(gè)通用分區(qū)。由于 6 月份沒有特定分區(qū),因此 6 月份數(shù)據(jù)進(jìn)入 PMAX 分區(qū)?;绎@的方框顯示了填充到該段中的數(shù)據(jù)。由于只填充了部分 PMAX 分區(qū),因此您只看到一部分灰色區(qū)域。
?
?
??? 現(xiàn)在,在 6 月 30 日對(duì)分區(qū) PMAX 進(jìn)行分割,以創(chuàng)建 6 月分區(qū)和新的 PMAX 分區(qū)。由于當(dāng)前 PMAX 中的所有數(shù)據(jù)都將進(jìn)入新的 6 月分區(qū),因此 Oracle 數(shù)據(jù)庫(kù) 10g 第 2 版只創(chuàng)建新的最大值分區(qū),并使現(xiàn)有分區(qū)成為新創(chuàng)建的月分區(qū)。這就導(dǎo)致了根本不會(huì)發(fā)生數(shù)據(jù)移動(dòng)(因此沒有“空”的 I/O 和 CPU 周期)。而最好之處在于,ROWID 不會(huì)發(fā)生變化。
通過聯(lián)機(jī)重新定義將 LONG 轉(zhuǎn)換為 LOB
?
??? 如果數(shù)據(jù)倉(cāng)庫(kù)數(shù)據(jù)庫(kù)已經(jīng)存在一段時(shí)間,并且您要處理大型文本數(shù)據(jù),則您可能擁有大量數(shù)據(jù)類型為 LONG 的列。毋庸質(zhì)疑,LONG 數(shù)據(jù)類型在大多數(shù)數(shù)據(jù)操作環(huán)境(如通過 SUBSTR 進(jìn)行搜索)中是沒有用處的。您肯定需要將它們轉(zhuǎn)換為 LOB 列。
?
??? 可以使用 DBMS_REDEFINITION 程序包聯(lián)機(jī)執(zhí)行該操作。但在 Oracle 數(shù)據(jù)庫(kù) 10g 第 2 之前,有一個(gè)很大的限制。
?
??? 將 LONG 列轉(zhuǎn)換為 LOB 列時(shí),您很希望獲得高性能;您需要使該過程盡可能地快。如果將表進(jìn)行了分區(qū),則該過程將跨分區(qū)并行執(zhí)行。但如果未將表進(jìn)行分區(qū),則該過程將串行執(zhí)行,從而可能持續(xù)很長(zhǎng)時(shí)間。
?
??? 幸好,在 Oracle 數(shù)據(jù)庫(kù) 10g 第 2 版中,即使表未分區(qū)也可以在 DBMS_REDEFINITION 程序包內(nèi)部執(zhí)行從 LONG 到 LOB 的聯(lián)機(jī)轉(zhuǎn)換。我們通過一個(gè)示例來(lái)了解該轉(zhuǎn)換的過程。以下是一個(gè)用于保存發(fā)送給客戶的電子郵件的表。由于郵件正文(存儲(chǔ)在 MESG_TEXT 中)通常是較長(zhǎng)的文本數(shù)據(jù),因此已將該列定義為 LONG。
?
SQL> desc acc_mesg
Name????????????????????????????? Null?Type
----------------------------------------- -------- ---------
Name????????????????????????????? Null?Type
----------------------------------------- -------- ---------
?
ACC_NO??????????????????????????????????? NOT NULL NUMBER
MESG_DT?????????????????????????????????? NOT NULL DATE
MESG_TEXT????????????????????????????????????????? LONG
MESG_DT?????????????????????????????????? NOT NULL DATE
MESG_TEXT????????????????????????????????????????? LONG
?
??? 您需要將該列轉(zhuǎn)換為 CLOB。首先,創(chuàng)建一個(gè)結(jié)構(gòu)相同的(最后一列除外,它被定義為 CLOB)空臨時(shí)表。
?
create table ACC_MESG_INT
(
acc_no number,
mesg_dt? date,
mesg_text clob
);
(
acc_no number,
mesg_dt? date,
mesg_text clob
);
?
??? 現(xiàn)在,啟動(dòng)重新定義過程。
?
1? begin
2???? dbms_redefinition.start_redef_table (
3??????? UNAME??????? => 'ARUP',
4??????? ORIG_TABLE?? => 'ACC_MESG',
5??????? INT_TABLE??? => 'ACC_MESG_INT',
6??????? COL_MAPPING? => 'acc_no acc_no, mesg_dt mesg_dt, to_lob(MESG_TEXT) MESG_TEXT'
7? );
8* end;
2???? dbms_redefinition.start_redef_table (
3??????? UNAME??????? => 'ARUP',
4??????? ORIG_TABLE?? => 'ACC_MESG',
5??????? INT_TABLE??? => 'ACC_MESG_INT',
6??????? COL_MAPPING? => 'acc_no acc_no, mesg_dt mesg_dt, to_lob(MESG_TEXT) MESG_TEXT'
7? );
8* end;
?
??? 注意第 6 行,該行已經(jīng)對(duì)列進(jìn)行了映射。前兩列保持不變,但第三列 MESG_TEXT 已被映射,以便通過對(duì)源表的列應(yīng)用函數(shù) TO_LOB 來(lái)填充目標(biāo)表的 MESG_TEXT 列。
?
??? 如果要重新定義的表很大,則需要定期對(duì)源表和目標(biāo)表之間的數(shù)據(jù)進(jìn)行同步。該方法加快了最終同步的速度。
?
begin
dbms_redefinition.sync_interim_table(
uname????? => 'ARUP',?
orig_table => 'ACC_MESG',
int_table? => 'ACC_MESG_INT'
??? );
end;
/
dbms_redefinition.sync_interim_table(
uname????? => 'ARUP',?
orig_table => 'ACC_MESG',
int_table? => 'ACC_MESG_INT'
??? );
end;
/
?
??? 根據(jù)表的大小,您可能需要多次執(zhí)行以上命令。最后,使用以下代碼完成重新定義過程
?
begin
dbms_redefinition.finish_redef_table (
UNAME??????? => 'ARUP',
ORIG_TABLE?? => 'ACC_MESG',
INT_TABLE??? => 'ACC_MESG_INT'
);
end;
/
dbms_redefinition.finish_redef_table (
UNAME??????? => 'ARUP',
ORIG_TABLE?? => 'ACC_MESG',
INT_TABLE??? => 'ACC_MESG_INT'
);
end;
/
?
??? 表 ACC_MESG 已經(jīng)發(fā)生了變化:
?
SQL> desc acc_mesg
Name????????????????????????????? Null?Type
----------------------------------------- -------- ---------
Name????????????????????????????? Null?Type
----------------------------------------- -------- ---------
?
ACC_NO??????????????????????????????????? NOT NULL NUMBER
MESG_DT?????????????????????????????????? NOT NULL DATE
MESG_TEXT
MESG_DT?????????????????????????????????? NOT NULL DATE
MESG_TEXT
?
??? 注意,MESG_TEXT 列現(xiàn)在為 CLOB 而非 LONG。
?
??? 該特性對(duì)于將錯(cuò)誤定義的數(shù)據(jù)結(jié)構(gòu)或原先遺留的數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)換為更容易管理的數(shù)據(jù)類型非常有用。
聯(lián)機(jī)重組單個(gè)分區(qū)
?
??? 假設(shè)您有一個(gè)包含事務(wù)歷史的表 TRANS。該表基于 TRANS_DATE 進(jìn)行分區(qū),每個(gè)季度作為一個(gè)分區(qū)。在正常的業(yè)務(wù)過程中,最新的分區(qū)經(jīng)常更新。某個(gè)季度過后,該分區(qū)上可能沒有很多活動(dòng)了,因此可以將它移動(dòng)到其他位置。但移動(dòng)本身將需要對(duì)表進(jìn)行鎖定,從而拒絕對(duì)分區(qū)的公共訪問。如何在不影響其可用性的情況下移動(dòng)分區(qū)?
?
??? 在 Oracle 數(shù)據(jù)庫(kù) 10g 第 2 版中,可以對(duì)單個(gè)分區(qū)使用聯(lián)機(jī)重新定義。您可以像對(duì)整個(gè)表執(zhí)行重新定義(使用 DBMS_REDEFINITION 程序包)一樣執(zhí)行此任務(wù),但底層機(jī)制并不相同。常規(guī)表是通過對(duì)源表創(chuàng)建物化視圖重新定義的,而單個(gè)分區(qū)是通過交換分區(qū)方法重新定義的。
?
??? 我們來(lái)看一下它的工作原理。以下是 TRANS 表的結(jié)構(gòu):
?
SQL> desc trans
Name????????????????????????????? Null?Type
--------------------------------- -------- -------------------------
TRANS_ID?????????????????????????????????? NUMBER
TRANS_DATE???????????????????????????????? DATE
TXN_TYPE?????????????????????????????????? VARCHAR2(1)
ACC_NO???????????????????????????????????? NUMBER
TX_AMT???????????????????????????????????? NUMBER(12,2)
STATUS????
Name????????????????????????????? Null?Type
--------------------------------- -------- -------------------------
TRANS_ID?????????????????????????????????? NUMBER
TRANS_DATE???????????????????????????????? DATE
TXN_TYPE?????????????????????????????????? VARCHAR2(1)
ACC_NO???????????????????????????????????? NUMBER
TX_AMT???????????????????????????????????? NUMBER(12,2)
STATUS????
?
??? 該表已經(jīng)按如下所示進(jìn)行了分區(qū):
?
partition by range (trans_date)
(
partition y03q1 values less than (to_date('04/01/2003','mm/dd/yyyy')),
partition y03q2 values less than (to_date('07/01/2003','mm/dd/yyyy')),
partition y03q3 values less than (to_date('10/01/2003','mm/dd/yyyy')),
partition y03q4 values less than (to_date('01/01/2004','mm/dd/yyyy')),
partition y04q1 values less than (to_date('04/01/2004','mm/dd/yyyy')),
partition y04q2 values less than (to_date('07/01/2004','mm/dd/yyyy')),
partition y04q3 values less than (to_date('10/01/2004','mm/dd/yyyy')),
partition y04q4 values less than (to_date('01/01/2005','mm/dd/yyyy')),
partition y05q1 values less than (to_date('04/01/2005','mm/dd/yyyy')),
partition y05q2 values less than (to_date('07/01/2005','mm/dd/yyyy'))
)
(
partition y03q1 values less than (to_date('04/01/2003','mm/dd/yyyy')),
partition y03q2 values less than (to_date('07/01/2003','mm/dd/yyyy')),
partition y03q3 values less than (to_date('10/01/2003','mm/dd/yyyy')),
partition y03q4 values less than (to_date('01/01/2004','mm/dd/yyyy')),
partition y04q1 values less than (to_date('04/01/2004','mm/dd/yyyy')),
partition y04q2 values less than (to_date('07/01/2004','mm/dd/yyyy')),
partition y04q3 values less than (to_date('10/01/2004','mm/dd/yyyy')),
partition y04q4 values less than (to_date('01/01/2005','mm/dd/yyyy')),
partition y05q1 values less than (to_date('04/01/2005','mm/dd/yyyy')),
partition y05q2 values less than (to_date('07/01/2005','mm/dd/yyyy'))
)
?
??? 在某個(gè)時(shí)刻,您決定將分區(qū) Y03Q2 移動(dòng)到另一個(gè)表空間 (TRANSY03Q2),該表空間可能位于一個(gè)不同類型的磁盤(一個(gè)慢一點(diǎn)、便宜一點(diǎn)的磁盤)上。為此,請(qǐng)首先確認(rèn)您可以聯(lián)機(jī)重新定義該表:
?
begin
dbms_redefinition.can_redef_table(
uname??????? => 'ARUP',?
tname??????? => 'TRANS',
options_flag => dbms_redefinition.cons_use_rowid,
part_name??? => 'Y03Q2');
end;
/
dbms_redefinition.can_redef_table(
uname??????? => 'ARUP',?
tname??????? => 'TRANS',
options_flag => dbms_redefinition.cons_use_rowid,
part_name??? => 'Y03Q2');
end;
/
?
??? 此處沒有輸出,因此您確認(rèn)可以聯(lián)機(jī)重新定義該表。接下來(lái),創(chuàng)建一個(gè)臨時(shí)表保存該分區(qū)的數(shù)據(jù):
?
create table trans_temp
(
trans_id??????? number,
trans_date date,
txn_type varchar2(1),
acc_no number,
tx_amt number(12,2),
status varchar2(1)
)
tablespace transy03q2
/
(
trans_id??????? number,
trans_date date,
txn_type varchar2(1),
acc_no number,
tx_amt number(12,2),
status varchar2(1)
)
tablespace transy03q2
/
?
??? 請(qǐng)注意,由于表 TRANS 進(jìn)行了范圍分區(qū),因此您已經(jīng)將該表定義為未分區(qū)表。該表在所需的表空間 TRANSY03Q2 中創(chuàng)建。如果表 TRANS 包含一些本地索引,則表示您已經(jīng)對(duì)表 TRANS_TEMP 創(chuàng)建了這些索引(當(dāng)然是創(chuàng)建為未分區(qū)索引)。
?
??? 現(xiàn)在,您就可以啟動(dòng)重新定義過程:
?
begin
dbms_redefinition.start_redef_table(
uname??????? => 'ARUP',?
orig_table?? => 'TRANS',
int_table??? => 'TRANS_TEMP',
col_mapping? => NULL,
options_flag => dbms_redefinition.cons_use_rowid,
part_name??? => 'Y03Q2');
end;
/
dbms_redefinition.start_redef_table(
uname??????? => 'ARUP',?
orig_table?? => 'TRANS',
int_table??? => 'TRANS_TEMP',
col_mapping? => NULL,
options_flag => dbms_redefinition.cons_use_rowid,
part_name??? => 'Y03Q2');
end;
/
?
??? 該調(diào)用有幾個(gè)注意事項(xiàng)。第一,將參數(shù) col_mapping 設(shè)置為 NULL;在單個(gè)分區(qū)重新定義中,該參數(shù)沒有意義。第二,一個(gè)新參數(shù) part_name 指定了要重新定義的分區(qū)。第三,注意其中沒有 COPY_TABLE_DEPENDENTS 參數(shù),該參數(shù)也沒有意義,原因是表本身無(wú)法更改;只移動(dòng)分區(qū)。
?
??? 如果該表很大,此操作可能持續(xù)很長(zhǎng)時(shí)間;因此請(qǐng)?jiān)诓僮鬟^程中對(duì)它進(jìn)行同步。
?
begin
dbms_redefinition.sync_interim_table(
uname????? => 'ARUP',?
orig_table => 'TRANS',
int_table? => 'TRANS_TEMP',
part_name? => 'Y03Q2');
end;
/
dbms_redefinition.sync_interim_table(
uname????? => 'ARUP',?
orig_table => 'TRANS',
int_table? => 'TRANS_TEMP',
part_name? => 'Y03Q2');
end;
/
?
??? 最后,使用以下代碼完成該過程
?
begin
dbms_redefinition.finish_redef_table(
uname????? => 'ARUP',?
orig_table => 'TRANS',
int_table? => 'TRANS_TEMP',
part_name? => 'Y03Q2');
end;
dbms_redefinition.finish_redef_table(
uname????? => 'ARUP',?
orig_table => 'TRANS',
int_table? => 'TRANS_TEMP',
part_name? => 'Y03Q2');
end;
?
??? 此時(shí),分區(qū) Y03Q2 位于表空間 TRANSY03Q2 中。如果該表存在任何全局索引,則它們將被標(biāo)記為 UNUSABLE 并且必須被重新構(gòu)建。
?
??? 單個(gè)分區(qū)重新定義對(duì)于跨表空間移動(dòng)分區(qū)(一個(gè)常見的信息生命周期管理任務(wù))很有用。但顯而易見,其中存在幾個(gè)限制。例如,您無(wú)法在重新定義過程中更改分區(qū)方法(即從范圍更改為散列)或更改表的結(jié)構(gòu)。
逐塊地刪除表
?
??? 您注意到過刪除一個(gè)分區(qū)的表需要多長(zhǎng)時(shí)間嗎?這是因?yàn)槊總€(gè)分區(qū)都是一個(gè)必須刪除的段。在 Oracle 數(shù)據(jù)庫(kù) 10g 第 2 版中,當(dāng)您刪除分區(qū)的表時(shí),分區(qū)將逐個(gè)被刪除。由于每個(gè)分區(qū)是單獨(dú)刪除的,因此所需的資源要比刪除整個(gè)表少。
?
??? 要演示這個(gè)新行為,您可以使用 10046 跟蹤跟蹤該會(huì)話。
?
alter session set events '10046 trace name context forever, level 12';
?
??? 然后,刪除該表。如果查看跟蹤文件,則將看到分區(qū)表刪除的代碼:
?
delete from tabpart$ where bo# = :1
delete from partobj$ where obj#=:1
delete from partcol$ where obj#=:1
delete from subpartcol$ where obj#=:1
delete from partobj$ where obj#=:1
delete from partcol$ where obj#=:1
delete from subpartcol$ where obj#=:1
?
??? 請(qǐng)注意,分區(qū)是按順序刪除的。該方法最大限度地降低了刪除過程中的資源使用率并增強(qiáng)了性能。
?
?
?
?
?