SQL執行流程圖如下

本文改編自《高性能Mysql》,煙哥用小說的形式來講這個内容。
我是一條sql,就是一條長長的字元串,不要問我長什麼樣,因為我比較傲嬌。
額~~不是我不說啊,因為細說起來,我可以細分為DML(Update、Insert、Delete),DDL(表結構修改),DCL(權限操作),DQL(Select)操作,一個個去介紹,我怕大家嫌我煩!
嗯,大家沒什麼意見,我繼續往下自我介紹了~
由于種類太多,這裡我隻是一條查詢SQL,也就是一句DQL。
用戶端按照Mysql通信協定,把我發送到服務端。
當我到達服務端後,我會在一個單獨的線程裡進行執行。服務端要先...
萬萬沒想到,我又被打斷了~好吧,因為我在一個線程裡執行,總要有辦法能看到線程的執行狀态吧。Mysql提供了下面的指令,給大家檢視
出來的結果是長下面這樣的
圖裡<code>Command</code>這一列,反應的就是這個線程目前的執行狀态啦。我在這個線程的執行過程中,狀态是會變化很多次。
你看圖裡,有一個<code>Sleep</code>,這是在告訴你線程正在等待用戶端發送新的請求。還有一個為<code>Query</code>,這代表線程正在執行查詢或者正在将結果發送給用戶端。
至于其他的,還有<code>Locked</code>、<code>Sending data</code>等等,分别代表...
額,好吧,唠唠叨叨了一大堆,大家居然木有嫌我煩,嗯,至于其他狀态的含義大家可以去Mysql官網查詢哦。
嗯,回到剛才的話題。我到達服務端後,Mysql要判斷我的前6個字元是否為<code>select</code>!
并且,語句中不帶有<code>SQL_NO_CACHE</code>關鍵字,如果符合條件,就進入查詢緩存。
說到查詢緩存,它其實是一個哈希表,它将執行過的語句及其結果會以 key-value 對的形式,被直接緩存在記憶體中。
它的key是一個哈希值,是通過查詢SQL(也就是我)、目前要查詢的資料庫、用戶端協定版本等,生成的一個哈希值,而它的value自然就是查詢結果啦。
當然,如果我要繞過查詢緩存,也很簡單。我可以像下面這麼寫:
也可以将參數<code>query_cache_type</code>設定成<code>DEMAND</code>來繞過查詢緩存。
可是,有一天查詢緩存悲傷的對我說:"你将來再也看不到我了,我已經被曆史淘汰了,Mysql8.0版本開始就沒有我了!"
聽到這個消息後,我表面上故作堅強的對查詢緩存說:"不要方,大家會想你的!"
然而,實際上心裡想的是:"嘿嘿嘿,你個坑爹的,終于不存在了!"大家不要覺得我太邪惡,畢竟查詢緩存實在是太不好用了。接下來我們來說說解析器...
萬萬沒想到,本來想糊弄過去的。結果...好吧,回到正題,因為
隻要有對一個表的更新,這個表上所有的查詢緩存都會被清空
SQL任何字元上的不同,如空格,注釋,都會導緻緩存不命中
是以,我能想到用查詢緩存的表,隻有一種情況,那就是配置表。其他的業務表,根本是無法利用查詢緩存的特性,或許Mysql團隊也是覺得查詢緩存的使用場景過于局限,就無情的将它剔除。
(本文将解析器和預處理器統一稱為分析器)
話說,我離開查詢緩存後,進入解析器。
解析器:"來來來,我先對你進行詞法分析,告訴我你長啥樣?"
我是下面這樣的
解析器:"好,好,好。我有兩個階段,我先對你進行詞法分析,我将你從左到右一個字元、一個字元地輸入,然後根據構詞規則識别單詞。你将會生成4個Token,如下所示。"
關鍵字
非關鍵字
select
username
from
userinfo
解析器:"接下來呢,進行文法解析,判斷你輸入的這個 SQL 語句是否滿足 MySQL 文法。然後生成下面這樣一顆文法樹。"
我:"如果文法不對呢?"
解析器:"那你會收到一個提示如下!"
解析器:"順利生成文法樹以後,我就将你送往預處理器!"
預處理器:"老弟,你來拉!"
我:"嗯!"
預處理器:"老弟,我來幫你看看你的列名對不對,資料庫的這張表裡是不是真的有這個列。再看看表名對不對,如果不對,你會看到下面的錯誤!"
預處理器:"最後我再給你送去做權限驗證,如果你沒有操作這個表的權限,會報下面這個錯誤!"
(這個地方,大家可能有疑問,因為有些文章說是執行器做的權限驗證,可以直接拉到本文底部看說明)
最後,這顆文法樹會傳遞給優化器。
在告别了解析器後,我進入了優化器。
優化器大哥:"告訴我,你長什麼樣啊?"
我說道:"大哥不要捉急,我是長這樣的~"(這裡優化的其實應該是文法樹,我隻是為了便于說明,才用SQL當例子,實際上是針對文法樹進行優化)
優化器大哥:"我的任務就是幫你判斷一下怎麼樣執行更快,比如先查<code>Table1</code>再查<code>Table2</code>,還是先查<code>Table2</code>再查<code>Table1</code>呢?判斷完如何執行以後,生成執行計劃就好啦!"
我很不信任的說道:“哼,你就不會判斷失誤麼!”
優化器大哥:"那就要對SQL進行改寫啦,比如如果你帶了<code>STRAIGHT_JOIN</code>關鍵字,長下面這樣"
"那我就知道強制先找<code>Table1</code>再關聯找<code>Table2</code>啦,類似的例子還有很多,我就不一一列舉了!"
(<code>STRAIGHT_JOIN</code>功能同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這個版本的源碼,其在處理查詢這段的核心代碼如下
在<code>sql_parse.cc</code>檔案中,有這麼一段代碼如下
其中<code>select_precheck</code>是進行權限校驗。而優化器和執行器是在<code>execute_sqlcom_select</code>這個方法中。
當然,大家有新的見解,歡迎留言。
作者:孤獨煙
出處: http://rjzheng.cnblogs.com/
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。如果覺得還有幫助的話,可以點一下右下角的【推薦】。