序章 自我介紹面試
我是一條sql,就是一條長長的字符串,不要問我長什麼樣,由於我比較傲嬌。sql
額~~不是我不說啊,由於細提及來,我能夠細分爲數據庫
DML
(Update、Insert、Delete),緩存
DDL
(表結構修改),架構
DCL
(權限操做),函數
DQL
(Select)操做,一個個去介紹,我怕你們嫌我煩!性能
嗯,你們沒什麼意見,我繼續往下自我介紹了~學習
因爲種類太多,這裏我只是一條查詢SQL,也就是一句DQL。優化
客戶端按照Mysql通訊協議,把我發送到服務端。插件
當我到達服務端後,我會在一個單獨的
線程
裏進行執行。服務端要先…
萬萬沒想到,我又被打斷了~好吧,由於我在一個線程裏執行,總要有辦法能看到線程的執行狀態吧。Mysql提供了下面的命令,給你們查看
SHOW [FULL] PROCESSLIST
出來的結果是長下面這樣的
圖裏
Command
這一列,反應的就是這個線程當前的執行狀態啦。我在這個線程的執行過程當中,狀態是會變化不少次。
你看圖裏,有一個
Sleep
,這是在告訴你線程正在等待客戶端發送新的請求。還有一個爲
Query
,這表明線程正在執行查詢或者正在將結果發送給客戶端。
至於其餘的,還有
Locked Sending data
等等,分別表明…
額,好吧,嘮嘮叨叨了一大堆,你們竟然木有嫌我煩,嗯,至於其餘狀態的含義你們能夠去Mysql官網查詢哦。
嗯,回到剛纔的話題。我到達服務端後,Mysql要判斷個人前6個字符是否爲
select
。而且,語句中不帶有
SQL_NO_CACHE
關鍵字,若是符合條件,就進入查詢緩存。
第一章 我和查詢緩存的那些事
說到查詢緩存,它實際上是一個哈希表,它將執行過的語句及其結果會以 key-value 對的形式,被直接緩存在內存中。
它的key是一個哈希值,是經過查詢SQL(也就是我)、當前要查詢的數據庫、客戶端協議版本等,生成的一個哈希值,而它的value天然就是查詢結果啦。
固然,若是我要繞過查詢緩存,也很簡單。我能夠像下面這麼寫:
Select SQL_NO_CACHE * from table
也能夠將參數query_cache_type設置成DEMAND來繞過查詢緩存。
但是,有一天查詢緩存悲傷的對我說:"你未來再也看不到我了,我已經被歷史淘汰了,Mysql8.0版本開始就沒有我了!"
聽到這個消息後,我表面上故做堅強的對查詢緩存說:"不要方,你們會想你的!"
然而,實際上內心想的是:"嘿嘿嘿,你個坑爹的,終於不存在了!"你們不要以爲我太邪惡,畢竟查詢緩存實在是太很差用了。接下來咱們來講說解析器…
萬萬沒想到,原本想糊弄過去的。結果…好吧,回到正題,由於
所以,我能想到用查詢緩存的表,只有一種狀況,那就是配置表。其餘的業務表,根本是沒法利用查詢緩存的特性,或許Mysql團隊也是以爲查詢緩存的使用場景過於侷限,就無情的將它剔除。
第二章 我和分析器的愛恨情仇
(本文將解析器和預處理器統一稱爲分析器)
話說,我離開查詢緩存後,進入解析器。
解析器:"來來來,我先對你進行詞法分析,告訴我你長啥樣?"
我是下面這樣的
select username from userinfo
解析器:"好,好,好。我有兩個階段,我先對你進行詞法分析,我將你從左到右一個字符、一個字符地輸入,而後根據構詞規則識別單詞。你將會生成4個Token,以下所示。"
解析器:"接下來呢,進行語法解析,判斷你輸入的這個 SQL 語句是否知足 MySQL 語法。而後生成下面這樣一顆語法樹。"
我:"若是語法不對呢?"
解析器:"那你會收到一個提示以下!"
You have an error in your SQL syntax
解析器:"順利生成語法樹之後,我就將你送往預處理器!"
預處理器:"老弟,你來拉!"
我:"嗯!"
預處理器:"老弟,我來幫你看看你的列名對不對,數據庫的這張表裏是否是真的有這個列。再看看錶名對不對,若是不對,你會看到下面的錯誤!"
Unknown column xxx in ‘where clause’
預處理器:"最後我再給你送去作權限驗證,若是你沒有操做這個表的權限,會報下面這個錯誤!"
ERROR 1142 (42000): SELECT command denied to user 'root'@'localhost' for table 'xxx'
(這個地方,你們可能有疑問,由於有些文章說是執行器作的權限驗證,能夠直接拉到本文底部看說明)
最後,這顆語法樹會傳遞給優化器。
第三章 我和優化器的動人過往
在告別瞭解析器後,我進入了優化器。
優化器大哥:"告訴我,你長什麼樣啊?"
我說道:"大哥不要捉急,我是長這樣的~"(這裏優化的其實應該是語法樹,我只是爲了便於說明,才用SQL當例子,其實是針對語法樹進行優化)
select t1.* from Table1 t1 inner join Table2 t2 on t1.CommonID = t2.CommonID
優化器大哥:"個人任務就是幫你判斷一下怎麼樣執行更快,好比先查Table1再查Table2,仍是先查Table2再查Table1呢?判斷完如何執行之後,生成執行計劃就好啦!"
我很不信任的說道:「哼,你就不會判斷失誤麼!」
優化器大哥:"那就要對SQL進行改寫啦,好比你帶了STRAIGHT_JOIN關鍵字,長下面這樣"
select t1.* from Table1 t1 STRAIGHT_JOIN Table2 t2 on t1.CommonID = t2.CommonID
"那我就知道強制先找Table1再關聯找Table2啦,相似的例子還有不少,我就不一一列舉了!"
(STRAIGHT_JOIN功能同join相似,但能讓左邊的表來驅動右邊的表,能改表優化器對於聯表查詢的執行順序。)
我說道:"哇塞,如何編寫一個高效的SQL,真是一門學問啊!"
因而,優化器大哥將我變身爲一個執行計劃,而後交給執行器啦~
第四章 我和執行器的悲情經歷
我:"執行器大哥,你是用來作什麼的?"
執行器:"就是根據執行計劃來進行執行查詢啦。我就根據你的指令,逐條調用底層存儲引擎,逐步執行。"
MySQL定義了一系列抽象存儲引擎API,以支持插件式存儲引擎架構。Mysql實現了一個抽象接口層,叫作 handler(sql/handler.h),其中定義了接口函數,好比:ha_open, ha_index_end, ha_create等等,存儲引擎須要實現這些接口才能被系統使用。
末章 一些感慨
最後一個階段,Mysql會將查詢結果返回客戶端。
惟一須要說明的是,若是是SELECT類型的SQL,Mysql會將查詢結果緩存起來。至於其餘的SQL,就將該表涉及到的查詢緩存清空。
一些疑問
這裏關於權限驗證究竟在哪一個階段執行,你們可能會有一些疑問。
以前有一個大牛的文章說是權限驗證是在執行階段,去執行前驗證權限,你們若是看過他的文章,可能會有疑問。我也不是亂質疑人家,畢竟我只是一個小咖。我在這裏只是發表一下我本身的論點,歡迎你們拍磚。
論點一:權限驗證在執行器中判斷從邏輯上說不通
一條查詢SQL通過查詢緩存、分析器、優化器,執行器。若是到最後一個階段執行器中才發現權限不足、那不是前面一系列流程白作了,Mysql應該不至於這麼傻吧~
論點二:同《高性能Mysql》一書內容不符
該書209頁有一句話以下圖所示
該書也指明權限驗證是在預處理器中執行。本文中將預處理和解析器統一劃分爲分析器的範疇。
論點三:同源碼不符
我翻看了Mysql5.7.25這個版本的源碼,其在處理查詢這段的核心代碼以下
在sql_parse.cc文件中,有這麼一段代碼以下
case SQLCOM_SELECT: { //省略 res= select_precheck(thd, lex, all_tables, first_table); if (!res) res= execute_sqlcom_select(thd, all_tables); //省略 }
其中select_precheck是進行權限校驗。而優化器和執行器是在execute_sqlcom_select這個方法中。
固然,你們有新的看法,歡迎留言。
以爲不錯請點贊支持,歡迎留言或進個人我的羣855801563領取【架構資料專題目合集90期】、【BATJTMD大廠JAVA面試真題1000+】,本羣專用於學習交流技術、分享面試機會,拒絕廣告,我也會在羣內不按期答題、探討。