對於一個作後臺不久的我,起初作項目只是實現了功能,所謂的增刪改查,和基本查詢索引的創建。直到有一個面試官問我一個問題,一條sql查詢語句在mysql數據庫中具體是怎麼執行的?我被虐了,很開心,感謝他。因而開始了深刻學習mysql。本篇文章經過mysql
一條sql查詢語句在mysql數據庫中具體是怎麼執行的?程序員
來具體講解mysql的基礎架構。面試
mysql> select * from Student where ID=1;
複製代碼
上面一條簡單的查詢語句很簡單,但我想好多開發者並不知道在MYSQL內部的執行過程。sql
Mysql基本架構示意圖。 數據庫
從圖中能夠看出Mysql能夠大致分爲Server層和存儲引擎層兩部分。Server層包括鏈接器、查詢緩存、分析器、優化器、執行器等,這些涵蓋了MySQL的大多數核心服務和全部的內置函數(如日期、時間、數學和加密函數等),跨存儲引擎的功能都在這一層實現,好比存儲過程、觸發器、視圖等。緩存
存儲引擎層負責數據的存儲和提取,提供數據的讀寫接口。其架構模式是插件式的,支持InnoDB、MyISAM、Memory等多個存儲引擎。目前開發中最經常使用的存儲引擎是InnoDB,它從MySQL5.5.5版本開始成爲默認存儲引擎,不過開發者也能夠經過指定存儲引擎的類型來選擇別的引擎。服務器
即便存儲引擎不一樣,可是也會共用一個Server層,接下來對Server層中的執行流程,依次對其做用進行講解。微信
運行查詢語句開始查詢的前提是第一步先鏈接數據庫,這時候等待你的就是鏈接器。鏈接器負責和客戶端創建鏈接、獲取權限、維持和管理鏈接。架構
常規的開發模式,客戶端與服務器須要創建鏈接。兩者在完成經典的TCP握手後,Server層鏈接器就要開始認證你的身份,這個時候是服務器端代碼使用的用戶名和密碼。函數
鏈接器一些內容說明:
mysql>show processlist
複製代碼
鏈接斷開相關:客戶端若是太長時間沒動靜,鏈接器就會自動將它斷開。這個時間是由參數 wait_timeout 控制的,默認值是 8 小時。 若是在鏈接被斷開以後,客戶端再次發送請求的話,就會收到一個錯誤提醒: Lost connection to MySQL server during query。這時候若是你要繼續,就須要重連,而後再執行請求了。數據庫裏面,長鏈接是指鏈接成功後,若是客戶端持續有請求,則一直使用同一個鏈接。短鏈接則是指每次執行完不多的幾回查詢就斷開鏈接,下次查詢再從新創建一個。
創建鏈接的過程一般是比較複雜的,因此我建議你在使用中要儘可能減小創建鏈接的動做,也就是儘可能使用長鏈接。
較好的鏈接方式長鏈接產生的問題以及解決辦法:
所有使用長鏈接後,你可能會發現,有些時候 MySQL 佔用內存漲得特別快,這是由於 MySQL 在執行過程當中臨時使用的內存是管理在鏈接對象裏面的。這些資源會在鏈接斷開的時候才釋放。因此若是長鏈接累積下來,可能致使內存佔用太大,被系統強行殺掉(OOM),從現象看就是 MySQL 異常重啓了。怎麼解決這個問題呢?你能夠考慮如下兩種方案。
按期斷開長鏈接。使用一段時間,或者程序裏面判斷執行過一個佔用內存的大查詢後,斷開鏈接,以後要查詢再重連。
若是你用的是 MySQL 5.7 或更新版本,能夠在每次執行一個比較大的操做後,經過執行 mysql_reset_connection 來從新初始化鏈接資源。這個過程不須要重連和從新作權限驗證,可是會將鏈接恢復到剛剛建立完時的狀
第一步鏈接創建完成後,就能夠執行查詢語句了。第二部:查詢緩存。 Mysql肯定了查詢語句,會先到查詢緩存中,看以前是否執行過這條查詢語句。以前若是執行過這條查詢語句,查詢結果可能會以key-value的方式直接緩存在內存中。key是查詢的語句,value是查詢到的值,這樣的話查詢緩存會直接把value值返回給客戶端。查詢語句若是步子查詢緩存中,會正常往下執行,獲取到新的查詢結果後會被存入到查詢緩存中。
說明:
大多數狀況下並不建議使用查詢緩存。查詢緩存每每弊大於利。
查詢緩存的失效很是頻繁,只要有對某個表的更新,該表的全部查詢緩存都會被清空。因此極可能你費勁把結果存起來,尚未使用,就被一個更新所有清空了,尤爲是對於更新壓力大的數據庫來講,查詢緩存的命中率很低。可是也不是不能使用,假如一張靜態表(系統配置表),很長時間更新一次,這種狀況就比較適合使用查詢緩存。
如何設置Mysql不使用查詢緩存
將Mysql參數query_cache_type設置成DEMAND,這樣默認的SQL語句都不使用查詢緩存
如何對某一條查詢語句指定使用查詢緩存 肯定使用查詢緩存的語句,能夠用SQL_CACHE顯示指定
mysql> select SQL_CACHE * from Student where ID=1; 複製代碼複製代碼
注意:
Mysql 8.0版本直接將查詢緩存對整塊功能刪除掉了,8.0以後將再也不出現查詢緩存。
若是在查詢緩存中未找到緩存數據,就會開始真正的執行查詢語句。Mysql須要直到這條查詢語句要作什麼?所以須要對SQL語句作解析。
解析流程:
詞法分析
分析器首先會作詞法分析,查詢語句中包括了多個字符串和空格組成,Mysql須要識別出裏面的字符串分別表明什麼。
mysql> select * from Student where ID=1;
複製代碼
分析這條查詢語句,"select"關鍵字能夠識別出是一個查詢語句。 字符串"Student"識別出是表名"Student",把字符串"ID"識別成列"ID"。
語法分析
詞法分析後,語句法分析會根據語法規則,判斷輸入的SQL語句是否知足MySql語法。若是語法不對,會收到「You have an error in your SQL syntax」的錯誤提醒。例如
mysql> elect * from Student where ID=1; 複製代碼ERROR 1064 (42000) You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'elect * from t where ID=1' at line 1 複製代碼
技巧:通常語法錯誤看錯誤提示的時候,要關注的是緊接「use near」的內容
分析器執行以後,到達了優化器。
優化器會作那些優化處理:
當在表中有多個索引的時候,優化器會決定這條查詢語句使用哪一個索引
一個查詢語句有多表關聯(join)的時候,決定各個表的鏈接順序。
例子以下:
mysql> select * from t1 join t2 using(ID) where t1.c=10 and t2.d=20;
複製代碼
兩種關聯查詢方案結果確定是同樣的,可是執行效率會有不一樣,優化器就是決定選擇使用哪個方案。
MySQL 經過分析器知道了你要作什麼,經過優化器知道了該怎麼作(執行方案是什麼?),因而就進入了執行器階段,開始執行語句。
開始執行的時候,要先判斷一下你對這個表 Student 有沒有執行查詢的權限,若是沒有,就會返回沒有權限的錯誤,以下所示 (在工程實現上,若是命中查詢緩存,會在查詢緩存返回結果的時候,作權限驗證。查詢也會在優化器以前調用 precheck 驗證權限)。
mysql> select * from Student where ID=10; 複製代碼ERROR 1142 (42000): SELECT command denied to user 'b'@'localhost' for table 'Student' 複製代碼
若是有權限,就打開表繼續執行。打開表的時候,執行器就會根據表的引擎定義,去使用這個引擎提供的接口。
來到存儲引擎,執行存儲引擎提供的數據讀寫接口。 這條查詢語句,執行器(注意這裏讀寫數據的仍是存儲引擎)讀寫數據的流程要分兩種狀況考慮:
表 Student 中,ID字段沒有索引,執行流程以下:
調用 InnoDB 引擎接口取這個表的第一行,判斷 ID 值是否是 1,若是不是則跳過,若是是則將這行存在結果集中;
調用引擎接口取「下一行」,重複相同的判斷邏輯,直到取到這個表的最後一行。
執行器將上述遍歷過程當中全部知足條件的行組成的記錄集做爲結果集返回給客戶端。
至此,這個語句就執行完成了。
表 Student 中,ID字段有索引,那麼執行器的執行流程是這樣的:
有索引的表,執行的邏輯也差很少。第一次調用的是「取知足條件的第一行」這個接口,以後循環取「知足條件的下一行」這個接口,這些接口都是引擎中已經定義好的。
到此,一條查詢語句在mysql架構中執行基本流程進行了一個大概的講解。在這個流程中,會有不少細節和可深挖學習的地方,例如關聯(join)、索引、日誌系統等,接下來會繼續學習並記錄一些MySql深刻的東西。
力推文章:
如何寫優雅的SQL原生語句? 兩篇文章一塊兒學習能完全搞懂sql語句到底怎麼在架構中執行的,到底應該怎麼寫優秀的sql。
以爲本文對你有幫助?請分享給更多人
歡迎你們關注個人公衆號——程序員成長指北。請自行微信搜索——「程序員成長指北」