先說句題外話… 歡迎成都天府軟件園的小夥伴來面基交流經驗~html
一:什麼是分區(Partition)?數據庫
分區是將一個表或索引物理地分解爲多個更小、更可管理的部分。服務器
分區對應用透明,即對訪問數據庫的應用而言,邏輯上講只有一個表或一個索引(至關於應用「看到」的只是一個表或索引),但在物理上這個表或索引可能由數十個物理分區組成。併發
每一個分區都是一個獨立的對象,能夠獨自處理,也能夠做爲一個更大對象的一部分進行處理。less
--------------------------Tips:分表與分區表--------------------------ide
分表是將一個大表按照必定的規則分解成多張具備獨立存儲空間的實體表(子表);函數
好比一個訂單表 ORDER,採用年月分表後可能就會除 ORDER 自己外還生成許多如 ORDER_20160一、ORDER_20160二、ORDER_201603… 等的子表。性能
分表在邏輯上是多張不一樣的表,而分區表在邏輯上是一張表。優化
--------------------------------------------------------------------spa
二:何時須要分區?
來自官網的兩個建議:
1. Tables greater than 2GB should always be considered for partitioning.(表數據量大於2GB時應該考慮使用分區)
2. Tables containing historical data, in which new data is added into the newest partition. A typical example is a historical table where only the current month's data is updatable and the other 11 months are read only.(新數據均加入至最新分區中的用於存儲歷史數據的表)
三:分區帶來的好處
1. 提升數據可用性
a) 得益於每一個分區的獨立性,優化器會在查詢時有須要的去除未用到的分區(這也叫消除分區)
好比:一個查詢若是隻用到了一個表三個分區中的其中一個分區的數據,那麼Oracle在執行這個查詢時只會掃描用到的這個分區的數據,不會掃描其餘兩個分區的數據。
這在OLAP系統中頗有用。
-----------------------延伸閱讀:OLTP與OLAP系統---------------------
OLTP(On-Line Transaction Processing):
聯機事務處理過程,也稱爲面向交易的處理過程,其基本特徵是前臺接收的用戶數據能夠當即傳送到計算中心進行處理,並在很短的時間內給出處理結果,實現對用戶操做的快速響應;
這樣的系統事務性要求很是高,通常都是高可用的在線系統,以小的事務以及小的查詢爲主。評估其系統的時候,通常看其每秒執行的 Transaction 以及 Execute SQL 的數量。單個數據庫每秒處理的 Transaction 每每超過幾百個或是幾千個,Select 語句的執行量每秒幾千甚至幾萬個;
OLTP是傳統的關係型數據庫的主要應用,典型的OLTP系統有電子商務系統、銀行、證券系統等。
OLAP(On-Line Analytical Processing):
聯機分析處理,是數據倉庫系統的主要應用,所謂數據倉庫是對於大量已經由OLTP造成的數據的一種分析型的數據庫,用於處理商業智能、決策支持等重要的決策信息;
數據倉庫是在數據庫應用到必定程度以後而對歷史數據的加工與分析,讀取較多、更新較少;
OLTP與OLAP簡單對比:
----------------------------------------------------------------------
b) 分區還能夠經過減小停機時間來提升可用性
例如:一個100GB的表,中間的數據若是遭到損壞,那麼恢復起來簡直讓人抓狂。
若是這100GB的表被劃分爲了50個2GB的分區,當其中某個分區數據遭到破壞時,只須要恢復一個2GB的分區數據便可。
出現錯誤時的停機時間將會大大減小,由於恢復所需的工做量大幅減小。
2. 方便管理
將一個大的對象分解爲數個小對象,操做這些小對象明顯比直接操做原來的大對象更加容易,且佔用的資源也更少。
3. 改善語句性能(多針對OLAP系統)
a) 並行DML(Parallel DML):
在 Oracle 9i 之前的版本中,PDML(Parallel DML)要求必須分區;
9i 及之後的版本中這個限制已經放鬆,只有兩個例外:
① 但願在一個表上執行 PDML,並且這個表的一個 LOB列上有一個位圖索引,要並行執行操做就必須對這個表分區;
② 對於並行訪問分區操做,取須要訪問的分區數爲並行度
---------------------延伸閱讀:PDML(Parallel DML)---------------------
什麼是Parallel(並行)技術?
對於一個大的任務,通常的作法是利用一個進程,串行的執行。
但若是系統資源足夠,能夠採用Parallel(並行)技術,把一個大的任務分紅若干個小的任務,同時啓用N個進程(或線程),並行的處理這些小的任務,這些併發的進程稱爲並行執行服務器(parallel executeion server),它們統一由一個稱爲併發協調進程的進程來管理。
注意:
只有在須要處理一個很大的任務(如須要幾個小時的做業),而且要有足夠的系統資源(包括CPU、內存、I/O等)的狀況下,才應該考慮使用Parallel技術。
不然,在一個多併發用戶環境下,系統自己資源負擔已經很大,啓用Parallel的話,將會致使某一個會話試圖佔用全部的資源,其餘會話不得不等待,從而致使系統性能反而降低的狀況。
通常狀況下,OLTP系統中不要使用Parallel技術,OLAP系統中能夠考慮使用。
PDML分類:
並行查詢:並行查詢容許將一個select語句劃分爲多個較小的查詢,每一個部分的查詢都併發地運行,而後將各個部分的結果組合起來,提供最終的結果。(多用於全表掃描,索引全掃描等)
並行DML:Parallel DML包括 insert、update、delete、merge,在PDML期間,Oracle可使用多個並行執行服務器(即併發進程)來執行 insert、update、delete、merge,多個會話同時執行,同時每一個會話(併發進程)都有本身的undo段,都是一個獨立的事務,這些事務要麼都由併發協調進程提交,要麼都rollback。
-----------------------------------------------------------------------
b) 查詢性能:
分區對於不一樣的系統帶來的影響可能不一樣;
對OLTP系統而言,須要謹慎使用分區操做,由於在傳統的OLTP系統中,大多數查詢極可能當即返回結果,並且獲取大多數數據可能都經過一個很小的索引區間掃描來完成。故分區帶來的性能方面的優勢在 OLTP 系統中可能根本表現不出來。
在一個OLTP系統中,分區若是應用不當,甚至可能使性能降低(分區可能會提升某些類型查詢的性能,可是這些查詢一般不在OLTP系統中使用);
因此有一點你必須明白:分區並不老是和「性能提高」聯繫在一塊兒。
對於OLAP系統而言,分區消除與並行查詢將可能帶來效率的大幅提高。
四:表分區機制
表分區的四種類型:
1. 範圍分區:
範圍(Range)分區將數據基於指定的分區鍵映射到每個分區中。
這種分區方式最爲經常使用,且經常採用日期做爲分區鍵。
注意:
① 每個分區都須要有一個 VALUES LESS THEN 子句,它指定了該分區的上限值(即該分區能接受的分區鍵的最大值)。記錄裏分區鍵的值小於這個上限值時,該記錄會被放入該分區;而當記錄裏分區鍵的值等於或大於這個上限值時該記錄會被放入下一個上限值更高的分區中。
② 全部分區裏,除了第一個分區,其餘分區其實都有一個隱式的下限值(即該分區能接受的分區鍵的最小值),這個下限值就是上一個分區的上限值。
③ 在最後一個分區中,可定義上限值爲 MAXVALUE(該值可理解爲全部分區中的一個最大上限值,包括空值),當記錄分區鍵的值大於以前全部分區的上限值時,這條記錄會被放入這最後一個分區中。
建表語句示例:
/****************************************************範圍分區示例******************************************/ --建立示例表 create table range_example ( id number(2), done_date date, data varchar2(50) ) --建立分區,分區鍵爲示例表(range_example)中的 done_date 字段 partition by range (done_date) ( partition part_1 values less than ( to_date('20160901', 'yyyymmdd') ), partition part_2 values less than ( to_date('20161001', 'yyyymmdd') ), partition part_3 values less than ( maxvalue ) ) --查看range_example表的分區信息 select * from user_tab_partitions where table_name = 'RANGE_EXAMPLE';
查看錶分區信息:
插入數據:
如圖,能夠看到示例表 range_example 已經分了三個區。
記錄1的 done_date 爲 2016/8/11,小於分區part_1的上限值,則記錄1會被放入part_1分區;
記錄2的 done_date 爲 2016/9/8 ,大於分區part_1的上限值但小於part_2的上限值,則記錄2會被放入part_2分區;
記錄3的 done_date 爲 2016/10/20,大於前兩個分區的上限值,故會被放入最後一個maxvalue的分區(part_3);
part_2的隱式的下限值實際就是上一個分區part_1的上限值;
2. 散列分區:
對一個表執行散列分區時,Oracle會對分區鍵應用一個散列(Hash)函數,以此肯定數據應當放在 N 個分區中的哪個分區中。
Oracle建議 N 是 2 的一個冪(如 N = 二、四、八、16 等),從而使表數據獲得最佳的整體分佈。
當列的值沒有合適的範圍條件時,建議使用散列分區。
注意:
若是改變散列分區的個數 (向一個散列分區表增長或刪除一個分區時),數據會在全部分區中從新分佈,即全部數據都會被重寫,由於如今每一行可能屬於一個不一樣的分區。
爲表選擇的散列鍵(分區鍵)應當是唯一的一個列或一組列(該列應有多個不一樣的值),以便行能在多個分區上均勻地分佈。
若是使用散列分區,你將沒法控制一行數據最終會放在哪一個分區中(由散列函數控制)。
建表語句示例:
/*******************************************散列分區示例***********************************************/ --建立示例表 create table hash_example ( id number(2), done_date date, data varchar2(50) ) --建立散列分區,分區鍵爲示例表(hash_example)中的 done_date 字段 partition by hash (done_date) ( partition part_1, partition part_2 ) select * from user_tab_partitions where table_name = 'HASH_EXAMPLE';
分區信息:
3. 列表分區:
列表分區能夠根據分區鍵的值明確指定哪些值的數據該放在哪一個分區。
注意:
列表分區中若是指定了 default 分區,則分區鍵的值不在任何分區值列表中的記錄,會被放入 default 分區;
而一旦建立了一個 default 分區後,就不能再向這個表中增長更多的分區了;
若是未指定 default 分區,則在插入分區鍵值不在任何分區值列表中的記錄時,Oracle會報錯(ORA-14400: inserted partition key does not map to any partition)。
建表語句示例:
/*******************************************列表分區示例***********************************************/ --建立示例表 create table list_example ( id number(2), name varchar(30), data varchar2(50) ) --建立列表分區,分區鍵爲示例表(list_example)中的 id 字段 partition by list (id) ( partition part_1 values ( '1', '3', '5', '7' ), partition part_2 values ( '2', '4', '6', '8' ), partition part_default values ( default ) ) select * from user_tab_partitions where table_name = 'LIST_EXAMPLE';
分區信息:
如上,分區鍵(即list_example表中id字段)值爲 一、三、五、7 的記錄,會被放入part_1分區;
分區鍵值爲 二、四、六、8 的記錄,會被放入part_2分區;
分區鍵值爲其餘值的記錄,會被放入最後一個part_default分區。
4. 組合分區:
組合分區是範圍分區與散列分區的組合,或者是範圍分區與列表分區的組合。
在組合分區中,頂層分區機制老是範圍分區,第二級分區機制多是散列分區也多是列表分區;
數據物理的存儲在子分區段上,分區(頂層的範圍分區)成爲了一個邏輯容器,或者是一個指向實際子分區的容器;
每一個頂層分區不須要有相同數目的子分區。
範圍-散列組合分區 建表語句示例:
/******************************************範圍-散列分區**************************************/ create table range_hash_example ( id number(2), done_date date, data varchar2(50) ) --頂層範圍分區的分區鍵爲 range_hash_example 表中的 done_date 字段; --第二層散列分區的分區鍵爲 range_hash_example 表中的 id 字段; partition by range (done_date) subpartition by hash (id) ( partition part_1 values less than ( to_date('20160901', 'yyyymmdd') ) ( subpartition part_1_sub_1, subpartition part_1_sub_2 ), partition part_2 values less than ( to_date('20161001', 'yyyymmdd') ) ( subpartition part_2_sub_1, subpartition part_2_sub_2 ), partition part_3 values less than ( maxvalue ) ( subpartition part_3_sub_1, subpartition part_3_sub_2 ) ) select * from user_tab_partitions where table_name = 'RANGE_HASH_EXAMPLE';
分區信息:
在如上的範圍-散列組合分區中,Oracle會首先應用範圍(Range)分區規則,得出數據屬於哪一個區間,(即先經過 done_date 字段肯定記錄是屬於part_1仍是part_2仍是part_3);
而後再應用散列(Hash)函數,來肯定數據最後要放在哪一個子分區(物理分區)中,(即經過 id 字段肯定記錄是屬於一個分區下的哪一個子分區中 )
範圍-列表組合分區 建表語句示例:
/******************************************範圍-列表分區**************************************/ create table range_list_example ( id number(2), done_date date, data varchar2(50) ) --頂層範圍分區的分區鍵爲 range_list_example 表中的 done_date 字段; --第二層列表分區的分區鍵爲 range_list_example 表中的 id 字段; partition by range (done_date) subpartition by list (id) ( partition part_1 values less than ( to_date('20160901', 'yyyymmdd') ) ( subpartition part_1_sub_1 values ( '1', '3', '5' ), subpartition part_1_sub_2 values ( '2', '4', '6' ) ), partition part_2 values less than ( to_date('20161001', 'yyyymmdd') ) ( subpartition part_2_sub_1 values ( '11', '13', '15', '17' ), subpartition part_2_sub_2 values ( '12', '14' ), subpartition part_2_sub_3 values ( '16', '18' ) ), partition part_3 values less than ( maxvalue ) ( subpartition part_3_sub_1 values ( '21', '23', '25' ), subpartition part_3_sub_2 values ( '22', '24', '26' ) ) ) select * from user_tab_partitions where table_name = 'RANGE_LIST_EXAMPLE';
分區信息:
如圖,每一個頂層的範圍分區能夠有不一樣數目的子分區。
5. 小結
通常來說,若是須要將數據按照某個值邏輯彙集,多采用範圍分區。如基於時間數據的按「年」、「月」等分區就是很典型的例子。在許多狀況下,範圍分區都能利用到分區消除特性( = >= <= between…and 等篩選條件下)。
若是在表裏沒法找到一個合適的屬性來按這個屬性完成範圍分區,但你又想享受分區帶來的性能與可用性的提高,則能夠考慮使用散列分區。(適合使用 = IN 等篩選條件)
若是數據中有一列或有一組離散值,且按這一列進行分區頗有意義,則這樣的數據就很適合採用列表分區。
若是某些數據邏輯上能夠進行範圍分區,可是獲得的範圍分區仍是太大,不能有效管理,則能夠考慮使用組合分區。
注意:
分區在最開始建立表時被一同建立,若是後期要更改分區策略的話,須要先重建表。
---------------------------延伸閱讀:自動遞增(自增)分區---------------------------
前面說到基於時間數據的按「年」、「月」進行的典型的範圍分區例子,這裏再補充一個應用場景:
假若有一張商品銷售記錄表(products_table),其中簡單記錄着商品的id號,名稱,銷售時間;
當按照銷售時間進行範圍分區時,由於表裏的記錄是不斷增長的(每賣出一個商品就會增長一條記錄),這時候就能夠考慮建立自增分區;
顧名思義的,當有新記錄插入時,Oracle會根據須要自動增長新分區來存儲新記錄(當新插入的記錄裏的分區鍵的值不在任何已有分區範圍內時,Oracle會自動建立一個新的分區)
你能夠根據須要來指定自增分區的自動遞增策略,好比按天自增、按周自增、按月自增、按年自增等等(具體自增語句百度一下便可知道);
商品銷售記錄表建立按月自增的範圍分區示例:
create table products_table ( id number(2), name varchar2(50), sale_date date ) partition by range(sale_date) interval (numtoyminterval(1,'month')) ( partition p_month_1 values less than (to_date('2016-01-01','yyyy-mm-dd')) )
如圖,取 products_table 中的 sale_date 列做爲分區鍵建立按月自增分區;
全部銷售時間在 ‘2016-01-01’以前的記錄都會被放入 p_month_1 分區;
銷售時間在‘2016-01-01’以後的記錄在插入時Oracle會自動建立記錄所屬月的分區;
好比當有銷售時間分別爲 2016年1月20日 與 2016年2月20日 的兩條記錄插入時,Oracle會分別建立一個上限值爲 ‘2016-01-31’的分區和一個上限值爲‘2016-02-29’的分區來存儲這兩條記錄
--------------------------------------------------------------------------------