資料庫 Join語句優化

2021-10-22 03:00:55 字數 2597 閱讀 8636

關聯查詢的演算法

• nested-loop join 演算法

• block nested-loop join 演算法

nested-loop join 演算法

乙個簡單的 nested-loop join(nlj) 演算法一次一行迴圈地從第一張表(稱為驅動表)中讀取行,在這行資料中取到關聯字段,根據關聯欄位在另一張表(被驅動表)裡取出滿足條件的行,然後取出兩張表的結果合集。

我們試想一下,如果在被驅動表中這個關聯字段沒有索引,那麼每次取出驅動表的關聯欄位在被驅動表查詢對應的資料時,都會對被驅動表做一次全表掃瞄,成本是非常高的(比如驅動表資料量是 m,被驅動表資料量是 n,則掃瞄行數為 m * n )。

好在 mysql 在關聯欄位有索引時,才會使用 nlj,如果沒索引,就會使用 block nested-loop join。我們先來看下在有索引情況的情況下,使用 nested-loop join 的場景(稱為:index nested-loop join)。

從執行計畫中可以看到這些資訊:

• 驅動表是 t2,被驅動表是 t1。原因是:explain 分析 join 語句時,在第一行的就是驅動表;選擇 t2 做驅動表的原因:如果沒固定連線方式優化器會優先選擇小表做驅動表。所以使用 inner join 時,前面的表並不一定就是驅動表。

• 使用了 nlj。原因是:一般 join 語句中,如果執行計畫 extra 中未出現 using join buffer (***);則表示使用的 join 演算法是 nlj。

sql1 的大致流程如下:

• 從表 t2 中讀取一行資料;

• 從第 1 步的資料中,取出關聯字段 a,到表 t1 中查詢;

• 取出表 t1 中滿足條件的行,跟 t2 中獲取到的結果合併,作為結果返回給客戶端;

• 重複上面 3 步。

在這個過程中會讀取 t2 表的所有資料,因此這裡掃瞄了 100 行,然後遍歷這 100 行資料中欄位 a 的值,根據 t2 表中 a 的值索引掃瞄 t1 表中的對應行,這裡也掃瞄了 100 行。因此整個過程掃瞄了 200 行。

block nested-loop join 演算法

block nested-loop join(bnl) 演算法的思想是:把驅動表的資料讀入到 join_buffer 中,然後掃瞄被驅動表,把被驅動表每一行取出來跟 join_buffer 中的資料做對比,如果滿足 join 條件,則返回結果給客戶端。

在 extra 發現 using join buffer (block nested loop),這個就說明該關聯查詢使用的是 bnl 演算法。

我們再看下 sql2 的執行流程:

• 把 t2 的所有資料放入到 join_buffer 中

• 把錶 t1 中每一行取出來,跟 join_buffer 中的資料做對比

• 返回滿足 join 條件的資料

在這個過程中,對錶 t1 和 t2 都做了一次全表掃瞄,因此掃瞄的總行數為10000(表 t1 的資料總量) + 100(表 t2 的資料總量) = 10100。並且 join_buffer 裡的資料是無序的,因此對錶 t1 中的每一行,都要做 100 次判斷,所以記憶體中的判斷次數是 100 * 10000= 100 萬次。

如果被驅動表的關聯欄位沒索引,為什麼會選擇使用 bnl 演算法而不繼續使用 nested-loop join 呢?

在被驅動表的關聯欄位沒索引的情況下,比如 sql2:

如果使用 nested-loop join,那麼掃瞄行數為 100 * 10000 = 100萬次,這個是磁碟掃瞄。

如果使用 bnl,那麼磁碟掃瞄是 100 + 10000=10100 次,在記憶體中判斷 100 * 10000 = 100萬次。

顯然後者磁碟掃瞄的次數少很多,因此是更優的選擇。因此對於 mysql 的關聯查詢,如果被驅動表的關聯欄位沒索引,會使用 bnl 演算法。

小表做驅動表

前面說到,index nested-loop join 演算法會讀取驅動表的所有資料,首先掃瞄的行數是驅動表的總行數(假設為 n),然後遍歷這 n 行資料中關聯欄位的值,根據驅動表中關聯欄位的值索引掃瞄被驅動表中的對應行,這裡又會掃瞄 n 行,因此整個過程掃瞄了 2n 行。當使用 index nested-loop join 演算法時,掃瞄行數跟驅動表的資料量成正比。所以在寫 sql 時,如果確定被關聯欄位有索引的情況下,建議用小表做驅動表。

臨時表多數情況我們可以通過在被驅動表的關聯欄位上加索引來讓 join 使用 nlj 或者 bka,但有時因為某條關聯查詢只是臨時查一次,如果再去新增索引可能會浪費資源,那麼有什麼辦法優化呢?

建立一張臨時表

create temporary table + 表名
臨時表與原表結構一致,只是多了想要新增的索引。

當遇到 bnl 的 join 語句,如果不方便在關聯欄位上新增索引,不妨嘗試建立臨時表,然後在臨時表中的關聯欄位上新增索引,然後通過臨時表來做關聯查詢。

重新連線mysql後臨時表會消失。

資料庫優化 sql語句優化

1 group by語句優化 因為mysql對所有group by的字段進行排序,所以如果包含group by但是想要避免排序結果的消耗,可以指定order by null來進行group by的排序。select id,sun moneys from sales group by id expla...

資料庫 sql語句優化

寫操作 write 1 如果你同時從同一客戶插入很多行,使用多個值表的insert語句。這比使用分開insert語句快 在一些情況中幾倍 insert into test values 1,2 1,3 1,4 2 如果你從不同客戶插入很多行,能通過使用insert delayed語句得到更高的速度。...

SQL資料庫使用JOIN的優化方法

很早以前,也是一提到sql server,就覺得它的效能沒法跟oracle相比,一提到大資料處理就想到oracle。自己一路走來,在本地blog上記錄了很多優化方面的 post,對的錯的都有,沒有時間系列的整理出來,這篇文章將join方法的概念稍微整理在一起,給大家個參考。通過查資料了解裡面提到的各...