面試官:說說一條查詢sql的執行流程和底層原理?

面试官:说说一条查询sql的执行流程和底层原理?

序章 自我介紹面試

我是一條sql,就是一條長長的字符串,不要問我長什麼樣,由於我比較傲嬌。sql

面試官:說說一條查詢sql的執行流程和底層原理?

 

額~~不是我不說啊,由於細提及來,我能夠細分爲數據庫

DML

(Update、Insert、Delete),緩存

DDL

(表結構修改),架構

DCL

(權限操做),函數

DQL

(Select)操做,一個個去介紹,我怕你們嫌我煩!性能

嗯,你們沒什麼意見,我繼續往下自我介紹了~學習

因爲種類太多,這裏我只是一條查詢SQL,也就是一句DQL。優化

客戶端按照Mysql通訊協議,把我發送到服務端。插件

當我到達服務端後,我會在一個單獨的

線程

裏進行執行。服務端要先…

面試官:說說一條查詢sql的執行流程和底層原理?

 

萬萬沒想到,我又被打斷了~好吧,由於我在一個線程裏執行,總要有辦法能看到線程的執行狀態吧。Mysql提供了下面的命令,給你們查看

SHOW [FULL] PROCESSLIST

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

 

面試官:說說一條查詢sql的執行流程和底層原理?

 

圖裏

Command

這一列,反應的就是這個線程當前的執行狀態啦。我在這個線程的執行過程當中,狀態是會變化不少次。

你看圖裏,有一個

Sleep

,這是在告訴你線程正在等待客戶端發送新的請求。還有一個爲

Query

,這表明線程正在執行查詢或者正在將結果發送給客戶端。

至於其餘的,還有

Locked
Sending data

等等,分別表明…

面試官:說說一條查詢sql的執行流程和底層原理?

 

額,好吧,嘮嘮叨叨了一大堆,你們竟然木有嫌我煩,嗯,至於其餘狀態的含義你們能夠去Mysql官網查詢哦。

嗯,回到剛纔的話題。我到達服務端後,Mysql要判斷個人前6個字符是否爲

select

。而且,語句中不帶有

SQL_NO_CACHE

關鍵字,若是符合條件,就進入查詢緩存。

第一章 我和查詢緩存的那些事

說到查詢緩存,它實際上是一個哈希表,它將執行過的語句及其結果會以 key-value 對的形式,被直接緩存在內存中。

它的key是一個哈希值,是經過查詢SQL(也就是我)、當前要查詢的數據庫、客戶端協議版本等,生成的一個哈希值,而它的value天然就是查詢結果啦。

固然,若是我要繞過查詢緩存,也很簡單。我能夠像下面這麼寫:

Select SQL_NO_CACHE * from table

也能夠將參數query_cache_type設置成DEMAND來繞過查詢緩存。

但是,有一天查詢緩存悲傷的對我說:"你未來再也看不到我了,我已經被歷史淘汰了,Mysql8.0版本開始就沒有我了!"

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

然而,實際上內心想的是:"嘿嘿嘿,你個坑爹的,終於不存在了!"你們不要以爲我太邪惡,畢竟查詢緩存實在是太很差用了。接下來咱們來講說解析器…

面試官:說說一條查詢sql的執行流程和底層原理?

 

萬萬沒想到,原本想糊弄過去的。結果…好吧,回到正題,由於

  • 只要有對一個表的更新,這個表上全部的查詢緩存都會被清空
  • SQL任何字符上的不一樣,如空格,註釋,都會致使緩存不命中

所以,我能想到用查詢緩存的表,只有一種狀況,那就是配置表。其餘的業務表,根本是沒法利用查詢緩存的特性,或許Mysql團隊也是以爲查詢緩存的使用場景過於侷限,就無情的將它剔除。

第二章 我和分析器的愛恨情仇

(本文將解析器和預處理器統一稱爲分析器)

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

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

我是下面這樣的

select username from userinfo

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

面試官:說說一條查詢sql的執行流程和底層原理?

 

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

面試官:說說一條查詢sql的執行流程和底層原理?

 

我:"若是語法不對呢?"

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

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頁有一句話以下圖所示

面試官:說說一條查詢sql的執行流程和底層原理?

 

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

論點三:同源碼不符

我翻看了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+】,本羣專用於學習交流技術、分享面試機會,拒絕廣告,我也會在羣內不按期答題、探討。

相關文章
相關標籤/搜索