程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> Oracle數據庫 >> Oracle數據庫基礎 >> Oracle 11g新特性:更加靈活的分區策略

Oracle 11g新特性:更加靈活的分區策略

編輯:Oracle數據庫基礎
 前不久,曾經接手一個性能調優案例:這是一個報表系統,其基礎數據主要存儲於三張表中。表的大小已經很大了,最大一張接近100G。在生成報表時需要長時間才能返回結果,一些online查詢甚至經常timeout。表中存儲的是2萬多個公司的數據,報表的生成也是以公司為單位的,因此,這一調優方案的思路比較明確:將表按公司分區。但是,這中間卻存在一些麻煩:每個公司的數據並不是均衡的。其中近200家公司屬於VIP用戶,他們的數據量最大,每個公司差不多是十幾萬到幾十萬的數據量,其總量占了全部數據的30%左右;而其它非VIP用戶的數據基本上每個都在1萬以內。而我們的主要目標就是要優先保證VIP用戶獲取到最佳的性能(由於其數據量,當前最大的性能問題恰恰就出在這些VIP用戶上)。因此,我們提出了2中分區方案:

  基於Company Id的Hash分區;

  基於Company Id的List分區;

  但是,這兩種方案各有優缺點:

  對於Hash分區,分區的大小更加均衡,因而性能也更加均衡。但是,可能出現一些無法控制的極端現象:Hash分區僅僅是對Company Id使用Hash函數進行分組,它能做到每個分區分配基本相當數量的Company Id,但是每個Company Id對應的數據量並不考慮在內,因此可能出現某些分區集中的都是VIP數據或者都是非VIP數據,造成分區過大或過小;另外一個缺點就是我們很難直接干預某個公司的性能。例如,可能有某個非VIP用戶成為了VIP用戶,其數據量激增,它又正好處於一個大的分區上,這時,我們很難將其從這個分區剝離出來,除非它所在分區正好出在一個即將分裂的分區上。

  對於List分區,VIP用戶的性能能夠得到保證。我們可以將每個VIP用戶單獨存儲在一個分區上,但是,不可能將非VIP用戶單獨存儲開(不僅增加維護難度,且增加整個表的大小),只能將非VIP用戶存儲在幾個分區上。但是這樣還是造成DDL語句非常復雜,並且非VIP的分區很大(每個都在10G左右,而VIP分區最大才200M)。

  由於List分區更加接近我們的優化目的,最終還是采用了List分區。

  其實,期間我們曾經考慮過使用復合分區。在10g中(我們的生產庫是10g),僅支持2種復合分區:Range-List和Range-Hash。我們的解決方案是:為表增加一個數字類型的ID字段,VIP用戶對應的數字大於100,000,非VIP用戶的ID小於100,000。每個VIP用戶被單獨放置在一個Range分區中,所有非VIP用戶被放置在一個Range分區中,然後再對非VIP分區通過Hash劃分子分區。這樣,即能保證VIP用戶的性能,也能均衡非VIP用戶的性能。但是,由於這種方案需要增加一個非業務的字段,以及其它一些原因,最終被否決了。

  到11g中,Oracle的分區策略更加靈活了。首先,11g支持更多方式的組合分區,除10g支持的兩種之外,還支持Range-Range、List-Range、List-List、List-Hash的組合分區策略。對於我們上述這個案例,就可以通過List-Hash的組合分區來解決。以下就是一個List-Hash分區的演示:

  SQL代碼

  SQL> create table par_test

  2 partition by list (owner)

  3 subpartition by hash (owner)

  4 store in (example)

  5 (partition p1 values ('SYS'),

  6 partition p2 values ('PUBLIC'),

  7 partition def values (default)

  8 subpartitions 4

  9 )

  10 as select * from dba_objects

  11 /

  Table created.

  SQL> analyze table par_test compute statistics;

  Table analyzed.

  SQL> select partition_name, subpartition_name, num_rows, blocks from dba_tab_subpartitions

  2 where table_name = 'PAR_TEST';

  PARTITION_NAME SUBPARTITION_NAME NUM_ROWS BLOCKS

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

  P2 SYS_SUBP154 26604 434

  DEF SYS_SUBP158 4529 70

  DEF SYS_SUBP157 2783 45

  DEF SYS_SUBP156 2422 39

  DEF SYS_SUBP155 2854 47

  P1 SYS_SUBP153 29770 437

  6 rows selected. 順便再提一下11g新增的其他分區策略。

  針對Range Partition,11g有了一種更加靈活的方式:Interval Partition。例如,我們一些分區表是依賴於時間做的范圍分區:每個月的數據存放到一個分區中。隨著數據的增長,還需要有一個作業來增加新的分區以滿足上述策略。而在11g中,通過Interval Parition,就無需這中人為的維護作業了,Oracle會為新的數據自動增加分區:

  SQL代碼

  SQL> create table par_test2 (a number, b date)

  2 partition by range (b)

  3 interval (numtoyminterval(1,'MONTH'))

  4 store in (example)

  5 (

  6 partition values less than (to_date('2009-09-01','yyyy-mm-dd'))

  7 )

  8 ;

  Table created.

  SQL> insert into par_test2 values(1, sysdate);

  1 row created.

  SQL> commit;

  Commit complete.

  SQL> select partition_name, high_value from dba_tab_partitions

  2 where table_name = 'PAR_TEST2';

  PARTITION_NAME HIGH_VALUE

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

  SYS_P164 TO_DATE(' 2009-09-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA '

  SQL> insert into par_test2 values(1, to_date('2009-10-01','yyyy-mm-dd'));

  1 row created.

  SQL> commit;

  Commit complete.

  SQL> select partition_name, high_value from dba_tab_partitions

  2 where table_name = 'PAR_TEST2';

  PARTITION_NAME HIGH_VALUE

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

  SYS_P164 TO_DATE(' 2009-09-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA '

  SYS_P165 TO_DATE(' 2009-11-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA '

  SQL> insert into par_test2 values(1, sysdate - interval '1' MONTH);

  1 row created.

  SQL> commit;

  Commit complete.

  SQL> select * from par_test2;

  A B

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

  1 31-AUG-09

  1 31-JUL-09

  1 01-OCT-09

  SQL> select partition_name, high_value from dba_tab_partitions

  2 where table_name = 'PAR_TEST2';

  PARTITION_NAME HIGH_VALUE

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

  SYS_P164 TO_DATE(' 2009-09-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA '

  SYS_P165 TO_DATE(' 2009-11-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA '

  從上述例子可以注意到,由於Range Partition的表達式的比較操作符是Less Than,因此,Interval Partition只會對超出(分區最大Partition Key值+Interval值)的數據創建新分區。同理,在指定Interval分區時,就不能再指定less than (MAXVALUE)了,否則Interval分區就沒有意義了。

  11g中還引入了一種新的分區策略:關聯分區。在10g和之前版本,我們在做分區表時可能會遇到這樣的問題:一個主表和多個子表都需要做分區,而分區所基於的關鍵值是只存在與主表中的一個字段,這時,我們就需要將這個字段冗余到子表當中去才能實現主表、子表采用相同的分區策略。11g中,這個問題可以通過關聯分區解決了:

  SQL代碼

  SQL> create table par_main (pid number primary key, crt_date date)

  2 partition by range (crt_date)

  3 (

  4 partition values less than (to_date('2009-09-01','yyyy-mm-dd')),

  5 partition values less than (maxvalue)

  6 );

  Table created.

  SQL> create table par_child (

  2 cid number primary key,

  3 pid number not null,

  4 constraint par_main_fk foreign key (pid) references par_main(pid)

  5 )

  6 partition by reference (par_main_fk);

  Table created.

關聯分區中要注意一點:關聯分區中,父表不能為Interval分區。

11g,還新增了以虛字段(Virtual Column)為分區鍵的分區方式。以下例子中,store就是一個虛列,它的數值並沒有實際存儲在表中,而是由其它兩個字段buy和sell計算得出,我們可以以它作為分區鍵建立分區:

SQL代碼
SQL> create table par_vc(
2 itemid number,
3 buy number,
4 sell number,
5 store number as (buy - sell)
6 )
7 partition by range (store)
8 (
9 partition values less than (1000),
10 partition values less than (2000),
11 partition values less than (maxvalue)
12 );

Table created.

11g中引入的最後一種特殊的分區是system partition。對於普通分區,必須有一個或多個字段做為分區鍵來建立分區,而system分區就沒有這種要求——僅僅是將表的數據分別存儲在多個段中,而你在插入數據時,需要指定數據存儲在哪個分區上:

SQL代碼
SQL> create table par_sys (a number, b varchar2(10))
2 partition by system
3 (
4 partition p1,
5 partition p2
6 );

Table created.

SQL> insert into par_sys values (1, 'a');
insert into par_sys values (1, 'a')
*
ERROR at line 1:
ORA-14701: partition-extended name or bind variable must be used for DMLs on
tables partitioned by the System method


SQL> insert into par_sys partition(p1) values (1, 'a');

1 row created.

從上面的例子可以注意到:在插入數據時,如果沒有指定分區就會拋錯。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved