天天看點

【原創】小說:我是一條DQL

SQL執行流程圖如下

【原創】小說:我是一條DQL

本文改編自《高性能Mysql》,煙哥用小說的形式來講這個内容。

我是一條sql,就是一條長長的字元串,不要問我長什麼樣,因為我比較傲嬌。

【原創】小說:我是一條DQL

額~~不是我不說啊,因為細說起來,我可以細分為DML(Update、Insert、Delete),DDL(表結構修改),DCL(權限操作),DQL(Select)操作,一個個去介紹,我怕大家嫌我煩!

嗯,大家沒什麼意見,我繼續往下自我介紹了~

由于種類太多,這裡我隻是一條查詢SQL,也就是一句DQL。

用戶端按照Mysql通信協定,把我發送到服務端。

當我到達服務端後,我會在一個單獨的線程裡進行執行。服務端要先...

【原創】小說:我是一條DQL

萬萬沒想到,我又被打斷了~好吧,因為我在一個線程裡執行,總要有辦法能看到線程的執行狀态吧。Mysql提供了下面的指令,給大家檢視

出來的結果是長下面這樣的

【原創】小說:我是一條DQL

圖裡<code>Command</code>這一列,反應的就是這個線程目前的執行狀态啦。我在這個線程的執行過程中,狀态是會變化很多次。

你看圖裡,有一個<code>Sleep</code>,這是在告訴你線程正在等待用戶端發送新的請求。還有一個為<code>Query</code>,這代表線程正在執行查詢或者正在将結果發送給用戶端。

至于其他的,還有<code>Locked</code>、<code>Sending data</code>等等,分别代表...

【原創】小說:我是一條DQL

額,好吧,唠唠叨叨了一大堆,大家居然木有嫌我煩,嗯,至于其他狀态的含義大家可以去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版本開始就沒有我了!"

聽到這個消息後,我表面上故作堅強的對查詢緩存說:"不要方,大家會想你的!"

然而,實際上心裡想的是:"嘿嘿嘿,你個坑爹的,終于不存在了!"大家不要覺得我太邪惡,畢竟查詢緩存實在是太不好用了。接下來我們來說說解析器...

【原創】小說:我是一條DQL

萬萬沒想到,本來想糊弄過去的。結果...好吧,回到正題,因為

隻要有對一個表的更新,這個表上所有的查詢緩存都會被清空

SQL任何字元上的不同,如空格,注釋,都會導緻緩存不命中

是以,我能想到用查詢緩存的表,隻有一種情況,那就是配置表。其他的業務表,根本是無法利用查詢緩存的特性,或許Mysql團隊也是覺得查詢緩存的使用場景過于局限,就無情的将它剔除。

(本文将解析器和預處理器統一稱為分析器)

話說,我離開查詢緩存後,進入解析器。

解析器:"來來來,我先對你進行詞法分析,告訴我你長啥樣?"

我是下面這樣的

解析器:"好,好,好。我有兩個階段,我先對你進行詞法分析,我将你從左到右一個字元、一個字元地輸入,然後根據構詞規則識别單詞。你将會生成4個Token,如下所示。"

關鍵字

非關鍵字

select

username

from

userinfo

解析器:"接下來呢,進行文法解析,判斷你輸入的這個 SQL 語句是否滿足 MySQL 文法。然後生成下面這樣一顆文法樹。"

【原創】小說:我是一條DQL

我:"如果文法不對呢?"

解析器:"那你會收到一個提示如下!"

解析器:"順利生成文法樹以後,我就将你送往預處理器!"

預處理器:"老弟,你來拉!"

我:"嗯!"

預處理器:"老弟,我來幫你看看你的列名對不對,資料庫的這張表裡是不是真的有這個列。再看看表名對不對,如果不對,你會看到下面的錯誤!"

預處理器:"最後我再給你送去做權限驗證,如果你沒有操作這個表的權限,會報下面這個錯誤!"

(這個地方,大家可能有疑問,因為有些文章說是執行器做的權限驗證,可以直接拉到本文底部看說明)

最後,這顆文法樹會傳遞給優化器。

在告别了解析器後,我進入了優化器。

優化器大哥:"告訴我,你長什麼樣啊?"

我說道:"大哥不要捉急,我是長這樣的~"(這裡優化的其實應該是文法樹,我隻是為了便于說明,才用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頁有一句話如下圖所示

【原創】小說:我是一條DQL

該書也指明權限驗證是在預處理器中執行。本文中将預處理和解析器統一劃分為分析器的範疇。

論點三:同源碼不符

我翻看了Mysql5.7.25這個版本的源碼,其在處理查詢這段的核心代碼如下

在<code>sql_parse.cc</code>檔案中,有這麼一段代碼如下

其中<code>select_precheck</code>是進行權限校驗。而優化器和執行器是在<code>execute_sqlcom_select</code>這個方法中。

當然,大家有新的見解,歡迎留言。

作者:孤獨煙

出處: http://rjzheng.cnblogs.com/

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。如果覺得還有幫助的話,可以點一下右下角的【推薦】。