對於我們這個項目來說,數據庫的存取的性能決定了數據提供的性能。優化的大致的原理只有兩個:一是數據分塊存放,便於數據的轉儲和管理;二是中間處理,提高數據提供的速度。
基於上面兩個根本的原理,借助於數據倉庫的概念,列舉數據庫的優化方式:
1. 分區
在數據倉庫中,事實表,索引表,維度表分處於三個不同的表空間當中(在部署的時候,最好是部署到不同的磁盤上)。這樣子做的原因就是便於並發操作,其實數據倉庫和普通的數據庫之間沒有嚴格的界限,主要還是部署上,當然Oracle本身會為數據倉庫中的“事實表”生成“知識庫”等操作,保證更快的數據提供效率,其實可以借助於job和外部程序來調度存儲過程實現。基於這個思路,本項目數據庫中的索引將和事實表分開維護,當然這稱不上分區()。真正的分區是指下面的內容。
分區就是partition/subpartition,對於事實表本身來說,以月為單位作partition掛載到不同表空間上。具體的示例如下:
create table T_LOGNODE_RECORD
(
……
CALLIN_HH NUMBER(2) not null,
CALLIN_DD NUMBER(2) not null,
CALLIN_MONTH NUMBER(2) not null,
CALLIN_YEAR NUMBER(4) not null,
MONTH_MOD NUMBER(1) not null
)
PARTITION BY LIST (MONTH_MOD)
(
PARTITION P0 VALUES (0) TABLESPACE TS0,
PARTITION P1 VALUES (1) TABLESPACE TS1,
PARTITION P2 VALUES (DEFAULT) TABLESPACE TS2
);
上述的代碼中以MONTH_MOD字段作為分區標准,將T_LOGNODE_RECORD分到三個不同的表空間(TS0,TS1,TS2)中,這裡有一個技巧,MONTH_MOD = 月份mod3,這樣子可以不需要人工的維護哪個月分入哪個分區。當然簡單的以月作為分區之後,對於我們當前的數據庫來說還是遠遠不夠的,因為即使是一個月的數據依然是很巨大的,那麼需要我們做SUBPARTITION,做法的示例為:
create table T_LOGNODE_RECORD
(
……
CALLIN_DD NUMBER(2) not null,
MONTH_MOD NUMBER(1) not null
)
PARTITION BY RANGE(MONTH_MOD)
SUBPARTITION BY LIST(CALLIN_DD)
SUBPARTITION TEMPLATE
(
SUBPARTITION SUBP1 values (1),
SUBPARTITION SUBP2 values (2),
SUBPARTITION SUBP3 values (3)
)
(
PARTITION P0 VALUES less than (1),
PARTITION P1 VALUES less than (2),
PARTITION P2 VALUES less than (3)
);
分區方式有三種:RANGE(范圍),LIST(列表分區),HASH(哈希分區),需要解釋的HASH分區,是Oracle調用內置的hash函數來完成對數據的平均分布,保證分區內部的數據量相同,所以只需要制定分區的數量即可。
對於復合分區來說。不是所有分區格式都可以嵌套的,Oracle只支持范圍列表分區,范圍哈希分區,特別的范圍分區可以支持多個字段range by(field1,fIEld2)。對於本項目中用到的分區方式,只有示例中給出的兩種,至於分區的好處,請查看google,^_^。給出操作的sql示例如下:
select * from T_LOGNODE_RECORD partition(P0)
update T_LOGNODE_RECORD partition(P0) t set……
insert語句沒有影響。當然上述的操作可以像普通的Sql語句一樣,不指定分區也可以進行,但在查詢的時候請盡量能確定分區和子分區subpartition,這樣子會讓查詢的速度提高百倍。
可以刪除指定的分區drop或者truncate, EXPORT和IMPORT分區中數據,但是當進行分區的刪除操作的時候,會使全局的索引(index)實效,需要重新建立。
2. 維度
Dimension這個算是Oracle的進階功能了,在PLSQL Developer沒有辦法找到這個對象(但是可以執行語句來生成該對象,但是無法進行後期的維護),只能在Toad中有它的身影。它是表明數據維度字段之間的大小關系。典型的自然是下面的年月日了。
create table DIM_TIME
(
D_YEAR VARCHAR2(4) not null,
D_QUATER VARCHAR2(2) not null,
D_MONTH VARCHAR2(2) not null,
D_DAY DATE not null
)
CREATE DIMENSION DIM_TIME
LEVEL YEAR IS (DIM_TIME.D_YEAR)
LEVEL QUATER IS (DIM_TIME.D_QUATER)
LEVEL MONTH IS (DIM_TIME.D_MONTH)
LEVEL DAY IS (DIM_TIME.D_DAY)
HIERARCHY Y_Q_M_D
(
DAY CHILD OF
MONTH CHILD OF
QUATER CHILD OF YEAR
)
HIERARCHY Y_M_D
(
DAY CHILD OF
MONTH CHILD OF YEAR
)
建立了兩條繼承關系Y_Q_M_D和Y_M_D。當DIM_TIME本身的統計,以及與其他表作聯合查詢然後匯總時,月的匯總數據自動以日匯總的數據為基礎,這樣子大大提高數據匯總的速度。
可以為一張表建立多個維度表,一個維度中可以有多個繼承關系。維度極大程度上提高了數據匯總的速度,使我們這個項目的數據統計分析的利器。
3. 物化視圖(快照)
物化視圖MATERIALIZED VIEW。值得高興的是PLSQL Developer和Toad上同時擁有了這個對象的存在(Toad上可能將這個稱為快照snapshots),但是令人郁悶的事情產生了,在PLSQL Developer上寫成的MATERIALIZED VIEW在Toad中無法識別,看來還要兩個都用才可以。
物化視圖,顧名思義擁有視圖的屬性,它是建立在實際表的基礎上,將實際表中的數據抽象展示,甚至創建的方式都幾乎一樣。但是它成為物化,就意味著它是實體對象,從某種意義上將,它更像一個中間表,可以像表格一樣直接查詢其中的數據(甚至就在table列表中可以看到它),但是它不僅僅是中間表,因為它可以影響原始表的數據,並且Oracle可以自動將針對原始表的查詢匯總語句直接重定向到物化視圖上,來提升查詢的速度。
CREATE MATERIALIZED VIEW MV_LOG_RECORD
REFRESH FORCE
ON DEMAND
ENABLE QUERY REWRITE
AS
SELECT t. NODE_ID, Count(*) as Call_Times
from T_LOGNODE_RECORD t
GROUP BY t. T_LOGNODE_RECORD
上面的SQL語句就已經在T_LOGNODE_RECORD表上建立一個物化視圖。REFRESH的參數 FORCE表示強制刷新,還可以選擇的參數有fast和complete兩種,fast是最快的刷新方式,表示增量刷新(要做到增量刷新,必須在物化視圖中包含唯一性的標示的字符,如主鍵和rowid等,當然即使包含了也不一定可以做到快速刷新,因為我們項目中沒有涉及到要刷新的物化視圖,在此就不多言了);complete表示完整的數據刷新,重新生成物化視圖;force是選擇性的刷新,根據實際的情況來選擇是force還是complete。
ON DEMAND表示刷新觸發的條件。上面的物化視圖只有在用戶發起查詢請求時,才會去刷新數據,即將原始表中的數據取到物化視圖的表中。
總結
在上述的三種方法中,屬於數據庫性能優化的主要手段,當然還有很多可以做的細節的優化,例如盡量不要在你要查詢的字段上面允許為空,盡量將聯合查詢的關系控制為number型等等。
對了,如果做了維度表,做了物化試圖,你去查詢原始表的時候速度一定會增加麼?答案是“不”,你要做的一個重要的事情,就是告訴Oracle,它可以主動的優化查詢,語句如下:
ALTER SESSION SET QUERY_REWRITE_ENABLED=TRUE;
ALTER SESSION SET QUERY_REWRITE_INTEGRITY=TRUSTED;
要把這三個優化的手段寫得很清楚,這麼簡單的幾句話是不可以的。只是讓大家了解一個項目的數據庫的部署和稍微高階一點的優化方式。