數據庫開發管理規範-PARTITION TABLE(分區)

隨着時間的推移及公司業務不斷髮展,各系統業務數據愈來愈龐大,數據庫中的數據量不斷膨脹,致使業務系統的查詢效率低下,運行緩慢,且龐大的數據量也不易管理維護。基於這種狀況,一個有效的解決方案是使用分區表和分區索引。但因爲以往分區表的應用較少,對於大部分IT人員來講,它們仍是比較陌生的。爲了指導開發人員合理的建立和使用分區表及分區索引,特創建此規範。


1.2 適用範圍#

本規範適用於開發人員、數據庫管理人員。

1.3 術語和縮略語#

此處請填入文檔中的專業術語及解釋。
序號 術語/縮略語 全稱和說明
1 分區表 (PARTITION TABLE) 分區表將數據分紅被稱爲分區甚至子分區的更小的、能夠單獨管理的塊,而這些塊共享相同的邏輯屬性
2 全局索引 創建在整個表上而非單獨分區上的索引
3 本地索引 創建在分區或者子分區上的索引

1.四、簡介#

分區表容許將數據分紅被稱爲分區甚至子分區的更小、更好管理的塊。索引也能夠這麼分區。每一個分區能夠被單獨管理,能夠不依賴於其餘分區而單獨發揮做用,所以對提升系統的可用性和性能有重要的意義。


二、設計要求規範#

2.1 分區依據#

2.1.1 分區的目的是爲了性能優化或數據管理(數據歸檔、大批量數據刪除等);在解決這其中一個需求的前提下,且表大於2GB,才考慮作分區。

2.1.2 建議1:分區是優化全表掃描的手段,當查詢返回的結果集超過表的總數據量的10%,Oracle會傾向於走全表掃描。爲下降I/O的消耗,可考慮作分區。
對於已經使用索引而且結果集較小的狀況,作分區不必定能提高性能。分區表並非用於,將一個大表走索引變成小表走索引。若是返回的結果集有必定的數據量,能將查詢的數據量集中在一個分區中進行掃描,效率會有必定的提升。 sql

2.2 分區設計規範#

2.2.1 分區設計中須要特別關注,最終的數據要均勻分佈到每一個分區,偏大或偏小的比例不超過15%。
這樣能夠提升效率,尤爲是在並行查詢時。

2.2.2 若是一個字段的UPDATE在一週內超過該表的總UPDATE行數的10%(參考Oracle的定義:當數據的10%被修改後,其Statistics被認爲是失真的),則該字段不容許做爲分區列。
若是分區列的值被修改,頻繁引發ROW MOVEMENT,會影響分區的性能,由於UPDATE操做會引發對原分區做DELETE操做,另外一個分區做INSERT操做,從而影響性能。
獲取UPDATE行數的方法參考附錄D。 數據庫

2.2.3 分區越多,ORACLE維護成本越高,管理越複雜。所以非哈希分區不要超過32個分區,哈希分區不要超過64個分區。
Hash分區主要用於併發查詢和解決熱點問題。考慮到主機CPU的併發處理能力,64個Hash分區已經足夠。 性能優化

2.2.4 建議:在實際的應用中,能夠根據須要用不一樣的粒度進行分區。例如,同一張表中,當年數據可按月份進行分區,當年以前的數據可按年進行分區。 架構

2.2.5 建議:列表分區一般用於應用中能夠經過某一個字段來平均分佈工做負載。例如RAC中,分節點跑不一樣的應用,同一張表上的數據也對應到不一樣的分區上,減小不一樣節點上的數據傳輸開銷。還能夠對應應用的分區,達到邏輯上分離數據庫的做用,例如根據某一個字段的狀態進行數據的管理、歸檔等。注意,一些狀態字段若是變化很是頻繁,會致使大量的row movement,影響性能,所以對於狀態字段變化很是多的狀況不適用。 併發

2.2.6 建議:哈希分區的優勢:每一個分區的數量平均,分區並行時效率高,根據hash值插入數據,能夠將數據插入到不一樣的塊,在並行度高時有利於提升效率。哈希分區還能夠用於解決熱塊的問題。當沒法採用範圍分區或列表分區時,能夠考慮採用哈希分區。 less

2.2.7 建議:複合分區的選擇,從性能、數據管理(數據歸檔)、資源分佈(應用的分區)、數據分佈等多個方面進行選擇。若是側重於從此對按時間進行數據歸檔,則能夠採起range-list分區方式;若是側重於資源分佈,則能夠採用list-range分區方式。若是兩方面都不太側重,則能夠再關注下表的數據分佈,根據數據的分佈狀況來決定採用何種複合分區方式。 jsp

2.2.8 生產系統中違規超過規範限定的分區個數時,可參照下列思路考慮改造方案:
1)對於非Hash分區,要考慮達到32個分區的使用現狀,是活躍數據已經達到最大限制,仍是存在非活躍數據歸檔後沒有及時合併或刪除空的分區。活躍數據已經達到32個分區的狀況,請架構師一塊兒參與評估,確認合理後能夠提交例外申請;其餘狀況,在性能影響評估後,考慮下發版本merge分區或者drop掉空的分區。
2)對於Hash分區,因爲增長或減小分區數量,均可能涉及到數據的從新分佈,須要請架構師一塊兒分析分區數量大於64的緣由,確認分區合理能夠申請例外,對於不合理的分區,能夠在性能影響評估後,使用語句ALTER TABLE schema.tablename COALESCE PARTITION來減小分區數量。
性能

2.3 各類分區方式相關規範#

2.3.1 分區列必須爲NOT NULL。
分區列不能爲NULL,必須爲NOT NULL。若是有NULL 值插入的話會進入MAXVALUE或default對應的分區,影響將來對MAXVALUE或default分區的split,以及在null改成not null值時產生row movement。

2.3.2 除採用11g的interval方式分區之外,範圍分區方法都須要指定maxvalue分區。
由於若是數據在分區字段上插入或者更新的值超出全部分區範圍,除非指定了maxvalue分區,不然該操做將會報錯。例如: 優化

CREATE TABLE epcis_sales (  acctount_no  NUMBER(5),  person       VARCHAR2(30),  sales_amount NUMBER(8),  department_code    varchar2(10))  PARTITION BY RANGE (department_code) (  PARTITION epcis_sales_201_pt VALUES LESS THAN (201) tablespace t1_data,  PARTITION epcis_sales_202_pt VALUES LESS THAN (202) tablespace t2_data,  ...  PARTITION epcis_sales_208_pt VALUES LESS THAN (208) tablespace t8_data, PARTITION epcis_sales_max_pt VALUES LESS THAN (maxvalue) tablespace             t_data);
如上面的分區表,若是插入一條記錄:
Insert into epcis_sales (acctount_no,person,sales_amount,department_code) values  (10001,’test’,200,‘209’);
那麼這條記錄將被存放在epcis_sales_max_pt這個分區中。若是表上沒有這個maxvalue分區,插入該數據時,數據庫將會報錯。爲了不這種狀況,採用範圍分區方法時,必須指定maxvalue的分區。

2.3.3 使用範圍分區和列表分區時必須使用 ENABLE ROW MOVEMENT。
當表中一行數據的分區字段的值被更新時,若是容許該數據移動到新的分區上,則在建立分區表時,需使用ENABLE ROW MOVEMENT,默認是不容許移動的,若是此時更新分區列,致使分區字段的值超出該分區的範圍時,將會報錯。所以,爲了不出現這種狀況,必須容許記錄移動;例如: ui

CREATE TABLE epcis_sales (  acctount_no  NUMBER(5),  person       VARCHAR2(30),  sales_amount NUMBER(8),  department_code    varchar2(10))  PARTITION BY RANGE (department_code) (  PARTITION epcis_sales_201_pt VALUES LESS THAN (201) tablespace t1_data,  PARTITION epcis_sales_202_pt VALUES LESS THAN (202) tablespace t2_data,  ...  PARTITION epcis_sales_208_pt VALUES LESS THAN (208) tablespace t8_data,  PARTITION epcis_sales_max_pt VALUES LESS THAN (maxvalue) tablespace t_data) ENABLE ROW MOVEMENT;


2.3.4 使用列表分區時,分區中必須包含default分區。
例如,有如下分區表:

CREATE TABLE sales_by_region ( Dept_no number, Dept_name varchar2(20), quarterly_sales number(10,2), state varchar2(2)) PARTITION BY LIST(state) ( PARTITION sales_by_region_east_pt VALUES('SH','NJ') tablespace t1_data, PARTITION sales_by_region_south_pt VALUES('GZ','SZ','HN') tablespace t2_data, PARTITION sales_by_region_west_pt VALUES('XN','NN') tablespace t3_data, PARTITION sales_by_region_north_pt VALUES('SY','LZ') tablespace t4_data) ENABLE ROW MOVEMENT;
若是向上表中插入一列state=’AA’的數據,數據庫將會報錯,由於該分區表並無指定state的值爲’AA’時的分區。爲了不出現報錯的狀況,在列表分區時,必須包含default分區。
示例:
CREATE TABLE sales_by_region ( Dept_no number, Dept_name varchar2(20), quarterly_sales number(10,2), state varchar2(2)) PARTITION BY LIST(state) ( PARTITION sales_by_region_east_pt VALUES('SH','NJ') tablespace t1_data, PARTITION sales_by_region_south_pt VALUES('GZ','SZ','HN') tablespace t2_data, PARTITION sales_by_region_west_pt VALUES('XN','NN') tablespace t3_data, PARTITION sales_by_region_north_pt VALUES('SY','LZ') tablespace t4_data, PARTITION sales_by_region_other_pt VALUES(DEFAULT)  tablespace t5_data) ENABLE ROW MOVEMENT;
這樣,當插入state=’AA’的數據時,該記錄會映射到sales_by_region_other_pt分區。

2.3.5 列表分區中分區列值必須區分大小寫。
列表分區中的分區列值是大小寫敏感的,上面的例子中,若是插入下列值:

insert into sales_by_ragion values(100,’test’,200,’sh’);
該記錄不會映射到sales_by_region_east_pt分區,而是映射到sales_by_region_other_pt分區,由於大小寫敏感的緣故,數據庫認爲sh和SH是不一樣的值。

2.3.6 使用哈希分區時,分區數量必需要是2的N次方冪。
例如2,4,8,16,32,64。

2.4分區表索引設計規範#

2.4.1 分區表索引設計規範
1. 若是分區列是索引的子集(不管是prefix仍是non-prefix),則創建local index。不然,請參照準則2。
2. 若是索引是惟一的,則創建global index。不然,請參照準則3。
3. 建議:若是基於性能考慮,且應用是OLTP類型,用戶須要更快的響應時間,則創建global index;若應用是DSS類型,且用戶更注重吞吐量,則創建local index。若是基於維護性考慮,則創建local index.

Local 索引較 global 索引具備更高的可維護性;local index能對不一樣的分區使用並行訪問,能夠提升數據庫單位時間內的整體吞吐量;local prefixed index使得優化器能夠直接根據分區作pruning限制至特定的分區,能夠適當引入OLTP系統;global index更適於 OLTP類型應用;對於等於或小範圍查詢較多的表,global index有比local index更快的響應時間。

2.5分區表使用規範#

2.5.1 若表作了範圍分區,範圍查詢要限制在一個分區內,不能跨分區;分區的寬度要按查詢的範圍作合理的設置;跨分區的狀況,能夠經過從新定義分區寬度,或改寫SQL用並行來提升性能。

2.5.2 若表作了列表和哈希分區,分區字段不容許作範圍查詢。

2.5.3 建議:在分區表與分區表關聯的查詢中,若是關聯條件正好是分區字段的話,在執行計劃中會產生partition join的方式,更加提升效率。

附錄#

附錄A 分區技術資料#

分區技術資料
表分區方法      ORACLE提供有以下幾種分區方法:      a:範圍分區(range partitioning);      b:哈希分區(hash partitioning);      c:列表分區(list partitioning);      d:範圍-哈希組合分區(composite range-hash partitioning);      e:範圍-列表組合分區(composite range-list partitioning);      ORACLE版本支持的分區策略以下: ORACLE版本        支持的分區策略 Oracle 8        範圍分區 Oracle 8i       哈希分區、範圍-哈希複合分區 Oracle 9i       範圍分區、哈希分區、範圍-哈希組合分區/列表分區 Oracle 9i R2    範圍分區、哈希分區、範圍-哈希組合分區、列表分區、範圍-列表組合分區  範圍分區方法 使用原則及基本語法 當數據能夠被均勻的劃分紅邏輯範圍時,如年度中的月份,就能夠用這種類型的分區。數據在整個範圍中能被均等地劃分時性能最好。 若是範圍分區會因爲不均等的劃分而致使分區在大小上明顯不一樣時,就須要考慮其餘分區方法。 建立範圍分區時,必須指定: 分區方法:範圍,分區列 ,標識分區邊界的分區描述; 示例: CREATE TABLE sales (  acctount_no  NUMBER(5),      person       VARCHAR2(30),  sales_amount NUMBER(8),  sell_date    CHAR(8))      PARTITION BY RANGE (sell_date) (  PARTITION pt_sales_2007_q1 VALUES LESS THAN (20070401),  PARTITION pt_sales_2007_q2 VALUES LESS THAN (20070701),  ...      PARTITION pt_sales_2007_q4 VALUES LESS THAN (20080101),  PARTITION pt_sales_2008     VALUES LESS THAN (maxvalue)); 範圍分區表的每一個分區都被存儲在單獨的段中,互不影響,且每一個分區均可指定單獨的 數據表空間。  哈希分區方法 若是數據不那麼容易進行範圍分區,但爲了性能和管理的緣由又想分區時,就使用哈希分區方法。 哈希分區提供了一種在指定數量的分區中均等地劃分數據的方法。建立和使用哈希分區會提供了一種很靈活的放置數據的方法, 進而來影響可用性和性能。 爲了建立哈希分區,須要指定: 分區方法:哈希,分區列,分區數量或單獨的分區描述 示例: CREATE TABLE test (  id NUMBER,  name VARCHAR2(60))  PARTITION BY HASH (id)   PARTITION pt_ test_1,  PARTITION pt_ test_2,  PARTITION pt_ test_3,  PARTITION pt_ test_3); 該示例建立了一個哈希分區表,分區列是id,建立4個分區並指定每一個分區的名稱; 使用哈希分區,用戶不能控制哪些數據放在哪些分區裏,由數據庫自動控制的。 列表分區方法   使用原則及基本語法 當須要明確控制如何將數據行映射到分區時,就使用列表分區方法。列表分區能夠很是天然地將無序的和不相關的數據集進行分組和組織到一塊兒。 與範圍分區和哈希分區所不一樣,列表分區不支持多列分區,分區鍵就只能由表的一個單獨的列組成。 當建立列表分區時,必須指定: 分區方法:列表,分區列,分區描述――每一個描述指定一串文字值(值的列表), 示例: CREATE TABLE sales_by_region ( deptno number, deptname varchar2(20), quarterly_sales number(10,2), state varchar2(2)) PARTITION BY LIST(state) ( PARTITION pt_sales_by_region_east VALUES('SH','NJ'), PARTITION pt_sales_by_region_south VALUES('GZ','SZ','HN'), PARTITION pt_sales_by_region_west VALUES('XN','NN'), PARTITION pt_sales_by_region_north VALUES('SY','LZ')) ENABLE ROW MOVEMENT;         包含default分區 若是向上表中插入一列state=’AA’的數據,數據庫將會報錯,由於該分區表並無指定state的值爲’AA’時的分區。 爲了不出現報錯的狀況,在列表分區時,必須包含default分區。 示例: CREATE TABLE sales_by_region ( deptno number, deptname varchar2(20), quarterly_sales number(10,2), state varchar2(2)) PARTITION BY LIST(state) ( PARTITION pt_sales_by_region_east VALUES('SH','NJ'), PARTITION pt_sales_by_region_south VALUES('GZ','SZ','HN'), PARTITION pt_sales_by_region_west VALUES('XN','NN'), PARTITION pt_sales_by_region_north VALUES('SY','LZ'), PARTITION pt_sales_by_region_other VALUES(DEFAULT) ) ENABLE ROW MOVEMENT;         這樣,當插入state=’AA’的數據時,該記錄會映射到pt_sales_by_region_other分區。 大小寫敏感          list分區中的分區列表值是大小寫敏感的,上面的例子中,若是插入下列值:          insert into sales_by_ragion values(100,’test’,200,’sh’);          該記錄不會映射到pt_sales_by_region_east分區,而是映射到pt_sales_by_region_other分區, 由於大小寫敏感的緣故,數據庫認爲shSH是不一樣的值。 範圍-哈希組合分區方法 該分區方法是在分區中使用範圍分區方法分區,而在子分區中則使用哈希分區方法。它改善了範圍分區及其數據放置的可管理性, 並提供了哈希分區的並行機制的優勢。 當建立組合時,要指定: 分區方法:範圍 分區列 標識分區邊界的分區描述 子分區方法:哈希 子分區列 每一個分區的子分區數量,或子分區的描述 示例: CREATE TABLE test ( Equip_no   NUMBER, Equip_name VARCHAR(32), price     NUMBER) PARTITION BY RANGE (equip_no) SUBPARTITION BY HASH(equip_name)  ( PARTITION pt_test_1000 VALUES LESS THAN (1000)  (           SUBPARTITION pt_test_1000_h1,           SUBPARTITION pt_test_1000_h2,           SUBPARTITION pt_test_1000_h3,           SUBPARTITION pt_test_1000_h4), PARTITION p2 VALUES LESS THAN (2000) (           SUBPARTITION pt_test_2000_h1,           SUBPARTITION pt_test_2000_h2,           SUBPARTITION pt_test_2000_h3,           SUBPARTITION pt_test_2000_h4), PARTITION p3 VALUES LESS THAN (MAXVALUE) (           SUBPARTITION pt_test_max_h1,           SUBPARTITION pt_test_max_h2,           SUBPARTITION pt_test_max_h3,           SUBPARTITION pt_test_max_h4) ) ENABLE ROW MOVEMENT; 在本列中,建立了3個範圍分區,每一個範圍分區包含4個子分區。 範圍-列表組合分區 如範圍-哈希組合分區方法同樣,範圍-列表組合分區也是有兩層分區:第一層分區基於一個範圍值,第二層分區基於一系列離散值。 這種組合分區兼有範圍分區和列表分區的優勢。 當建立組合時,要指定: 分區方法:範圍 分區列 標識分區邊界的分區描述 子分區方法:列表 子分區列 子分區描述,爲每一個子分區指定一系列離散值; 示例: CREATE TABLE regional_sales ( depment_no number, item_no varchar2(20), sale_date date, sale_amount number, state varchar2(2)) PARTITION BY RANGE (sale_date) SUBPARTITION BY LIST (state) (PARTITION regional_sales_1999 VALUES LESS THAN (TO_DATE('1999-04-01','YYYY-MM-DD'))( SUBPARTITION pt_regional_sales_q1_east VALUES ('OR', 'WA'), SUBPARTITION pt_regional_sales_q1_south VALUES ('AZ', 'UT', 'NM'), SUBPARTITION pt_regional_sales_q1_west VALUES ('NY', 'VM', 'NJ'), SUBPARTITION pt_regional_sales_q1_north VALUES ('FL', 'GA'), SUBPARTITION pt_regional_sales_q1_other VALUES(DEFAULT) ), PARTITION q2_1999 VALUES LESS THAN ( TO_DATE('1999-07-01','YYYY-MM-DD'))( SUBPARTITION pt_regional_sales_q2_east VALUES ('OR', 'WA'), SUBPARTITION pt_regional_sales_q2_south VALUES ('AZ', 'UT', 'NM'), SUBPARTITION pt_regional_sales_q2_west VALUES ('NY', 'VM', 'NJ'), SUBPARTITION pt_regional_sales_q2_north VALUES ('FL', 'GA') SUBPARTITION pt_regional_sales_q2_other VALUES(DEFAULT) ), PARTITION q3_1999 VALUES LESS THAN (maxvalue)( SUBPARTITION pt_regional_sales_q3_east VALUES ('OR', 'WA'), SUBPARTITION pt_regional_sales_q3_south VALUES ('AZ', 'UT', 'NM'), SUBPARTITION pt_regional_sales_q3_west VALUES ('NY', 'VM', 'NJ'), SUBPARTITION pt_regional_sales_q3_north VALUES ('FL', 'GA'), SUBPARTITION pt_regional_sales_q3_other VALUES(DEFAULT))) ENABLE ROW MOVEMENT; 分區索引         索引能夠像表同樣可以被分區,分區或者非分區索引都可以使用在分區或者非分區表上。分區索引有兩種類型:         a:全局索引;         b:本地索引;  全局索引(global index            全局索引又可分爲全局分區索引(global partitioned index)和全局非分區索引(global non-partitioned index),示例:        建立全局分區索引:        CREATE INDEX ix_sales_month ON sales(sales_month) GLOBAL PARTITION BY RANGE(sales_month)( PARTITION ix_sales_month_2 VALUES LESS THAN (2), PARTITION ix_sales_month_3 VALUES LESS THAN (3), PARTITION ix_sales_month_4 VALUES LESS THAN (4), PARTITION ix_sales_month_5 VALUES LESS THAN (5), PARTITION ix_sales_month_6 VALUES LESS THAN (6), PARTITION ix_sales_month_7 VALUES LESS THAN (7), PARTITION ix_sales_month_8 VALUES LESS THAN (8), PARTITION ix_sales_month_9 VALUES LESS THAN (9), PARTITION ix_sales_month_10 VALUES LESS THAN (10), PARTITION ix_sales_month_11 VALUES LESS THAN (11), PARTITION ix_sales_month_12 VALUES LESS THAN (12), PARTITION ix_sales_month_13 VALUES LESS THAN (MAXVALUE)) ;     建立全局非分區索引(即普通索引): CREATE INDEX ix_sales_month ON sales(sales_month) GLOBAL;            須要注意的是,全局分區索引必須是前綴索引(Prefixed index),即索引的分區鍵第一個字段與表的分區列是相同的, ORACLE不支持非前綴的全局分區索引,而全局非分區索引沒有這個限制。 本地索引(local index) 本地索引必須是創建在分區表上。本地索引也分爲前綴索引和非前綴索引, 索引分區鍵第一個字段與表的分區列相同的爲前綴索引,不然爲非前綴索引。 示例: 若有下列分區表:      CREATE TABLE department_define (      department_code NUMBER,      department_name VARCHAR2(10),      remark          VARCHAR2(14))      PARTITION BY RANGE (department_code) (      PARTITION pt_department_define_30 VALUES LESS THAN (30),      PARTITION pt_department_define_max VALUES LESS THAN (MAXVALUE)) ENABLE ROW MOVEMENT; 在該表上建立本地前綴索引: CREATE  INDEX  ix_department_define_code ON department_define(department_code) LOCAL TABLESPACE PCISBASE_IDX ; 建立本地非前綴索引:     CREATE  INDEX  ix_department_define_name  ON department_define (department_name) LOCAL TABLESPACE PCISBASE_IDX ; 自動更新全局索引         對分區表的許多表維護操做會使全局索引不可用(標記成UNUSABLE)。那麼,就必須重建整個全局索引。 Oracle容許在用於維護操做的ALTER TABLE語句中,指定UPDATE GLOBAL INDEXES。指定這個子句也就告訴Oracle,當它執行維護操做的DDL語句時, 更新全局索引。 在對分區表的某個分區做維護操做同時自動更新全局索引,會致使DDL要執行更長的時間;與先不更新索引而執行DDL,再重建索引所花的時間相比較, 誰具備優點也很難斷言。一個適用的規則是,若是維護操做的分區大小小於表大小的5%,則自動更新索引更快點。 示例: Alter table sales drop partition department_define_30 update global indexes; 以下操做支持UPDATE GLOBAL INDEXES子句: ADD PARTITION|SUBPARTITION(僅限於哈希分區) DROP PARTITION EXCHANGE PARTITION|SUBPARTITION MERGE PARTITION MOVE PARTITION|SUBPARTITION SPLIT PARTITION TRUNCATE PARTITION|SUBPARTITION COALESCE PARTITION|SUBPARTITION

附錄B 11G分區的新特性#

1. Interval Partitioning
11g中生成新分區的這項工做能夠交由Oracle自動完成,指定須要Oracle自動建立分區的間隔時間,下面這個例子是1個月,而後至少建立一個基本分區。
例子中20110101以前的全部數據都在epcis_sales_2010_pt分區中,之後每月的數據都會存放在Oracle自動建立的一個新分區中。
CREATE TABLE epcis_sales 
(
 acctount_no  NUMBER(5),
 person       VARCHAR2(30),
 sales_amount NUMBER(8),
 department_code    varchar2(10),
 sale_time date)
PARTITION BY RANGE (sale_time) 
INTERVAL(NUMTOYMINTERVAL(1, 'month')) 
(PARTITION epcis_sales_2010_pt VALUES LESS THAN (TO_DATE('20110101', 'yyyymmdd'))); 


2. System Partitioning
在這個新的類型中,咱們不須要指定任何分區鍵,數據會進入哪一個分區徹底由應用程序決定,
實際上也就是由SQL來決定,即在Insert語句中能夠指定插入哪一個分區了。
因爲System Partitioning的特殊性,這種類型的分區將不支持Partition Split操做,也不支持create table as select操做。
舉例:建立了下面這張分區表,沒有指定任何分區鍵
CREATE TABLE sales_by_region (
Dept_no number, 
Dept_name varchar2(20), 
quarterly_sales number(10,2), 
state varchar2(2))
PARTITION BY SYSTEM 
( 
PARTITION sales_by_region_east_pt TABLESPACE tbs_1, 
PARTITION sales_by_region_south_pt TABLESPACE tbs_2, 
PARTITION sales_by_region_west_pt TABLESPACE tbs_3, 
PARTITION sales_by_region_north_pt TABLESPACE tbs_4
);

如今由SQL語句來指定插入哪一個分區:
--數據插入sales_by_region_east_pt分區
INSERT INTO sales_by_region PARTITION(sales_by_region_east_pt) VALUES (100,’test’,100,’SH’);
--數據插入第2個分區,也就是sales_by_region_south_pt分區
INSERT INTO sales_by_region PARTITION(sales_by_region_south_pt) VALUES (200,’test2’,200,’SZ’);
--爲了實現綁定變量,用pno變量來代替實際分區號,以免過分解析
INSERT INTO systab PARTITION(:pno) VALUES (300,’test3’,300,’LZ’);


3. Virtual Column-Based Partitioning
Virtual Column是11g中的一個新功能,這種列中的數據並不實際存儲於磁盤上(能夠當作是一個相似Function的列),
只有當讀取的時候才實時計算。
虛擬列雖然沒有實際的存儲空間,可是卻能夠跟其餘普通列同樣,建立索引,做爲分區鍵,甚至能夠收集統計信息。
能夠經過這樣的語句來建立虛擬列:
CREATE TABLE tb_v 
(col_1 number(6) not null, 
col_2 number not null, 
col_v as (col_1 *( 1+col_2)));


四、Reference Partitioning
order_itmes表自動建立了和父表相對應的分區。不能指定引用分區的上下限,但能夠給分區命名,若是沒有顯示的給各個分區命名,
將自動繼承父表的分區名稱。
一旦父表的分區發生變化,子表分區也會自動適應,而單獨修改子表分區則不被容許。
CREATE TABLE orders
( order_id NUMBER(12),
  order_date DATE,
  order_mode VARCHAR2(8),
  customer_id NUMBER(6),
  CONSTRAINT pk_orders PRIMARY KEY(order_id)
)
PARTITION BY RANGE(order_date)
  (PARTITION orders_2011_Q1_pt VALUES LESS THAN (TO_DATE('2011-4-1','yyyy-mm-dd')),
   PARTITION orders_2011_Q2_pt VALUES LESS THAN (TO_DATE('2011-7-1','yyyy-mm-dd')),
   PARTITION orders_2011_Q3_pt VALUES LESS THAN (TO_DATE('2011-10-1','yyyy-mm-dd')),
   PARTITION orders_2011_Q4_pt VALUES LESS THAN (TO_DATE('2012-1-1','yyyy-mm-dd')))
;

CREATE TABLE order_items
( order_id NUMBER(12) ,
  line_item_id NUMBER(3),
  product_id NUMBER(6),
  unit_price NUMBER(8,2),
  quantity NUMBER(8),
  CONSTRAINT pk_order_items PRIMARY KEY(order_id,line_item_id),
  CONSTRAINT fk_order_items FOREIGN KEY(order_id) REFERENCES orders(order_id)
)
PARTITION BY REFERENCE(fk_order_items);

select PARTITION_NAME from dba_tab_partitions
where table_name='ORDERS';

PARTITION_NAME
------------------------------
ORDERS_2011_Q4_PT
ORDERS_2011_Q3_PT
ORDERS_2011_Q2_PT
ORDERS_2011_Q1_PT            

select PARTITION_NAME from dba_tab_partitions
where table_name='ORDER_ITEMS';

PARTITION_NAME
------------------------------
ORDERS_2011_Q4_PT
ORDERS_2011_Q3_PT
ORDERS_2011_Q2_PT
ORDERS_2011_Q1_PT

alter table orders drop partition orders_2011_Q1_pt;
select PARTITION_NAME from dba_tab_partitions where table_name='ORDERS';

PARTITION_NAME
------------------------------
ORDERS_2011_Q4_PT
ORDERS_2011_Q3_PT
ORDERS_2011_Q2_PT

select PARTITION_NAME from dba_tab_partitions where table_name='ORDER_ITEMS';

PARTITION_NAME
------------------------------
ORDERS_2011_Q4_PT
ORDERS_2011_Q3_PT
ORDERS_2011_Q2_PT

alter table order_items drop partition orders_2011_Q2_pt;
報錯:
ORA-14255: 未按範圍, 列表, 組合範圍或組合列表方法對錶進行分區


5. More Composite Partitioning
在11g前,複合分區只支持Range-List和Range-Hash;
在11g中複合分區的類型大大增長,如今Range,List,Interval均可以做爲Top level分區,而Second level則能夠是Range,List,Hash,
也就是在11g中能夠有3*3=9種複合分區。


附錄C 索引分區的語法舉例#

對分區表上的索引再建分區,語法參考以下


一、建分區表
create table orders(
order_no      number,
part_no       varchar2(40),
ord_date      date
)
partition by range (ord_date)
(partition orders_201101_pt values less than (TO_DATE('2011-02-01','YYYY-MM-DD')),
partition orders_201102_pt values less than (TO_DATE('2011-03-01','YYYY-MM-DD')),
partition orders_201103_pt values less than (TO_DATE('2011-04-01','YYYY-MM-DD')),
partition orders_201104_pt values less than (TO_DATE('2011-05-01','YYYY-MM-DD'))
)ENABLE ROW MOVEMENT;

二、建global分區索引,global索引分區的鍵值不用和表的分區鍵值相同
create index orders_ord_date_global_idx
on orders(ord_date)
global partition by range (ord_date)
(partition orders_global_idx_201101_pt values less than (TO_DATE('2011-02-01','YYYY-MM-DD')),
partition orders_global_idx_201104_pt values less than (TO_DATE('2011-05-01','YYYY-MM-DD')),
partition orders_global_idx_201107_pt values less than (TO_DATE('2011-08-01','YYYY-MM-DD')),
partition orders_global_idx_max_pt values less than (MAXVALUE)
);

三、Oracle不支持非前綴的全局分區索引,若是須要創建非前綴分區索引,索引必須建成本地索引。
下面兩條建索引的語句都會報錯:"ORA-14038: GLOBAL partitioned index must be prefixed"
/*
create index orders_part_no_global_idx
on orders(part_no)
  global partition by range (order_no)
   (partition orders_global_idx_555555_pt values less than (555555),
    partition orders_global_idx_max_pt values less than (MAXVALUE)
   );         

create index orders_part_no_global_idx
on orders(part_no)
global partition by range (ord_date)
(partition orders_global_idx_201101_pt values less than (TO_DATE('2011-02-01','YYYY-MM-DD')),
partition orders_global_idx_201102_pt values less than (TO_DATE('2011-03-01','YYYY-MM-DD')),
partition orders_global_idx_201103_pt values less than (TO_DATE('2011-04-01','YYYY-MM-DD')),
partition orders_global_idx_max_pt values less than (MAXVALUE)
);

*/

創建帶前綴的全局分區索引,正確的sql以下:
create index orders_part_no_global_idx
on orders(ord_date,part_no)
global partition by range (ord_date)
(partition orders_global_idx_201201_pt values less than (TO_DATE('2012-02-01','YYYY-MM-DD')),
partition orders_global_idx_201202_pt values less than (TO_DATE('2012-03-01','YYYY-MM-DD')),
partition orders_global_idx_201203_pt values less than (TO_DATE('2012-04-01','YYYY-MM-DD')),
partition orders_global_idx_max_pt values less than (MAXVALUE)
);


四、建local分區索引,local索引分區的鍵值必須和表的分區鍵值相同
4.一、註釋中的語句加上時,會報錯:"ORA-14010:this physical attribute may not be specified for an index partition"
drop index orders_ord_date_global_idx;
create index orders_ord_date_local_idx
on orders(ord_date)
local  
(partition orders_local_idx_201101_pt /*values less than (TO_DATE('2011-02-01','YYYY-MM-DD'))*/,  
partition orders_local_idx_201102_pt /*values less than (TO_DATE('2011-03-01','YYYY-MM-DD'))*/,
partition orders_local_idx_201103_pt /*values less than (TO_DATE('2011-04-01','YYYY-MM-DD'))*/,
partition orders_local_idx_max_pt /*values less than (MAXVALUE)*/
);

4.二、註釋中的語句執行時,會報錯:
ORA-14024:number of partitions of LOCAL index must equal that of the underlying table
/*
create index orders_party_no_local_idx
     on orders(part_no)
      local 
   (partition orders_local2_idx_201101_pt,
    partition orders_local2_idx_max_pt
   );   */

4.三、正確的sql以下,雖然索引的鍵值是part_no(無用),但索引分區的鍵值仍然和表的分區鍵值相同,即ord_date:
create index orders_party_no_local_idx
     on orders(part_no)
      local 
  (partition orders_local2_idx_201101_pt ,
partition orders_local2_idx_201102_pt ,
partition orders_local2_idx_201103_pt,
partition orders_local2_idx_max_pt
);

附錄D 獲取UPDATE行數的方法#

說明:執行用戶須要有查詢v$sql的權限和在本身名下建表的權限。
步驟(如下步驟都在同一執行用戶下完成):
一、	執行initial_get_col_update_tab.sql,建立表coll_col_update_info,該表用來存放捕獲的表字段更新信息;
二、	建立一個排程任務(Job,Cron均可以),該任務按期執行get_col_update.sql,用來按期收集被更新的表和字段的信息。
三、	收集到必定時間之後(例如一週),從表coll_col_update_info裏面以字段名爲條件過濾統計rows_processed量並和
全部的rows_processed統計量對比便可獲得某一特定字段更新的佔比。

initial_get_col_update_tab.sql
create table coll_col_update_info as 
select sysdate as snap_time,
       parsing_schema_name,
       COMMAND_TYPE,
       sql_id,
       updated_table,
       updated_sql_section,
       ceil(rows_processed / active_duration) as update_per_second,
       ceil(executions / active_duration) as exec_per_second,
       executions,
       rows_processed,
       first_load_time,
       last_active_time
  from (select parsing_schema_name,
               sql_id,
               COMMAND_TYPE,
               substr(upper(sql_text),
                      instr(upper(sql_text), 'UPDATE') + 7,
                      instr(upper(sql_text), ' SET ') -
                      (instr(upper(sql_text), 'UPDATE') + 7)) as updated_table,
               upper(substr(sql_text,
                            instr(upper(sql_text), 'SET'),
                            case
                              when instr(upper(sql_text), 'WHERE') = 0 then
                               length(sql_text)
                              else
                               instr(upper(sql_text), 'WHERE')
                            END - instr(upper(sql_text), 'SET'))) as updated_sql_section,
               executions,
               rows_processed,
               to_date(first_load_time, 'YYYY-MM-DD HH24:MI:SS') as first_load_time,
               last_active_time,
               case
                 when (last_active_time -
                      to_date(first_load_time, 'YYYY-MM-DD HH24:MI:SS')) * 24 * 3600 <= 0 then
                  1
                 else
                  (last_active_time -
                  to_date(first_load_time, 'YYYY-MM-DD HH24:MI:SS')) * 24 * 3600
               end as active_duration
          from v$sql
         where command_type = 6 /*command_type equal 6 means update sql*/
           and executions > 0) col_update_total
 where 1=2
 order by executions desc;


get_col_update.sql
insert into coll_col_update_info 
select parsing_schema_name,
       COMMAND_TYPE,
       sql_id,
       updated_table,
       updated_sql_section,
       rows_processed,
       executions,
       rows_processed,
       first_load_time,
       last_active_time
  from (select parsing_schema_name,
               sql_id,
               COMMAND_TYPE,
               substr(upper(sql_text),
                      instr(upper(sql_text), 'UPDATE') + 7,
                      instr(upper(sql_text), ' SET ') -
                      (instr(upper(sql_text), 'UPDATE') + 7)) as updated_table,
               upper(substr(sql_text,
                            instr(upper(sql_text), 'SET'),
                            case
                              when instr(upper(sql_text), 'WHERE') = 0 then
                               length(sql_text)
                              else
                               instr(upper(sql_text), 'WHERE')
                            END - instr(upper(sql_text), 'SET'))) as updated_sql_section,
               executions,
               rows_processed,
               to_date(first_load_time, 'YYYY-MM-DD HH24:MI:SS') as first_load_time,
               last_active_time,
               case
                 when (last_active_time -
                      to_date(first_load_time, 'YYYY-MM-DD HH24:MI:SS')) * 24 * 3600 <= 0 then
                  1
                 else
                  (last_active_time -
                  to_date(first_load_time, 'YYYY-MM-DD HH24:MI:SS')) * 24 * 3600
               end as active_duration
          from v$sql
         where command_type = 6 /*command_type equal 6 means update sql*/
           and executions > 0) col_update_total
 where rows_processed / active_duration > 1
   and updated_sql_section is not null
   and ((active_duration >= 3600 and rows_processed / active_duration > 0.1) or
       ((active_duration < 3600 and rows_processed / active_duration > 1) and
       rows_processed > 1000))
   and updated_table like '<YOUR_TABLE_NAME>%' 
 order by executions desc;
相關文章
相關標籤/搜索