前言
日常開發(fā)中,我們經常需要寫查詢SQL。但是,大家知道一條查詢SQL在mysql內部是如何執(zhí)行的嘛?比如這條簡單的SQL:
select*fromtest_db.user_info_tabwhereuser_id=123;
我們知道在mySQL客戶端,輸入一條查詢SQL,然后看到返回查詢的結果。這條查詢語句在 MySQL 內部到底是如何執(zhí)行的呢?本文跟大家探討一下哈,我們先來看下MySQL基本架構~
MySQL 基本架構
總體來說,MySQL大體分為兩部分,分別是Server 層和存儲引擎層。
Server 層
它包括連接器、查詢緩存、分析器、優(yōu)化器、執(zhí)行器等。比如存儲過程,觸發(fā)器,視圖都是在這一層實現的。
連接器(Connection Manager):負責處理客戶端與服務器之間的連接。它接受來自客戶端的請求,并進行身份驗證和權限檢查,建立和管理連接。
查詢緩存(Query Cache):在舊版 MySQL 中有,但在較新的版本中已不推薦使用。它能夠緩存查詢和對應的結果,以提高查詢性能。然而,在高并發(fā)和大型數據庫中,它反而可能成為性能瓶頸,因為它在某些情況下會引起鎖和不必要的開銷。
分析器(Parser):負責分析 SQL 查詢語句,驗證其語法和語義,確保查詢的正確性。它將 SQL 語句轉換成內部數據結構供優(yōu)化器和執(zhí)行器使用。
優(yōu)化器(Optimizer):接收來自分析器的查詢請求,并決定如何最有效地執(zhí)行查詢。優(yōu)化器的目標是找到最佳的執(zhí)行路徑,選擇合適的索引、連接順序和訪問方法,以提高查詢性能。
執(zhí)行器(Executor):負責執(zhí)行優(yōu)化器生成的執(zhí)行計劃,獲取存儲引擎返回的數據,并處理客戶端請求。它與存儲引擎交互,執(zhí)行查詢并返回結果給用戶。
存儲引擎層: 它負責數據的存儲和提取。Mysql支持InnoDB、MyISAM、Memory 等多個存儲引擎。我們日常開發(fā)中,一般用的存儲引擎就是InnoDB。從 MySQL 5.5 版本開始,InnoDB 就成為了默認的存儲引擎。
介紹完MySQL基本架構,帶大家看一下,每個組件,一條查詢SQL主要做什么事~~
連接器
我們要執(zhí)行查詢SQL,一般在MySQL客戶端, 需要輸入連接命令,連接到MySQL服務端。在MySQL服務端,就是連接器負責跟你的客戶端建立連接、獲取權限、維持和管理連接。
連接命令如下:
mysql-h(ip地址)-P(端口)-u(用戶名)-p
輸入完連接命令之后,我們接著輸入正確的密碼,經過經典的TCP握手之后,就可以成功連接到MySQL服務器啦,如下:
C:MySQLMySQLServer8.0in>mysql-h127.0.0.1-P3306-uroot-p Enterpassword:****** WelcometotheMySQLmonitor.Commandsendwith;org. YourMySQLconnectionidis50 Serverversion:8.0.31MySQLCommunityServer-GPL Copyright(c)2000,2022,Oracleand/oritsaffiliates. OracleisaregisteredtrademarkofOracleCorporationand/orits affiliates.Othernamesmaybetrademarksoftheirrespective owners. Type'help;'or'h'forhelp.Type'c'toclearthecurrentinputstatement. mysql>
如果輸入密碼錯誤,則會收到一個 Access denied的錯誤信息,如下:
C:ProgramFilesMySQLMySQLServer8.0in>mysql-h127.0.0.1-P3306-uroot-p Enterpassword:***** ERROR1045(28000):Accessdeniedforuser'root'@'localhost'(usingpassword:YES)
連接成功之后,大家就可以直接輸入查詢SQL,就可以看到結果啦。
mysql>select*fromtest_db.user_info_tabwhereuser_id=123; +---------+----------------+------+------+--------+---------+--------------------------+ |id|user_name|age|city|status|user_id|password| +---------+----------------+------+------+--------+---------+--------------------------+ |1570091|撿田螺的小男孩|28|深圳|活躍|123|523da7ne+yndc5nb1zWWlA==| +---------+----------------+------+------+--------+---------+--------------------------+ 1rowinset(0.01sec)
大家注意一下哈,如果連接成功后,沒有后續(xù)的輸入查詢SQL等其他操作。這時候,這個連接是空閑的哈,可以用show processlist查看。
查詢緩存
在老版本的MySQL中,連接成功后,我們執(zhí)行查詢SQL,會先執(zhí)行查詢緩存。
也就是說MySQL接受到一個查詢SQL請求時,會先去查詢緩存看看,如果緩存有這條SQL的查詢結果,會直接返回。如果查詢緩存沒有,就繼續(xù)往下執(zhí)行,執(zhí)行完之后,把結果寫入緩存。其中,這個查詢緩存是key-value的結果,你可以把它理解為一個map吧,其中key就是這個查詢SQL,value則是這個查詢的結果。
同時,如果你查詢的表進行更新的時候,會清空緩存的。一個表更新比較頻繁的話,使用查詢緩存命中率會很低,你剛查完放到緩存,更新SQL又清空了,就很不劃算。有些時候,一些靜態(tài)配置表,很少更新的,才建議使用查詢緩存。其他更新頻繁的表,則不建議使用查詢緩存,你可以通過這個參數query_cache_type 設值是否走查詢緩存。
其實,MySQL 比較新的版本,如8.0 已經廢棄了查詢緩存,并且相應的參數 query_cache_type 也不再存在。因為在高并發(fā)和大型數據庫環(huán)境下,查詢緩存可能導致性能問題,并且在實際測試中發(fā)現,禁用查詢緩存可能會提高整體性能和可伸縮性。
分析器
如果查詢SQL沒有命中查詢緩存的話,繼續(xù)往下執(zhí)行,就到分析器上場了。它負責分析 SQL 查詢語句,驗證其語法和語義,確保查詢的正確性。
你扔個SQL給MySQL服務器,它肯定需要先解析,才知道這個SQL是做什么的,對吧。它會派出分析器,先做詞法分析。你提交過來的查詢SQL是由很多個字符創(chuàng)和空格組成的,MySQL會先解析出這些字符串表示什么意思。
select*fromtest_db.user_info_tabwhereuser_id=123;
它先把關鍵字select解析出來,然后把user_info_tab解析成表,user_id解析成列名。做完詞法分析之后,開始做語法分析。語法分析主要就是判斷,你的SQL是否滿足MYSQL的語法。
如果你的SQL寫錯了,語法分析就會報錯誤提示: ERROR 1064 (42000): You have an error in your SQL syntax;
mysql>select*romtest_db.user_info_tabwhereuser_id=123; ERROR1064(42000):YouhaveanerrorinyourSQLsyntax;checkthemanualthatcorrespondstoyourMySQLserverversionfortherightsyntaxtousenear'romtest_db.user_info_tabwhereuser_id=123'atline1
平時大家看到這個錯誤的時候,只需要,關注關鍵詞 syntax to use near 就可以快速知道哪里寫錯啦。比如這個例子,就是我的from寫錯了,少了f。
優(yōu)化器
經過分析器之后,MySQL已經知道需要做什么了。但是在經過執(zhí)行器之前,還會先經過優(yōu)化器。優(yōu)化器做的事情就是,怎么去做才是最好的。對于一條查詢SQL來說就是:怎么去查是最佳效率的。
比如這個查詢SQL:
select*fromtest_db.user_info_tabwhereuser_id=123anduser_name='田螺';
其中,在user_info_tab表中,user_id為索引字段,user_name也是索引字段。
這條SQL執(zhí)行的時候,可能使用索引user_id,也可能使用使用user_name。選擇不同的索引,執(zhí)行效率是不一樣的。具體怎么選擇,就是優(yōu)化器所做的事情。
大家是否還記得explain。我們使用它加在我們查詢的SQL,就可以幫助了解優(yōu)化器在執(zhí)行查詢時,選擇的執(zhí)行計劃和相應的優(yōu)化策略。
經過優(yōu)化器之后,就來到了執(zhí)行器階段。也就是真正執(zhí)行查詢SQL了。
執(zhí)行器
select*fromtest_db.user_info_tabwhereuser_id=123;
在要開始執(zhí)行時候,會判斷一下,該用戶是否對這個SQL有查詢的權限,如果沒有,則會報權限錯誤。如果有權限的時候,打開表直接執(zhí)行。執(zhí)行的過程,其實類似于執(zhí)行調用引擎提供的接口。
我們現在假設user_id不是索引字段,我們使用的是InnoDb存儲引擎,這個查詢SQL執(zhí)行過程就是這樣:
調用InnoDb存儲引擎提供的接口,獲取user_info_tab表的第一行。
判斷user_id是不是為123,如果不是,跳過這一行。如果是,把這一行放到結果集。
調用InnoDb存儲引擎提供的接口,獲取user_info_tab表的下一行。
判斷user_id是不是為123,如果不是,跳過這一行。如果是,把這一行放到結果集。
重復3、4步驟,一直掃描完user_info_tab表的所有行。最后把結果集返回客戶端。
聊到這里,其實一條查詢SQL的執(zhí)行過程,已經講完啦,是不是很簡單呀~~
審核編輯:黃飛
-
SQL
+關注
關注
1文章
764瀏覽量
44130 -
Server
+關注
關注
0文章
90瀏覽量
24037 -
MySQL
+關注
關注
1文章
809瀏覽量
26569
原文標題:一條查詢SQL是如何執(zhí)行的
文章出處:【微信號:DBDevs,微信公眾號:數據分析與開發(fā)】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
相關推薦
評論