面試官:聽說你sql寫的挺溜的,你說一說查詢sql的執行過程。

當希望Mysql能夠高效的執行的時候,最好的辦法就是清楚的瞭解Mysql是如何執行查詢的,只有更加全面的瞭解SQL執行的每一個過程,才能更好的進行SQl的優化。

當執行一條查詢的SQl的時候大概發生了一下的步驟:

  1. 客戶端發送查詢語句給服務器。
  2. 服務器首先檢查緩存中是否存在該查詢,若存在,返回緩存中存在的結果。若是不存在就進行下一步。
  3. 服務器進行SQl的解析、語法檢測和預處理,再由優化器生成對應的執行計劃。
  4. Mysql的執行器根據優化器生成的執行計劃執行,調用存儲引擎的接口進行查詢。
  5. 服務器將查詢的結果返回客戶端。

Mysql的執行的流程

Mysql的執行的流程圖如下圖所示:

這裏以一個實例進行說明Mysql的的執行過程,新建一個User表,如下:

// 新建一個表
DROP TABLE IF EXISTS User;
CREATE TABLE `User` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(10) DEFAULT NULL,
  `age` int DEFAULT 0,
  `address` varchar(255) DEFAULT NULL,
  `phone` varchar(255) DEFAULT NULL,
  `dept` int,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=40 DEFAULT CHARSET=utf8;

// 並初始化數據,如下
INSERT INTO User(name,age,address,phone,dept)VALUES('張三',24,'北京','13265543552',2);
INSERT INTO User(name,age,address,phone,dept)VALUES('張三三',20,'北京','13265543557',2);
INSERT INTO User(name,age,address,phone,dept)VALUES('李四',23,'上海','13265543553',2);
INSERT INTO User(name,age,address,phone,dept)VALUES('李四四',21,'上海','13265543556',2);
INSERT INTO User(name,age,address,phone,dept)VALUES('王五',27,'廣州','13265543558',3);
INSERT INTO User(name,age,address,phone,dept)VALUES('王五五',26,'廣州','13265543559',3);
INSERT INTO User(name,age,address,phone,dept)VALUES('趙六',25,'深圳','13265543550',3);
INSERT INTO User(name,age,address,phone,dept)VALUES('趙六六',28,'廣州','13265543561',3);
INSERT INTO User(name,age,address,phone,dept)VALUES('七七',29,'廣州','13265543562',4);
INSERT INTO User(name,age,address,phone,dept)VALUES('八八',23,'廣州','13265543563',4);
INSERT INTO User(name,age,address,phone,dept)VALUES('九九',24,'廣州','13265543564',4);

現在針對這個表發出一條SQl查詢:查詢每個部門中25歲以下的員工個數大於3的員工個數和部門編號,並按照人工個數降序排序和部門編號升序排序的前兩個部門。

SELECT dept,COUNT(phone) AS num FROM User WHERE age< 25 GROUP BY dept HAVING num >= 3 ORDER BY num DESC,dept ASC LIMIT 0,2;

執行連接器

開始執行這條sql時,會檢查該語句是否有權限,若是沒有權限就直接返回錯誤信息,有權限會進行下一步,校驗權限的這一步是在圖一的連接器進行的,對連接用戶權限的校驗。

執行檢索內存

相連建立之後,履行查詢語句的時候,會先行檢索內存,Mysql會先行冗餘這個sql與否履行過,以此Key-Value的形式平緩適用內存中,Key是檢索預定,Value是結果集

假如內存key遭擊中,便會間接回到給客戶端,假如沒命中,便會履行後續的操作,完工之後亦會將結果內存上去,當下一次進行查詢的時候也是如此的循環操作。

執行分析器

分析器主要有兩步:(1)詞法分析(2)語法分析

詞法分析主要執行提煉關鍵性字,比如select,提交檢索的表提交字段名提交檢索條件。語法分析主要執行辨別你輸出的sql與否準確,是否合乎mysql的語法

當Mysql沒有命中內存的時候,接着執行的是 FROM student 負責把數據庫的表文件加載到內存中去,WHERE age< 60,會把所示表中的數據進行過濾,取出符合條件的記錄行,生成一張臨時表,如下圖所示。

GROUP BY dept 會把上圖的臨時表分成若干臨時表,切分的過程如下圖所示:


查詢的結果只有部門2和部門3纔有符合條件的值,生成如上兩圖的臨時表。接着執行SELECT後面的字段,SELECT後面可以是表字段也可以是聚合函數

這裏SELECT的情況與是否存在GROUP BY有關,若是不存在Mysql直接按照上圖內存中整列讀取。若是存在分別SELECT臨時表的數據。

最後生成的臨時表如下圖所示:

緊接着執行HAVING num>2過濾員工數小於等於2的部門,對於WHEREHAVING都是進行過濾,那麼這兩者有什麼不同呢?

第一點是WHERE後面只能對錶字段進行過濾,不能使用聚合函數,而HAVING可以過濾表字段也可以使用聚合函數進行過濾。

第二點是WHERE是對執行from USer操作後,加載表數據到內存後,WHERE是對原生表的字段進行過濾,而HAVING是對SELECT後的字段進行過濾,也就是WHERE不能使用別名進行過濾

因爲執行WHERE的時候,還沒有SELECT,還沒有給字段賦予別名。接着生成的臨時表如下圖所示:

最後在執行ORDER BY後面的排序以及limit0,2取得前兩個數據,因爲這裏數據比較少,沒有體現出來。最後生成得結果也是如上圖所示。接着判斷這個sql語句是否有語法錯誤關鍵性詞與否準確等等。

執行優化器

查詢優化器會將解析樹轉化成執行計劃。一條查詢可以有多種執行方法,最後都是返回相同結果。優化器的作用就是找到這其中最好的執行計劃

生成執行計劃的過程會消耗較多的時間,特別是存在許多可選的執行計劃時。如果在一條SQL語句執行的過程中將該語句對應的最終執行計劃進行緩存。

相似的語句再次被輸入服務器時,就可以直接使用已緩存的執行計劃,從而跳過SQL語句生成執行計劃的整個過程,進而可以提高語句的執行速度。


MySQL使用基於成本的查詢優化器。它會嘗試預測一個查詢使用某種執行計劃時的成本,並選擇其中成本最少的一個。

執行執行器

由優化器生成得執行計劃,交由執行器進行執行,執行器調用存儲引擎得接口,存儲引擎獲取數據並返回,結束整個查詢得過程。

這裏之講解了select的過程,對於update這些修改數據或者刪除數據的操作,會涉及到事務,會使用兩個日誌模塊,redo log和binlog日誌。具體對這兩個日誌的介紹請看着一篇文章。

以前的Mysql的默認存儲引擎MyISAM引擎是沒redo log的,而現在的默認存儲引擎InnoDB引擎便是透過redo 複雜度來擁護事務的,保證事務能夠準確的回滾或者提交,保證事務的ACID。

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