天天看點

多表使用use_hash hint,你寫對了嗎?

oracle的online document裡面,對use_hash的hint文法是這樣描述的:

http://docs.oracle.com/cd/E16338_01/server.112/b56299/img_text/use_hash_hint.htm :

/*+ USE_HASH ( [ @ queryblock ] tablespec [ tablespec ]... ) */           

複制

而大部分的開發人員也确實是這樣寫的: use_hash(a b) ,這個确實沒問題。

當關聯的表超過2個的時候,寫成use_hash(a b c d)有沒有問題呢?

我們先來看一個test case,這個案例根據客戶真實案例改編,模拟的是在表關聯條件複雜的情況下,優化器對表關聯後的結果集估值過小,可能使用錯誤的執行計劃,希望通過增加use_hash hint來優化SQL。

--建立4個表

create table tv as select rownum as id,a.* from dba_objects a;

create table tt as select * from tv;

create table tw as select * from tv;

create table tu as select * from tv;

--收集統計資訊

exec dbms_stats.gather_table_stats(user,'tw');

exec dbms_stats.gather_table_stats(user,'tt');

exec dbms_stats.gather_table_stats(user,'tu');

exec dbms_stats.gather_table_stats(user,'tv');

SQL:

select /*+ use_hash(v t u w) */

count(*) from tv v,tu u,tw w,tt t

where

t.id=v.id and t.object_name=upper(v.object_name) and

w.id=u.id and

v.created between t.created and t.last_ddl_time and

v.created between u.created and u.last_ddl_time and

t.object_id=w.object_id and w.created=v.created;

真實案例的情況是:SQL正常執行時間3.4分鐘,某天TV表delete一些記錄後,執行了将近20分鐘還沒有完成,而其中最重要的變化就是執行計劃其中的一個hash join變成了nested loops,雖然hint中已經指定全部表要use_hash。

這個模拟的SQL展示的就是真實案例出現異常的情況。其中一個步驟使用了nested loops,大概要執行4分鐘左右才能完(測試時可以cancel),全部hash的執行計劃不到1秒。

目前hint生成的執行計劃:

多表使用use_hash hint,你寫對了嗎?

這個執行計劃出現了nested loops的情況,沒有按照hint的訓示全部使用hash_join,說明這種hint的寫法确實是有問題的。

那麼,正确的寫法是怎樣的呢?

問題的關鍵在于:

多表的use_hash,一定要配合leading的hint使用。

根據sql的關聯條件,我們增加leading的hint再測試一下:

select /*+ leading(v t w u) use_hash(u v t w) */

count(*) from tv v,tu u,tw w,tt t

where

t.id=v.id and t.object_name=upper(v.object_name) and

w.id=u.id and

v.created between t.created and t.last_ddl_time and

v.created between u.created and u.last_ddl_time and

t.object_id=w.object_id and w.created=v.created;

這次,SQL隻需要不到1秒時間就能跑出結果了,執行計劃也正是我們需要的全部hash join:

多表使用use_hash hint,你寫對了嗎?

在優化器内部生成的标準執行計劃outline data中,上面的hint最終被轉化成這樣:

多表使用use_hash hint,你寫對了嗎?

(有沒有注意到,其中leading的第一個表沒有做use_hash(V)?

這是因為,有第二個表的use_hash(t)的存在,t表做use_hash(t),跟誰做?當然是和第一個表V)。

結論:

我們在寫多表use_hash(use_nl也一樣)hint的時候,use_hash的括号裡面是可以放多個表(順序無關),但是一定要結合leading 的hint,才能保證優化器不使用其他的join方式。 leading裡面表的順序非常關鍵哦,搞錯了會帶你去見笛卡爾(cartesian join)。