兩張超級大表join優化


一個簡單的兩表關聯,SQL跑了差不多一天一夜,這兩個表都非常巨大,每個表都有幾十個G,數據量每個表有20多億,表的字段也特別多。

相信大家也知道SQL慢在哪里了,單個進程的PGA 是絕對放不下幾十個G的數據,這就會導致消耗大量temp tablespace,SQL慢就是慢在temp來回來回來回...的讀寫數據。  

先創建2個測試表 t1,t2 數據來自dba_objects

create table t1 as select * from dba_objects;

create table t2 as select * from dba_objects;

我們假設 t1 和 t2 就是 兩個超級大表, 要運行的 SQL:   select * from t1,t2 where t1.object_id=t2.object_id;

假設t1 t2 都是幾十個GB 或者更大, 那么你懂的,上面的SQL基本上是跑不出結果的。

有些人在想,開個並行不就得了,用並行 hash hash 算法跑SQL,其實是不可以的,原因不多說了。

我們可以利用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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM