當希望Mysql能夠高效的執行的時候,最好的辦法就是清楚的瞭解Mysql是如何執行查詢的,只有更加全面的瞭解SQL執行的每一個過程,才能更好的進行SQl的優化。
當執行一條查詢的SQl的時候大概發生了一下的步驟:
- 客戶端發送查詢語句給服務器。
- 服務器首先檢查緩存中是否存在該查詢,若存在,返回緩存中存在的結果。若是不存在就進行下一步。
- 服務器進行SQl的解析、語法檢測和預處理,再由優化器生成對應的執行計劃。
- Mysql的執行器根據優化器生成的執行計劃執行,調用存儲引擎的接口進行查詢。
- 服務器將查詢的結果返回客戶端。
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的部門,對於WHERE
和HAVING
都是進行過濾,那麼這兩者有什麼不同呢?
第一點是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。