記得5年前遇到一個SQL。就是一個簡單的兩表關聯。SQL跑了幾乎相同一天一晚上,這兩個表都很是巨大。每個表都有幾十個G。數據量每個表有20多億,表的字段也特別多。web
相信你們也知道SQL慢在哪裏了,單個進程的PGA 是絕對放不下幾十個G的數據,這就會致使消耗大量temp tablespace,SQL慢就是慢在temp來回來回來回...的讀寫數據。 算法
遇到這樣的超級大表與超級大表怎麼優化呢?這篇文章將告訴你答案。數據庫
首先建立2個測試表 t1,t2 數據來自dba_objects架構
create table t1 as select * from dba_objects;oracle
create table t2 as select * from dba_objects;app
咱們若是 t1 和 t2 就是 兩個超級大表, 要執行的 SQL: select * from t1,t2 where t1.object_id=t2.object_id;函數
若是t1 t2 都是幾十個GB 或者更大。 那麼你懂的。上面的SQL基本上是跑不出結果的。oop
有些人在想,開個並行不就得了,用並行 hash hash 算法跑SQL,事實上是不可以的。緣由很少說了。post
咱們可以利用MPP數據庫架構(Greenplum/Teradata/vertica)思想。或者是利用HADOOP的思想來對上面的SQL進行優化。大數據
MPP架構/HADOOP架構的很是重要的思想就是把數據分割。把大的數據分割爲很是多份小的數據。而後再對小的進行關聯。那速度天然就快了。
在Oracle裏面怎麼把大數據切成小數據呢。有兩個辦法,一個是分區。另一個是分表。我這裏選擇的是分區,固然了看了這篇文章你也可以分表。
建立一個表P1。在T1的表結構基礎上多加一個字段HASH_VALUE,並且依據HASH_VALUE進行LIST分區
CREATE TABLE P1(
HASH_VALUE NUMBER,
OWNER VARCHAR2(30),
OBJECT_NAME VARCHAR2(128),
SUBOBJECT_NAME VARCHAR2(30),
OBJECT_ID NUMBER,
DATA_OBJECT_ID NUMBER,
OBJECT_TYPE VARCHAR2(19),
CREATED DATE,
LAST_DDL_TIME DATE,
TIMESTAMP VARCHAR2(19),
STATUS VARCHAR2(7),
TEMPORARY VARCHAR2(1),
GENERATED VARCHAR2(1),
SECONDARY VARCHAR2(1),
NAMESPACE NUMBER,
EDITION_NAME VARCHAR2(30)
)
PARTITION BY list(HASH_VALUE)
(
partition p0 values (0),
partition p1 values (1),
partition p2 values (2),
partition p3 values (3),
partition p4 values (4)
)
相同的。在T2的表結構基礎上多加一個字段HASH_VALUE。並且依據HASH_VALUE進行LIST分區
CREATE TABLE P2(
HASH_VALUE NUMBER,
OWNER VARCHAR2(30),
OBJECT_NAME VARCHAR2(128),
SUBOBJECT_NAME VARCHAR2(30),
OBJECT_ID NUMBER,
DATA_OBJECT_ID NUMBER,
OBJECT_TYPE VARCHAR2(19),
CREATED DATE,
LAST_DDL_TIME DATE,
TIMESTAMP VARCHAR2(19),
STATUS VARCHAR2(7),
TEMPORARY VARCHAR2(1),
GENERATED VARCHAR2(1),
SECONDARY VARCHAR2(1),
NAMESPACE NUMBER,
EDITION_NAME VARCHAR2(30)
)
PARTITION BY list(HASH_VALUE)
(
partition p0 values (0),
partition p1 values (1),
partition p2 values (2),
partition p3 values (3),
partition p4 values (4)
)
注意:P1和P2表的分區必須如出一轍
delete t1 where object_id is null;
commit;
delete t1 where object_id is null;
commit;
insert into p1
select ora_hash(object_id,4), a.* from t1 a; ---工做中用append parallel並行插入
commit;
insert into p2
select ora_hash(object_id,4), a.* from t2 a; ---工做中用append parallel並行插入
commit;
這樣就把 T1 和 T2的表的數據轉移到 P1 和 P2 表中了
那麼以前執行的 select * from t1,t2 where t1.object_id=t2.object_id 事實上就等價於如下5個SQL了
select * from p1,p2 where p1.object_id=p2.object_id and p1.hash_value=0 and p2.hash_value=0;
select * from p1,p2 where p1.object_id=p2.object_id and p1.hash_value=1 and p2.hash_value=1;
select * from p1,p2 where p1.object_id=p2.object_id and p1.hash_value=2 and p2.hash_value=2;
select * from p1,p2 where p1.object_id=p2.object_id and p1.hash_value=3 and p2.hash_value=3;
select * from p1,p2 where p1.object_id=p2.object_id and p1.hash_value=4 and p2.hash_value=4;
工做中。大表拆分爲多少個分區,請本身推斷。
另一個需要注意的就是ORA_HASH函數
oracle中的hash分區就是利用的ora_hash函數
partition by hash(object_id) 等價於 ora_hash(object_id,4294967295)
ora_hash(列,hash桶) hash桶默認是4294967295 可以設置0到4294967295
ora_hash(object_id,4) 會把object_id的值進行hash運算,而後放到 0,1,2,3,4 這些桶裏面,也就是說 ora_hash(object_id,4) 僅僅會產生 0 1 2 3 4
有興趣的同窗可以本身去測試速度。
生產庫採用這樣的優化方法,以前需要跑一天一晚上的SQL。在1小時內完畢。
爲了簡便,可以使用PLSQL編寫存儲過程封裝上面操做。
固然了,若是使用hadoop 或者 greenplum 有另外的優化方法這裏就不作介紹了。
道森出品必屬精品 抄襲翻版必究