mysql 做爲一個關係型數據庫,在國內使用應該是最普遍的。也許你司使用 Oracle、Pg 等等,可是大多數互聯網公司,好比我司使用得最多的仍是 Mysql,重要性不言而喻。前端
事情是這樣的,某天我司小胖問我執行select * from table,數據庫底層到底發生了啥?從而咱們獲得數據呢?如下把我給問住了,爲此我查閱了大量的書籍、博客。因而就有了這篇文章。java
假設如今我有張 user 表,只有兩列,一列 id 自增的,一列 name 是 varchar 類型。建表語句是這樣的:mysql
CREATE TABLE IF NOT EXISTS `user`( `id` INT UNSIGNED AUTO_INCREMENT, `name` VARCHAR(100) NOT NULL, PRIMARY KEY ( `id` ) )ENGINE=InnoDB DEFAULT CHARSET=utf8;
小胖的問題就是下面這個語句的執行過程。面試
select * from user where id = 1;
要想理解這個問題就必需要知道 mysql 的內部架構。爲此,我畫了張 mysql 的架構圖(你也能夠理解爲 sql 查詢語句的執行過程),以下所示:算法
首先 msql 分爲 server 層和存儲引擎層兩個部分。server 層包括四個功能模塊,分別是:鏈接器、查詢緩存、優化器、執行器。這一層負責了 mysql 的全部核心工做,好比:內置函數、存儲過程、觸發器以及視圖等。sql
而存儲引擎層則是負責數據的存取。注意,存儲引擎在 mysql 是可選的,常見的還有:InnoDB、MyISAM 以及 Memory等,最經常使用的就是 InnoDB。如今默認的存儲引擎也是它(從 mysql 5.5.5 版本開始),你們能夠看到我上面的建表語句就是指定了 InnoDB 引擎。固然,你不指定的話默認也是它。數據庫
因爲存儲引擎是可選的,因此 mysql 中,全部的存儲引擎實際上是共用一個 server層的。回到正題,咱們就以這張圖的流程來解決一下小胖的問題。編程
首先,數據庫要執行 sql,確定要先鏈接數據庫吧。這部分工做就是由鏈接器完成。它負責校驗帳戶密碼、獲取權限、管理鏈接數,最終與客戶端創建鏈接等工做。mysql 連接數據庫是這樣寫的:設計模式
mysql -h 127.0.0.1 -P 3306 -u root -p # 127.0.0.1 : ip 3306 : 端口 root : 用戶名
運行命令以後須要輸入密碼,固然也能夠跟在 -p 後面。不過不建議這麼作,會有密碼泄露的風險。緩存
輸入命令後,鏈接器根據你的帳戶名密碼驗證身份。這是會出現兩種狀況:
注意,我說的是此時查到的權限。就算你用管理員帳號修改了當前用戶的權限,此時已鏈接上的當前用戶不受影響,必需要重啓 mysql 新的權限纔會生效。
鏈接完成,若是後續沒有作任何事情,這個鏈接就處於空閒狀態。你能夠用 show processlist; 命令查看 mysql 的鏈接信息,以下圖,個人數據庫鏈接都是 Sleep 狀態的,除了執行 show processlist 操做的鏈接。
若是客戶端太長時間沒有操做,此鏈接將會自動斷開。這個時間默認是 8 小時,由參數 wait_timeout 控制。若是斷開之後繼續操做就會收到 "Lost connection to MySQL server during query"的錯誤。這時就必須重連才能執行請求。
數據庫裏面有長短鏈接之分,長鏈接:鏈接成功後不斷有請求,就會一直使用同一鏈接。短鏈接:每次執行完幾回請求就斷開鏈接,下次須要再創建。
因爲創建鏈接是比較耗時的操做,因此建議使用長鏈接。但這會有個問題長鏈接一直連着就會致使內存佔用過大,被系統強行沙雕。從而致使 MySQL 異常重啓。如何解決呢?兩個方法:
鏈接創建之後能夠執行 select 語句了。這就會來到第二步:查詢緩存。
查詢緩存中存儲的數據是 key-value 的形式,key 是查詢語句,value 是查詢的結果。邏輯是這樣的:先看看查詢緩存有沒該語句對應的 value?有則直接取出返回客戶端,無則繼續到數據庫執行語句。查出結果後會放一份到緩存中,再返回客戶端。
你可能發現緩存真的香,可是並不建議使用查詢緩存,由於有弊端。查詢緩存的失效很是頻繁,只有某個表有更新。它立刻失效了,對於常常更新的表來講,命中緩存的機率極低。它僅僅適用於那些不常常更新的表。
而 MySQL 彷佛也考慮到這點了。提供了 query_cache_type 參數,把它設置爲 DEMAND 就再也不適用韓村。而對於要使用緩存的語句則可用 SQL_CACHE 顯示指定,像這樣:
select SQL_CACHE * from user where id = 1;
PS:MySQL 8.0 及以上版本把查詢緩存刪掉了,以後再也沒有這塊功能了。
若是沒有命中緩存就進入分析器,這裏就是對 sql 進行分析。分析器會作詞法分析。你輸入的 sql 是啥,由啥組成,MySQL 都須要知道它們表明什麼。
首先根據 "select" 識別出這是查詢語句。字符串"user"識別成"表名 user"、字符串"id"識別成"列名id"。
以後進行語法分析,它會根據輸入的語句分析是否是符合 MySQL 的語法。具體表現就是 select、where、from 等關鍵字少了個字母,明顯不符合 MySQL 語法,此次就會報個語法錯誤的異常:它通常會提示錯誤行數,關注"use near"後面便可。
過了分析器,就來到了優化器。MySQL 是個聰明的仔,再執行以前會本身優化下客戶端傳過來的語句,看看那種執行起來不那麼佔內存、快一點。好比下面的 sql 語句:
select * from user u inner join role r on u.id = r.user_id where u.name = "狗哥" and r.id = 666
它能夠先從 user 表拿出 name = "狗哥" 記錄的 ID 值再跟 role 表內鏈接查詢,再判斷 role 表裏面 id 的值是否 = 666
也能夠反過來:先從 role 表拿出 id = 666 記錄的 ID 值再跟 user 表內鏈接查詢,在判斷 user 表裏面的 name 值是否 = "狗哥"。
兩種方案的執行結果是同樣的,可是效率不同、佔用的資源也就不同。優化器就是在選擇執行的方案。它優化的是索引應該用哪一個?多表聯查應該先查哪一個表?怎麼鏈接等等。
分析器知道了作啥、優化器知道了應該怎麼作。接下來就交給執行器去執行了。
開始執行,判斷是否有相應的權限。好比該帳戶對 user 表沒權限就返回無權限的錯誤,以下所示:
select * from user where id = 1; ERROR 1142 (42000): SELECT command denied to user 'nasus'@'localhost' for table 'user'
PS:若是命中緩存沒走到執行器這裏,那麼在返回查詢結果時作權限驗證。
回到正題,若是有權限,繼續打開表執行。執行器會根據表定義的引擎去使用對應接口。好比咱們上面的 sql 語句執行流程是這樣的:
至此,整個 SQL 的執行流程完畢,小胖懂了嗎?
巨人的肩膀
本文經過一條簡單的 SQL 查詢語句,引出 MySQL 的結構以及這條 sql 查詢語句的執行流程。相信你看完會對 SQL 有更深的理解。
若是看到這裏,喜歡這篇文章的話,請幫點個好看。微信搜索一個優秀的廢人,關注後回覆電子書送你 1000+ 本編程電子書 ,包括 C、C++、Java、Python、GO、Linux、Git、數據庫、設計模式、前端、人工智能、面試相關、數據結構與算法以及計算機基礎,詳情看下圖。回覆 1024 送你一套完整的 java 視頻教程。