面試官:你來講講一條查詢語句的具體執(zhí)行過程
對于一個開發(fā)工程師來說,了解一下 MySQL 是如何執(zhí)行一條查詢語句的,不是一件壞事,阿粉帶你來瞅瞅它是怎么執(zhí)行的
一條看似非常簡單的查詢語句:
- select * from T where id=1;
然后 MySQL 就返回給了你結(jié)果,但是里面具體是如何執(zhí)行的呢?
別急,聽阿粉慢慢跟你說
首先咱們先來看一張圖,接下來的過程都是基于這張圖來講的:
連接器
當客戶端連接 MySQL 時,會發(fā)出連接請求到連接器,連接器此時就會去驗證這個連接的賬號密碼
- 如果賬號或者密碼不正確,客戶端就會收到一個 Access denied for user 的錯誤,之后此次連接結(jié)束
- 賬號密碼正確,連接器會到權(quán)限表里面查詢出該賬號所擁有的權(quán)限,之后這個連接里面的權(quán)限判斷,都是以此時讀到的權(quán)限為根據(jù)
所以你知道為什么更改了一個賬號的權(quán)限之后,一定要斷開再重連才有效吧~
在實際中肯定有這樣的情況,就是一個連接建立之后,但是我沒有執(zhí)行什么操作,那么就可以說這個連接處于空閑狀態(tài)( sleep )
如果長時間都沒有什么操作的話,連接器就會選擇把它斷開,這個時間是由 wait_timeout 來控制的,默認值是 8 小時
連接都被斷開了,如果此時客戶端再次發(fā)送請求想要進行一些操作的話,那就需要重新建立連接才能往下走
在數(shù)據(jù)庫中有兩種連接:
- 短連接:每次執(zhí)行完很少的幾次查詢就斷開連接,下次想查詢時,就要重新建立一個
- 長連接:如果客戶端持續(xù)有請求,那就一直使用同一個連接
建立連接是比較麻煩的,首先要發(fā)送請求吧,發(fā)送了請求要去驗證賬號密碼吧,驗證完了要去看你所擁有的權(quán)限吧,所以在使用過程中,盡量使用長連接
但是使用長連接又有新的問題:有時候,你會發(fā)現(xiàn) MySQL 占用內(nèi)存,因為是長連接嘛,所以它會在斷開的時候才將資源釋放掉。
這個時候可以考慮下面兩種方案:
- 定期斷開長連接
- 如果使用的是 MySQL 5.7 或者更高版本,可以在每次執(zhí)行一個比較大的操作之后,通過執(zhí)行 mysql_reset_connection 來重新初始化連接資源,這個過程不需要重新連接和權(quán)限驗證
分析器
連接器這一關(guān)是過來了,接下來就是去查詢緩存
首先看緩存里面有沒有,如果有呢,那就沒有必要向下走,直接返回給客戶端結(jié)果就可以了
如果緩存中沒有的話,那就去分析器
但是聰明的你肯定發(fā)現(xiàn)了,我的小標題并不是緩存,而是分析器,為啥呢?
因為查詢緩存的失效非常頻繁,只要有對一個表的更新,那在這個表上的所有查詢緩存都會被清空。所以就會導(dǎo)致 MySQL 費勁吧啦的把緩存給建立起來了,結(jié)果呢還沒怎么用,一個更新操作,給弄沒了
所以 MySQL 8.0 版本直接將查詢緩存的整塊功能都給刪掉了,那么在這里也不細說,免得奇怪的知識增加
分析器首先會進行"詞法分析",詞法分析就是 select * from T where id=1; ,它會將 select 識別出來,哦,這是一個查詢語句,接下來會將 T 也識別出來,哦,你是想要在這個表中做查詢,然后將 where 后面的條件也識別出來,原來我需要去查找這些內(nèi)容
OK ,“詞法分析”之后,接下來是“語法分析”,語法分析主要就是分析輸入的 SQL 語句合不合法
就比如英語里面的語法 “我用 is , 你用 are ”這種,如果不對肯定是不可以的,語法分析之后發(fā)現(xiàn)你的 SQL 語句不符合規(guī)則,就會收到 You hava an error in your SQL syntax 的錯誤提示
優(yōu)化器
經(jīng)過分析器分析之后, MySQL 就知道你要的是什么了
但是就像條條大路通羅馬一樣,看似是一條簡單的 SQL 查詢語句,有可能有好多條執(zhí)行路徑可以走,比如說要查詢的表里面有多個索引,我使用哪兒個索引效率會比較高呀;多表聯(lián)查的時候,我先關(guān)聯(lián)哪兒個表效率會比較高呀
像這種就是優(yōu)化器需要做的事情
執(zhí)行器
優(yōu)化器做完優(yōu)化之后,就到了執(zhí)行器
執(zhí)行器就是要去執(zhí)行語句了嘛,那我肯定要看看對于要查詢的表 T 有沒有查詢權(quán)限,如果沒有直接就拒絕,這沒啥說的 如果有的話,那就會這樣(在這里以 InnoDB 為例):
- 調(diào)用 InnoDB 引擎接口取這個表的第一行,判斷 ID 的值是不是 10 ,如果不是就跳過,如果是那就放在結(jié)果集中;
- 調(diào)用引擎接口取“下一行”;重復(fù)相同的判斷邏輯,直到這個表的最后一行
- 執(zhí)行器將上述遍歷過程中所有滿足條件的行,組成記錄集返回給客戶端
至此,語句執(zhí)行結(jié)束
存儲引擎
存儲引擎,一聽名字大概就能知道,它負責的是對數(shù)據(jù)的存儲和提取
關(guān)于存儲引擎這塊,最熟悉的應(yīng)該就是 InnoDB 了,畢竟從 MySQL 5.5.5 版本開始它就成為了默認的存儲引擎
舉個栗子
OK ,到這里你可能就比較了解了,但是阿粉這個暖男,為了讓你更加的理解,還準備了個栗子,咱們來瞅瞅:
- select * from T where `name` = '阿粉' and age = '18' ;
還記得嗎?MySQL 首先會怎么做?
- 通過連接器查詢當前執(zhí)行者的角色是否有權(quán)限,進行查詢。如果有的話,就繼續(xù)往下走,如果沒有的話,抱歉,哪怕是你貌美如花,也要拒絕掉,同時甩你一個 Access denied for user 的錯誤信息
- 接下來就是分析器來分析語句了,嗯,你這個語句寫的沒啥問題,繼續(xù)向下執(zhí)行吧
- 此時來到了優(yōu)化器,優(yōu)化器就想,這條執(zhí)行語句,有兩種執(zhí)行方案?。?
- 先查詢表 T 中姓名為 阿粉 的,然后再判斷他年齡是不是 18 歲
- 先查詢表 T 中年齡為 18 歲的所有人,然后再從里面找到姓名為 阿粉 的
- 等優(yōu)化器決定選擇哪個方案之后,執(zhí)行器就去執(zhí)行了。然后返回給客戶端結(jié)果