掌握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

連接查詢中需要注意的點

什麼是驅動表,什麼是被驅動表,這兩個概念在查詢中有時容易讓人搞混,有下面幾種情況,大家需要了解。

  1. 當連接查詢沒有where條件時,左連接查詢時,前面的表是驅動表,後面的表是被驅動表,右連接查詢時相反,內連接查詢時,哪張表的數據較少,哪張表就是驅動表
  2. 當連接查詢有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.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章