在oracle 10g世界裏面,分區表主要分range,hash,list,range-hash,range-list五種類型,在oracle 11g中,則發展到了3*3的分區組合類型,以滿足更多的應用場景!但無論在什麼情況下,範圍分區都是最常見的一種表分區方式,尤其在需要對過期的數據進行整理歸檔,只保留一定時期內的數據的條件下,幾乎都會優先選擇使用範圍分區的方式!分區表可以說是一項百利而無一害的技術,當數據量達到一定的級別後(通常是超過100G後),就算使用了ASM技術,數據庫中一樣會產生嚴重的I/O等待事件!
下面來簡要介紹下範圍分區,範圍分區的主要優點主要如下:
1:分區表可以將表存儲在多個表空間內,進而離散I/O;
2:同時各個分區維護各自的本地索引(一般使用local索引,而不是global索引);
3:select語句可以根據索引進行分區範圍掃描,減少查詢語句所帶來的一致性讀;
4:可以對單個分區進行備份或者truncate,歸檔或者清除過期的數據;
5: 可以方便的對錶的分區進行添加,刪除,truncate,拆分和合並操作
一:創建一張分區表,分區的條件是以銷售日期來界定,同時分區的索引爲本地索引,每個分區的對應一個單獨的表空間,基於離散I/O和方便管理的雙重需要
SQL> create table sale_data SQL> select owner,partitioning_type,partition_count,status from dba_part_tables where table_name='SALE_DATE'; OWNER PARTITI PARTITION_COUNT STATUS SQL> create index ind_sale_data_date on sale_data(sale_id) local SQL> select segment_name,partition_name,tablespace_name from user_segments where segment_name in ('SALE_DATA','IND_SALE_DATA_DATE'); SEGMENT_NAME PARTITION_NAME TABLESPACE_NAME SEGMENT_NAME PARTITION_NAME TABLESPACE_NAME SEGMENT_NAME PARTITION_NAME TABLESPACE_NAME |
二:插入測試數據,收集優化器的統計信息(包括索引),當前的系統時間爲2012年3月12日(傳說中的植樹節哦!),因而數據應當在sales_03和sales_05兩個分區
- SQL> begin
- 2 for i in 1..10000
- 3 loop
- 4 insert into sale_data values (i,'yang',sysdate);
- 5 commit;
- 6 end loop;
- 7* end;
- PL/SQL procedure successfully completed.
- SQL> begin
- 2 for i in 1..10000
- 3 loop
- 4 insert into sale_data values (i,'yang',sysdate+60);
- 5 commit;
- 6 end loop;
- 7* end;
- PL/SQL procedure successfully completed.
- SQL> exec dbms_stats.gather_table_stats('SALE','SALE_DATA',CASCADE => TRUE);
- PL/SQL procedure successfully completed.
三:以sys用戶運行,將plustrace權限賦予用戶sale
- SQL> @?/sqlplus/admin/plustrce.sql;
- SQL>
- SQL> drop role plustrace;
- drop role plustrace
- *
- ERROR at line 1:
- ORA-01919: role 'PLUSTRACE' does not exist
- SQL> create role plustrace;
- Role created.
- SQL> grant select on v_$sesstat to plustrace;
- Grant succeeded.
- SQL> grant select on v_$statname to plustrace;
- Grant succeeded.
- SQL> grant select on v_$mystat to plustrace;
- Grant succeeded.
- SQL> grant plustrace to dba with admin option;
- Grant succeeded.
- SQL> set echo off
- SQL> grant plustrace to sale;
- Grant succeeded.
四:查看在分區表上的查詢語句的執行計劃
SQL> set autot trace exp stat ------------------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Cost (%CPU)| Time | Pstart| Pstop | ------------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 1 | 17 (0)| 00:00:01 | | | | 1 | SORT AGGREGATE | | 1 | | | | | | 2 | PARTITION RANGE ALL| | 20000 | 17 (0)| 00:00:01 | 1| 12 | | 3 | TABLE ACCESS FULL | SALE_DATA | 20000 | 17 (0)| 00:00:01 | | 12 | -------------------------------------------------------------------------------- Statistics -------------------------------------------------------------------------------- | 1 | SORT AGGREGATE | | 1 | | | | | | 2 | PARTITION RANGE SINGLE| | 10000 | 9 (0)| 00:00:01 | 3 | 3 | | 3 | TABLE ACCESS FULL | SALE_DATA | 10000 | 9 (0)| 00:00:01 |3 | 3 | -------------------------------------------------------------------------------- SQL> set autot trace exp stat SALE_ID SALESMAN_NAME SALES_DATE -------------------------------------------------------------------------------- | 1 | PARTITION RANGE SINGLE | | 1 | 16| 2 (0)| 00:00:01 | 3 | 3 | | 2 | TABLE ACCESS BY LOCAL INDEX ROWID| SALE_DATA | 1 | 162| 2 (0)| 00:00:01 | 3 | 3 | |* 3 | INDEX RANGE SCAN | IND_SALE_DATA_DATE | 1 | -------------------------------------------------------------------------------- Statistics 可以看到,當進行等值查詢的時候,先走了範圍索引掃描,而後是通過本地索引定位rowid,然後訪問單個分區,總共有4個一致性讀! |
五:對單個分區的truncate操作
- SQL> select count(*) from sale_data partition(sales_03);
- COUNT(*)
- ----------
- 10000
- SQL> select count(*) from sale_data partition(sales_05);
- COUNT(*)
- ----------
- 10000
- SQL> select * from sale_data partition(sales_03) where rownum=1;
- SALE_ID SALESMAN_NAME SALES_DATE
- ---------- ------------------------------ -------------------
- 1 yang 2012-03-12:20:04:31
- SQL> select * from sale_data partition(sales_05) where rownum=1;
- SALE_ID SALESMAN_NAME SALES_DATE
- ---------- ------------------------------ -------------------
- 1 yang 2012-05-11:20:08:45
- SQL> alter table sale_data truncate partition sales_03;
- Table truncated.
- SQL> select count(*) from sale_data partition(sales_03);
- COUNT(*)
- ----------
- 0
六:添加分區(非默認)以及默認分區(對應範圍分區的最大值),本地索引會自動創建,刪除分區的時候,索引也會自動維護!
- SQL> alter table sale_data add partition sales_13 values less than (to_date('01/02/2013','DD/MM/YYYY')) tablespace tbs_sale12;
- Table altered.
- SQL> alter table sale_data add partition sales_14 values less than (maxvalue) tablespace tbs_sale12;
- Table altered.
- SQL> select segment_name,partition_name,tablespace_name from user_segments where segment_name='IND_SALE_DATA_DATE' and partition_name in ('SALES_13','SALES_14');
- SEGMENT_NAME PARTITION_NAME TABLESPACE_NAME
- -------------------- -------------------- ------------------------------
- IND_SALE_DATA_DATE SALES_13 TBS_SALE12
- IND_SALE_DATA_DATE SALES_14 TBS_SALE12
- SQL> alter table sale_data drop partition sales_13;
- Table altered.
- SQL> alter table sale_data drop partition sales_14;
- Table altered.
- SQL> select segment_name,partition_name,tablespace_name from user_segments where segment_name='IND_SALE_DATA_DATE' and partition_name in ('SALES_13','SALES_14');
- no rows selected.
七:下面來討論下表分區的拆分與合併,創建一張結構簡單的分區表來說明,根據用戶id進行分區;
- SQL> create table emp (id number,first_name char(10))
- 2 partition by range(id)
- 3 (
- 4 partition p_1 values less than (10000) tablespace tbs_sale01,
- 5 partition p_2 values less than (20000) tablespace tbs_sale02,
- 6* partition p_3 values less than (30000) tablespace tbs_sale03)
- Table created.
- SQL> begin
- 2 for i in 1..20000
- 3 loop
- 4 insert into emp values (i,'t_i');
- 5 commit;
- 6 end loop;
- 7* end;
- PL/SQL procedure successfully completed.
1:首先,當創建分區表的時候未指定maxvalue值所在的分區的情況下,插入分區以外的值,將會報ORA-14400錯誤
- SQL> insert into emp values (30001,'t');
- insert into emp values (30001,'t')
- *
- ERROR at line 1:
- ORA-14400: inserted partition key does not map to any partition
- SQL> alter table emp add partition p_max values less than (maxvalue) tablespace tbs_sale04;
- Table altered.
- SQL> insert into emp values (30001,'t');
- 1 row created.
- SQL> commit;
- Commit complete.
- SQL> select * from emp partition(p_max);
- ID FIRST_NAME
- ---------- ----------
- 30001 t
2:將p_1分區進行拆分操作
- SQL> select partition_name,tablespace_name from user_segments where segment_name='EMP';
- PARTITION_NAME TABLESPACE_NAME
- ------------------------------ ------------------------------
- P_1 TBS_SALE01
- P_2 TBS_SALE02
- P_3 TBS_SALE03
- P_MAX TBS_SALE04
- SQL> alter table emp split partition p_1 at('9999') into (partition p_1_01 tablespace tbs_sale02,partition p_1_02 tablespace tbs_sale03);
- Table altered.
- SQL> select partition_name,tablespace_name from user_segments where segment_name='EMP';
- PARTITION_NAME TABLESPACE_NAME
- ------------------------------ ------------------------------
- P_2 TBS_SALE02
- P_3 TBS_SALE03
- P_MAX TBS_SALE04
- P_1_01 TBS_SALE02
- P_1_02 TBS_SALE03
- SQL> select count(*) from emp partition(p_1_01);
- COUNT(*)
- ----------
- 9998
- SQL> select count(*) from emp partition(p_1_02);
- COUNT(*)
- ----------
- 1
- 由此可見,at關鍵字指的是在這個點進行拆分,且包含這個點!
3:將p_1_01和p_1_02分區進行合併操作,若不指定表空間,將使用用戶的默認表空間
- SQL> alter table emp merge partitions p_1_01,p_1_02 into partition p_01;
- Table altered.
- SQL> select partition_name,tablespace_name from user_segments where segment_name='EMP';
- PARTITION_NAME TABLESPACE_NAME
- ------------------------------ ------------------------------
- P_01 USERS
- P_2 TBS_SALE02
- P_3 TBS_SALE03
- P_MAX TBS_SALE04
八:針對單個分區的導出和導入,使用expdp和impdp實現
1:查看錶各個分區的情況,創建目錄對象並授權,導出分區P_1
- SQL> select PARTITION_NAME ,TABLESPACE_NAME from user_segments where segment_name='EMP';
- PARTITION_NAME TABLESPACE_NAME
- ------------------------------ ------------------------------
- P_1 TBS_SALE01
- P_2 TBS_SALE02
- P_3 TBS_SALE03
- P_MAX TBS_SALE04
- SQL> select count(*) from emp partition (p_1);
- COUNT(*)
- ----------
- 9999
- SQL> select count(*) from emp partition (p_2);
- COUNT(*)
- ----------
- 10000
- SQL> select count(*) from emp partition (p_3);
- COUNT(*)
- ----------
- 1
- SQL> select count(*) from emp partition (p_max);
- COUNT(*)
- ----------
- 1
- SQL> conn /as sysdba
- Connected.
- SQL> create directory dir01 as '/home/oracle/dir01';
- Directory created.
- SQL> grant read,write on directory dir01 to sale;
- Grant succeeded.
- [oracle@rhel6 ~]$ expdp help=y
- Example: expdp scott/tiger DUMPFILE=scott.dmp DIRECTORY=dmpdir SCHEMAS=scott
- or TABLES=(T1:P1,T1:P2), if T1 is partitioned table
- [oracle@rhel6 ~]$ expdp sale/123456 directory=dir01 dumpfile=emp_p1.dmp logfile=emp_p1.log tables=emp:p_1
- Export: Release 10.2.0.1.0 - 64bit Production on Tuesday, 13 March, 2012 21:06:13
- Copyright (c) 2003, 2005, Oracle. All rights reserved.
- Connected to: Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - 64bit Production
- With the Partitioning, OLAP and Data Mining options
- Starting "SALE"."SYS_EXPORT_TABLE_01": sale/******** directory=dir01 dumpfile=emp_p1.dmp logfile=emp_p1.log tables=emp:p_1
- Estimate in progress using BLOCKS method...
- Processing object type TABLE_EXPORT/TABLE/TABLE_DATA
- Total estimation using BLOCKS method: 256 KB
- Processing object type TABLE_EXPORT/TABLE/TABLE
- Processing object type TABLE_EXPORT/TABLE/STATISTICS/TABLE_STATISTICS
- . . exported "SALE"."EMP":"P_1" 190.5 KB 9999 rows
- Master table "SALE"."SYS_EXPORT_TABLE_01" successfully loaded/unloaded
- ******************************************************************************
- Dump file set for SALE.SYS_EXPORT_TABLE_01 is:
- /home/oracle/dir01/emp_p1.dmp
- Job "SALE"."SYS_EXPORT_TABLE_01" successfully completed at 21:06:17
2.導出完成後,截斷P_1分區
- [oracle@rhel6 ~]$ sqlplus sale/123456
- SQL*Plus: Release 10.2.0.1.0 - Production on Tue Mar 13 21:07:18 2012
- Copyright (c) 1982, 2005, Oracle. All rights reserved.
- Connected to:
- Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - 64bit Production
- With the Partitioning, OLAP and Data Mining options
- SQL> alter table emp truncate partition p_1;
- Table truncated.
- SQL> select count(*) from emp partition (p_1);
- COUNT(*)
- ----------
- 0
3:導入分區P_1的備份,需要設置table_exists_action參數爲append,否則將導入失敗
- [oracle@rhel6 ~]$ impdp sale/123456 directory=dir01 dumpfile=emp_p1.dmp logfile=emp_p1.log2 table_exists_action=append tables=emp:p_1
- Import: Release 10.2.0.1.0 - 64bit Production on Tuesday, 13 March, 2012 21:29:58
- Copyright (c) 2003, 2005, Oracle. All rights reserved.
- Connected to: Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - 64bit Production
- With the Partitioning, OLAP and Data Mining options
- Master table "SALE"."SYS_IMPORT_TABLE_01" successfully loaded/unloaded
- Starting "SALE"."SYS_IMPORT_TABLE_01": sale/******** directory=dir01 dumpfile=emp_p1.dmp logfile=emp_p1.log2 table_exists_action=append tables=emp:p_1
- Processing object type TABLE_EXPORT/TABLE/TABLE
- ORA-39152: Table "SALE"."EMP" exists. Data will be appended to existing table but all dependent metadata will be skipped due to table_exists_action of append
- Processing object type TABLE_EXPORT/TABLE/TABLE_DATA
- . . imported "SALE"."EMP":"P_1" 190.5 KB 9999 rows
- Processing object type TABLE_EXPORT/TABLE/STATISTICS/TABLE_STATISTICS
- Job "SALE"."SYS_IMPORT_TABLE_01" completed with 1 error(s) at 21:30:01
- SQL> conn sale/123456
- Connected.
- SQL> select count(*) from emp partition (p_1);
- COUNT(*)
- ----------
- 9999
九:分區交換,這是一項非常實用的技術,即可以將字段完全相同的分區表分區和普通表進行存儲空間的交換,從而達到數據交換的目的。就類似於將兩個用戶的名字對調,而原本用戶用戶的對象都沒有改變。並且,當僅有local分區索引,且和普通表的索引相對應時,還可以將索引一起作交換。exchange partition的語法如下:
ALTER TABLE table1
EXCHANGE PARTITION partition
WITH TABLE table2
[{ INCLUDING | EXCLUDING } INDEXES]
[{ WITH | WITHOUT } VALIDATION]
[EXCEPTIONS INTO [schema.]table]
[UPDATE/NVALIDATE GLOBAL INDEXES
[NOPARALLEL/PARALLEL[int]]]
- SQL> create table emp_exchange as select * from emp where 1=0;
- Table created.
- SQL> select count(*) from emp_exchange;
- COUNT(*)
- ----------
- 0
- SQL> select count(*) from emp partition(p_1);
- COUNT(*)
- ----------
- 9999
- SQL> alter table emp exchange partition p_1 with table emp_exchange;
- Table altered.
- SQL> select count(*) from emp partition(p_1);
- COUNT(*)
- ----------
- 0
- SQL> select count(*) from emp_exchange;
- COUNT(*)
- ----------
- 9999
- SQL> select tablespace_name from user_segments where segment_name='EMP_EXCHANGE';
- TABLESPACE_NAME
- ------------------------------
- TBS_SALE01
- SQL> select tablespace_name from user_segments where segment_name='EMP';
- TABLESPACE_NAME
- ------------------------------
- USERS
- TBS_SALE02
- TBS_SALE03
- TBS_SALE04