oracle sql最佳化的核心基礎-記憶體中三大連線方式

經常寫sql的人,基本都會問怎麼最佳化sql語句,做sql的效能調優,小編認為最核心的掌握表連線的三大方式。在資料庫中,join操作是最費資源的,實際生產環境中,一條慢sql,很多時候是資料庫選擇了錯誤的連線方式,導致查詢緩慢。oracle 資料庫的表連線,在記憶體中有三種連線方式,1。nest loop join 2。hash join 3。merge join

1。nest loop join

oracle sql最佳化的核心基礎-記憶體中三大連線方式

這種連線方式是早期資料庫中就存在的,可以通俗地叫它“迴圈連線”,為什麼叫迴圈連線呢?

如圖 Outer Table 就表示驅動表,row1表示第一行資料,它會去關聯 被驅動表(inner table),row1第一次去找inner table 時,屬於一次隨機讀,被驅動表有M條,row1就會去關聯M次,row1對inner table的訪問包括一次隨機讀,M-1條順序讀,相當於找inner table要花費一個隨機讀,找到之後,對inner table 遍歷就屬於順序讀了,隨機讀的效能比順序讀差一千倍左右,一個隨機讀可以想象成10ms,一個順序讀大概0。1ms。假設驅動表N條資料,被驅動表M條資料,就會產生N*M次關聯,其中N次隨機讀,inner table要被全表掃描N次。隨機讀,全表掃描,都是很費資源的,所以說驅動表不能太大,N不能太大。

我們經常聽說說驅動表要選擇小一點的表,根本原因之一就是如此,驅動表的每一條資料都要去把inner table全表掃描一遍,就像程式裡面的for迴圈,for(驅動表每條資料取一次){ 每條驅動表的資料 都全表掃面inner table 做連線匹配 },“迴圈連線”這個名字就是這麼來的。

舉個極端的例子,表1 有10條資料,表2有100萬條資料,如果選大表作為驅動表,那麼表1短時間內要被訪問一百萬次,表2每次去關聯表1都是一次隨機讀,100萬個隨機讀很費時間如果調整一下,把表1這個小表,當作驅動表,這時候只有10個隨機讀,10次表2的全表掃描,全表掃描是順序讀,效能還勉強看的過去。

透過上面的解釋,應該清楚為什麼nest loop join 時驅動表要選擇小表,主要原因是因為隨機讀和全表掃描。那有什麼辦法最佳化呢?這時候很多書本會告訴你建立連線列的索引,為什麼建連線列的索引就可以最佳化呢?

oracle sql最佳化的核心基礎-記憶體中三大連線方式

建立連線列索引後,進行連線匹配的階段,壓根就不需要訪問表,直接訪問索引就可以進行匹配,索引的大小相對於表還是很小的,兩個索引進行匹配,可以想象成兩個小表進行匹配,大的資料才有效能問題,小的東西當然快,這只是次要原因,主要原因還是索引結構帶來的加速,有些人認為建連線列索引是為了消除大量隨機讀,我不這麼認為,我們可以認為索引就是有結構的小表,兩個索引之間的匹配還是像表之間的nest loop一樣,n條索引資料,產生n條隨機讀,並不會減少隨機讀,索引帶來的巨大優勢是減少被驅動表的順序讀,沒有索引時,被驅動表只能全表掃描,被驅動表1千方條資料,就要一千萬的順序讀,有索引後,因為索引的結構,儲存千萬級別的資料只要5層索引結構,千萬級別資料量中搜索一條資料,只要5次IO,千萬次和5次,差別很大。連線列建立索引並不能消除大量隨機讀,大量隨機讀本身就是nest loop join的缺陷,後面就提出的hash join能改善這種大量隨機讀導致的效能問題。建立連線列索引後,nest loop join對於返回結果集特別小的查詢,十分友好,比如驅動表經過where過濾後只有10條資料,這時整個驅動表不需要置入記憶體,只要拿這10條資料,與被驅動表的連線列索引匹配,速度很快。返回資料少,用nest loop join,返回資料多,用hash join (hash 需要把驅動表置入記憶體,如果只返回少量資料,還要用hash join有點浪費記憶體資源)

今天只講nest loop join,下次再講hash join ,merge join,今天主要核心知識點:隨機讀,順序讀,迴圈連線,索引層級