掌握MySQL連線查詢到底什麼是驅動表
準備我們需要的表結構和資料
兩張表 studnet(學生)表和score(成績)表, 建立表的SQL語句如下
CREATE TABLE `student` ( `id` int(11) NOT NULL, `no` varchar(20) DEFAULT NULL, `name` varchar(20) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `score` ( `id` int(11) NOT NULL, `no` varchar(20) DEFAULT NULL, `chinese` double(4,0) DEFAULT NULL, `math` double(4,0) DEFAULT NULL, `engilsh` double(4,0) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
MySQL連線查詢分為以下三種
left join 左連線,用法如下,這種查詢會把左表(student)所有資料查詢出來,右表不存在的用空表示,結果圖如下
select * from student s1 left join score s2 on s1.on = s2. on
right join 右連線, 用法如下,這種查詢會把右表(score)所有資料查詢出來,左表不存在的用空表示,結果圖如下
select * from student s1 right join score s2 on s1.no = s2.no
inner join 內連線,用法如下,這種查詢會把左右表都存在的資料查詢出來,不存在資料忽略,結果圖如下
select * from student s1 inner join score s2 on s1.no = s2.no
連線查詢中需要注意的點
什麼是驅動表,什麼是被驅動表,這兩個概念在查詢中有時容易讓人搞混,有下面幾種情況,大家需要了解。
- 當連線查詢沒有where條件時,左連線查詢時,前面的表是驅動表,後面的表是被驅動表,右連線查詢時相反,內連線查詢時,哪張表的資料較少,哪張表就是驅動表
- 當連線查詢有where條件時,帶where條件的表是驅動表,否則是被驅動表
怎麼確定我們上面的兩種情況呢,執行計劃是不會騙人的,我們針對上面情況分別看看執行計劃給出的答案
首先第一種情況,student表中3條資料,score表中2條資料,但兩張表中只有一條資料是關聯的(編號是1),看如下SQL查詢
//左連線查詢
explain select * from student s1 left join score s2 on s1.no = s2.no
//右連線查詢
explain select * from student s1 right join score s2 on s1.no = s2.no
//內連線查詢
explain select * from student s1 inner join score s2 on s1.no = s2.no
執行計劃中靠前的表是驅動表,我們看下面三種圖中,是不是全度符合情況一,第一張圖中s1是驅動表,第二張圖中s2是驅動表,第三種途中s2是驅動表
其次第二種情況,還是上面三種SQL語句,我們分別加上where條件,再來看看執行計劃的結果是什麼樣呢?
//左連線查詢
explain select * from student s1 left join score s2 on s1.no = s2.no
where s2. no = 1
//右連線查詢
explain select * from student s1 right join score s2 on s1.no = s2.no
where s1.no = 1
//內連線查詢
explain select * from student s1 inner join score s2 on s1.no = s2.no
where s1.no = 1
我們看下面三種執行計劃結果,全都以where條件為準了,而且跟上面情況一的都相反了,因此情況二也是得到了驗證.
連線查詢優化
要理解連線查詢優化,得先理解連線查詢的演算法,連線查詢常用的一共有兩種演算法,我們簡要說明一下
Simple Nested-Loop Join Algorithms (簡單巢狀迴圈連線演算法)
比如上面的查詢中,我們確定了驅動表和被驅動表,那麼查詢過程如下,很簡單,就是雙重迴圈,從驅動表中迴圈獲取每一行資料,再在被驅動表匹配滿足條件的行。
for (row1 : 驅動表) {
for (row2 : 被驅動表){
if (conidtion == true){
send client
}
}
}
Index Nested-Loop Join Algorithms (索引巢狀迴圈連線演算法)
上面雙重for迴圈的查詢中,相信很多研發人員看到這種情況第一個想法就是效能問題,是的,join查詢的優化思路就是小表驅動大表,而且在大表上建立索引(也就是被動表建立索引),如果驅動表建立了索引,MySQL是不會使用的
for (row1 : 驅動表) {
索引在被驅動表中命中,不用再遍歷被驅動表了
}
Block Nested-Loop Join Algorithm(基於塊的連線巢狀迴圈演算法)
其實很簡單就是把一行變成了一批,塊巢狀迴圈(BNL)巢狀演算法使用對在外部迴圈中讀取的行進行緩衝,以減少必須讀取內部迴圈中的表的次數。例如,如果將10行讀入緩衝區並將緩衝區傳遞到下一個內部迴圈,則可以將內部迴圈中讀取的每一行與緩衝區中的所有10行進行比較。這將內部表必須讀取的次數減少了一個數量級。
MySQL連線緩衝區大小通過這個引數控制 : join_buffer_size
MySQL連線緩衝區有一些特徵,只有無法使用索引時才會使用連線緩衝區;聯接中只有感興趣的列儲存在其聯接緩衝區中,而不是整個行;為每個可以緩衝的連線分配一個緩衝區,因此可以使用多個連線緩衝區來處理給定查詢;在執行連線之前分配連線緩衝區,並在查詢完成後釋放連線緩衝區
所以查詢時最好不要把 * 作為查詢的欄位,而是需要什麼欄位查詢什麼欄位,這樣緩衝區能夠緩衝足夠多的行。
從上面的執行計劃中其實我們已經看到了 useing join buffer了,是的,那是因為我們對兩張表都有建立索引
三種演算法優先順序
第一種演算法忽略,MySQL不會採用這種的,當我們對被驅動表建立了索引,那麼MySQL一定使用的第二種演算法,當我們沒有建立索引或者對驅動表建立了索引,那麼MySQL一定使用第三種演算法
MySQL連線演算法官方文件
https://dev.mysql.com/doc/refman/8.0/en/nested-loop-joins.h