mysql如何執行關聯查詢與優化
一、前言
在數據庫中執行查詢(select)在我們工作中是非常常見的,工作中離不開CRUD,在執行查詢(select)時,多表關聯也非常常見,我們用的也比較多,那么mysql內部是如何執行關聯查詢的呢?它又做了哪些優化呢?今天我們就來揭開mysql關聯查詢的神秘面紗。
二、mysql如何執行關聯查詢
mysql關聯執行的策略很簡單:mysql對任何關聯都執行嵌套循環關聯操作。即:mysql先在一個表中循環取出單條數據,然后再嵌套循環到下一個表中尋找匹配的行,依次下去,直到找到所有表中匹配的行為止。然后根據各個表匹配的行,返回查詢中需要的各個列。如果mysql在最后一個關聯表無法找到更多的行,它將返回上一層關聯表,看看能否找到更多的匹配記錄,以此類推迭代執行。
按照這種方式,mysql查找第一個表的記錄,再嵌套查詢下一個關聯表,然后回溯到上一個表,這正如其名——“嵌套循環關聯”。看一下下面的例子:
SELECT t1.column1, t2.column2 FROM tb1 t1 INNER JOIN tb2 t2 ON t1.column3 = t2.column3 WHERE t1.column1 IN (4, 6)
假設mysql按照查詢中的表順序進行關聯操作,我們可以用偽代碼表示其過程:
outer_iter = iterator over t1 WHERE column3 IN (4, 6) outer_row = outer_iter.next
WHILE outer_row inner_iter = iterator over t2 WHERE column3 = outer_row.column3 inner_row = inner_iter.next
WHILE inner_row output [ outer_row.column1,inner_row.column2 ] inner_row = inner_iter.next
END outer_row = outer_iter.next
END
上面的執行過程對於單表查詢和多表關聯查詢都適用,如果只是單表查詢,那么只需要完成最外層的循環操作即可。如果關聯中存在外連接,上面的過程仍然適用,我們只需略作修改。查詢sql如下:
SELECT t1.column1, t2.column2 FROM tb1 t1 LEFT OUTER JOIN tb2 t2 ON t1.column3 = t2.column3 WHERE t1.column1 IN (4, 6)
對應的偽代碼修改如下:
outer_iter = iterator over t1 WHERE column3 IN (4, 6) outer_row = outer_iter.next
WHILE outer_row inner_iter = iterator over t2 WHERE column3 = outer_row.column3 inner_row = inner_iter.next
IF inner_row WHILE inner_row output [ outer_row.column1,inner_row.column2 ] inner_row = inner_iter.next
END
ELSE output [ outer_row.column1,NULL ] END outer_row = outer_iter.next
END
如果用圖表示關聯查詢的過程,圖示如下,請從左至右,從上至下看這幅圖:
t1 | t2 | 結果行 |
column1=4,column3=1 | column3=1,column2=1 | column1=4,column3=1 |
column3=1,column2=2 | column1=4,column3=2 | |
column3=1,column2=3 | column1=4,column3=3 | |
column1=6,column3=2 | column3=2,column2=1 | column1=6,column3=1 |
column3=2,column2=2 | column1=6,column3=2 | |
column3=2,column2=3 | column1=6,column3=3 |
mysql的關聯方式也可以由一棵樹表示,它是一個左側深度優先樹:
三、關聯查詢優化器
mysql優化器最重要的一部分就是關聯查詢優化,它決定了多個表關聯時的順序。通常多表關聯的時候,可以有多種不同的關聯順序來獲得相同的結果。關聯查詢優化器則通過評估不同順序時的成本來選擇一個代價最小的關聯順序。
大家看一下下面的查詢,它可以通過不同的關聯順序得到相同的結果:
SELECT u.realname, u.mobile, c.`name` FROM
USER u INNER JOIN user_company uc ON u.id = uc.user_id
INNER JOIN company c ON uc.company_id = c.id
按照上面的關聯執行規則,我們可以給出執行計划,mysql可以從user表開始,通過user_company表的user_id列找到對應的company_id,然后再通過company表的主鍵找到對應的記錄。我們執行了mysql的explain,得出的結果如下:
這和我們給出的執行順序不一致,這樣的效率是否更高呢?我們使用STRAIGHT_JOIN關鍵字得出的分析結果如下:
我們分析一下mysql為什么會改變關聯的順序,我們可以看到改變順序后,第一個關聯表只需要掃描很少的行數,第二個、第三個關聯表的掃描項也是不同的。uc表只有480條記錄,而u表有2300條記錄。如果先掃描uc表,只返回480條記錄,然后進行嵌套循環查詢,如果先掃描u表,則返回2300條記錄。換句話說,更改順序后,查詢可以進行更少的嵌套循環和回溯操作。
通過這個例子,我們可以看到mysql是如何選擇合適的順序讓查詢執行的成本更低的。重新定義關聯順序是優化器的一個重要的功能,它嘗試在所有關聯順序中選擇一個成本最小的來生成執行計划樹。
至此,mysql是如何進行關聯查詢的,以及優化,已經介紹完了,歡迎大家多多交流。