目錄
- 一、MySQL 邏輯架構(gòu)概覽
- 二、連接器(Connector)
- 三、查詢緩存(Query Cache)
- 四、解析器(Parser)
- 五、優(yōu)化器(Optimizer)
- 六、執(zhí)行器
- 七、小結(jié)
一、MySQL 邏輯架構(gòu)概覽
MySQL 最重要、最與眾不同的特性就是它的可插拔存儲(chǔ)引擎架構(gòu)(pluggable storage engine architecture),這種架構(gòu)的設(shè)計(jì)將查詢處理及其他系統(tǒng)任務(wù)和數(shù)據(jù)的存儲(chǔ)/提取分離開來(lái)。來(lái)看官網(wǎng)的解釋:
The MySQL pluggable storage engine architecture enables a database professional to select a specialized storage engine for a particular application need while being completely shielded from the need to manage any specific application coding requirements.
大致意思就是,MySQL 可插拔存儲(chǔ)引擎架構(gòu)使開發(fā)者能夠?yàn)樘囟☉?yīng)用程序需求選擇專門的存儲(chǔ)引擎,同時(shí)完全無(wú)需管理任何特定應(yīng)用程序編碼要求。也就是說(shuō),盡管不同存儲(chǔ)引擎具有不同的功能,但應(yīng)用程序不受這些差異的影響。
如果應(yīng)用程序更改帶來(lái)了需要更改底層存儲(chǔ)引擎的需求,或者需要添加一個(gè)或多個(gè)存儲(chǔ)引擎來(lái)支持新需求,則無(wú)需進(jìn)行重大的編碼或流程更改即可使工作正常進(jìn)行。 MySQL 服務(wù)器架構(gòu)通過(guò)提供適用于跨存儲(chǔ)引擎的一致且易于使用的 API,使應(yīng)用程序免受存儲(chǔ)引擎底層復(fù)雜性的影響。
MySQL 的邏輯架構(gòu)圖如下,參考《高性能 MySQL - 第 3 版》:
我們可以大致把 MySQL 的邏輯架構(gòu)分成 Server 層和存儲(chǔ)引擎層:
1)大多數(shù) MySQL 的核心服務(wù)功能都在 Server 層,包括連接,查詢解析、分析、優(yōu)化、緩存以及所有的內(nèi)置函數(shù)(例如,日期、時(shí)間、數(shù)學(xué)和加密函數(shù)),所有跨存儲(chǔ)引擎的功能都在這一層實(shí)現(xiàn):存儲(chǔ)過(guò)程、觸發(fā)器、視圖等。
值得一提的是,Server 最上面的服務(wù)也就是連接器,擁有管理 MySQL 連接、權(quán)限驗(yàn)證的功能。顯然這并非 MySQL 所獨(dú)有,大多數(shù)基于網(wǎng)絡(luò)的客戶端/服務(wù)器的工具或者服務(wù)都有類似的架構(gòu)。
2)第二層就是存儲(chǔ)引擎(支持 InnoDB、MyISAM、Memory 等多個(gè)存儲(chǔ)引擎)。存儲(chǔ)引擎負(fù)責(zé) MySQL 中數(shù)據(jù)的存儲(chǔ)和提取,響應(yīng)上層服務(wù)器的請(qǐng)求。每個(gè)存儲(chǔ)引擎自然是有它的優(yōu)勢(shì)和劣勢(shì),不同的存儲(chǔ)引擎之間無(wú)法相互通信,所以我們需要根據(jù)不同的場(chǎng)景來(lái)選擇合適的存儲(chǔ)引擎。
服務(wù)器通過(guò) API 與存儲(chǔ)引擎進(jìn)行通信。這些接口屏蔽了不同存儲(chǔ)引擎之間的差異,使得這些差異對(duì)上層的查詢過(guò)程透明。存儲(chǔ)引擎 API 包含幾十個(gè)底層函數(shù),用于執(zhí)行諸如 “開始一個(gè)事務(wù)” 或者 “根據(jù)主鍵提取一行記錄” 等操作。
需要注意的是,在 MySQL 5.1 及之前的版本,MyISAM 是默認(rèn)的存儲(chǔ)引擎,而在 MySQL 5.5.5 后,InnoDB 成為了默認(rèn)的存儲(chǔ)引擎。
二、連接器(Connector)
MySQL 5.7 的官方文檔中,是這樣描述連接器的:
MySQL Connectors provide connectivity to the MySQL server for client programs.
MySQL 連接器為客戶端程序提供到 MySQL 服務(wù)器的連接。 說(shuō)得更細(xì)節(jié)一點(diǎn)的話,連接器其實(shí)會(huì)做兩個(gè)事情,一個(gè)是管理 MySQL 連接,一個(gè)是權(quán)限驗(yàn)證。我們依次來(lái)解釋下。
首先,要連接到 MySQL 服務(wù)器,我們通常需要提供 MySQL 用戶名和密碼,并且如果服務(wù)器運(yùn)行在我們登錄的機(jī)器以外的機(jī)器上,還需要指定一個(gè)主機(jī)名比如 host。 所以連接命令一般是這樣的:
shell> mysql -h host -u user -p
Enter password: ********
當(dāng)然了,如果在運(yùn)行 MySQL 的同一臺(tái)機(jī)器上登錄,就可以省略主機(jī)名,只需使用以下內(nèi)容:
shell> mysql -u user -p
上面這個(gè)命令各位應(yīng)該都很熟悉。
OK,通過(guò)上述命令完成經(jīng)典的 TCP 三次握手建立連接后,連接器就會(huì)根據(jù)你輸入的用戶名和密碼來(lái)認(rèn)證你的身份:
1)如果用戶名或密碼不對(duì),你就會(huì)收到一個(gè) "Access denied for user" 的錯(cuò)誤,然后客戶端程序結(jié)束執(zhí)行。
2)如果用戶名密碼認(rèn)證通過(guò),你會(huì)看到下面這一串內(nèi)容:
mysql>
就是在提示你 MySQL 已準(zhǔn)備好了,你可以開始輸入 SQL 語(yǔ)句了!
當(dāng)然,連接器做的事情不僅僅是比對(duì)一下用戶名和密碼,它還會(huì)驗(yàn)證該用戶是否具有執(zhí)行某個(gè)特定查詢的權(quán)限(例如,是否允許該用戶對(duì) world 數(shù)據(jù)庫(kù)的 Country 表執(zhí)行 SELECT 語(yǔ)句)。之后,這個(gè)連接里面的所有權(quán)限判斷邏輯,都將依賴于此時(shí)讀到的權(quán)限。
這意味著,當(dāng)一個(gè)用戶成功建立連接后,即使你在另一個(gè)終端用管理員賬號(hào)對(duì)這個(gè)用戶的權(quán)限做了修改,對(duì)當(dāng)前已經(jīng)存在連接的權(quán)限不會(huì)造成任何影響。
也就是說(shuō),當(dāng)修改了用戶權(quán)限后,只有再新建的連接才會(huì)使用新的權(quán)限設(shè)置。
當(dāng)一個(gè)連接建立起來(lái)后,如果你沒(méi)有后續(xù)的動(dòng)作,那么這個(gè)連接就處于空閑狀態(tài)(Sleep)。
事實(shí)上,對(duì)于一個(gè) MySQL 連接來(lái)說(shuō)(或者說(shuō)一個(gè)線程),任何時(shí)刻都有一個(gè)狀態(tài),該狀態(tài)表示了 MySQL 當(dāng)前正在做什么。有很多種方式能查看當(dāng)前的狀態(tài),最簡(jiǎn)單的是使用 SHOW FULL PROCESSLIST
命令(該命令返回結(jié)果中的 Command 列就表示當(dāng)前的狀態(tài))。
在一個(gè)查詢的生命周期中,狀態(tài)會(huì)變化很多次。這里就不詳細(xì)列出來(lái)了,上圖中的 Sleep
狀態(tài)就是說(shuō)當(dāng)前連接正在等待客戶端發(fā)送新的請(qǐng)求,Query
狀態(tài)表示當(dāng)前連接正在執(zhí)行查詢或者正在將結(jié)果發(fā)送給客戶端。
在 MyQL 的默認(rèn)設(shè)置中,如果一個(gè)連接處在 Sleep 狀態(tài) 8 小時(shí)(就是超過(guò) 8 小時(shí)沒(méi)有使用),服務(wù)器將斷開這條連接,后續(xù)在該連接上進(jìn)行的所有操作都將失敗。這個(gè)時(shí)間是由參數(shù) wait_timeout
控制的:
三、查詢緩存(Query Cache)
OK,連接建立完成后,我們就可以輸入 select 語(yǔ)句進(jìn)行查詢了。執(zhí)行邏輯就來(lái)到了第二步:查詢緩存。
官方文檔是這樣解釋 Query Cache 的:
The query cache stores the text of a SELECT statement together with the corresponding result that was sent to the client. If an identical statement is received later, the server retrieves the results from the query cache rather than parsing and executing the statement again. The query cache is shared among sessions, so a result set generated by one client can be sent in response to the same query issued by another client.
就是說(shuō)查詢緩存存儲(chǔ)了 SELECT 語(yǔ)句的文本以及響應(yīng)給客戶端的相應(yīng)結(jié)果。這樣,如果服務(wù)器稍后接收到相同的 SELECT 語(yǔ)句,服務(wù)器會(huì)先從查詢緩存中檢索結(jié)果,而不是再次解析和執(zhí)行該語(yǔ)句。查詢緩存在 session 之間共享,因此可以發(fā)送一個(gè)客戶端生成的結(jié)果集以響應(yīng)另一個(gè)客戶端發(fā)出的相同查詢。
如果當(dāng)前的查詢恰好命中了查詢緩存,那么在返回查詢結(jié)果之前 MySQL 會(huì)檢查一次用戶權(quán)限。這仍然是無(wú)須解析查詢SQL語(yǔ)句的,因?yàn)樵诓樵兙彺嬷幸呀?jīng)存放了當(dāng)前查詢需要訪問(wèn)的表信息。
那么既然涉及到緩存,就必然繞不開緩存一致性問(wèn)題了。值得慶幸的是,不需要我們進(jìn)行額外操作,查詢緩存并不會(huì)返回陳舊數(shù)據(jù)!
The query cache does not return stale data. When tables are modified, any relevant entries in the query cache are flushed.
當(dāng)表被修改時(shí),查詢緩存中的任何相關(guān)條目都會(huì)被 flushed,注意,這里的 flushed 翻譯為清空而不是刷新。
看起來(lái)好像還不錯(cuò)?不用我們手動(dòng)操作,失效緩存就能夠被自動(dòng)清空。
然而,很不幸的是,正是由于這個(gè)特性,從 MySQL 5.7.20 開始,官方不再推薦使用查詢緩存,并在 MySQL 8.0 中直接刪除了查詢緩存!
The query cache is deprecated as of MySQL 5.7.20, and is removed in MySQL 8.0.
其實(shí)不難理解,舉個(gè)例子,對(duì)于一個(gè)流量很大的論壇項(xiàng)目來(lái)說(shuō),查詢帖子表的需求每時(shí)每刻都存在,帖子也幾乎每時(shí)每刻都在增加,那只要這個(gè)表一更新,這個(gè)表上所有的查詢緩存都會(huì)被清空,這對(duì)于 MySQL 數(shù)據(jù)庫(kù)的壓力之大,可想而知了吧。費(fèi)個(gè)勁把查詢結(jié)果存起來(lái),還沒(méi)來(lái)得及使用呢,就被一個(gè)更新全清空了。
對(duì)于 MySQL 8.0 之前的版本來(lái)說(shuō),你可以將參數(shù) query_cache_type
設(shè)置成 DEMAND
,這樣所有的 SQL 語(yǔ)句都不會(huì)再使用查詢緩存。而對(duì)于你確定要使用查詢緩存的語(yǔ)句,可以用 SQL_CACHE
顯式指定,像下面這個(gè)語(yǔ)句一樣:
mysql> select SQL_CACHE * from t1 where id = 1;
四、解析器(Parser)
如果沒(méi)有命中或者沒(méi)有開啟查詢緩存,MySQL 服務(wù)器接下來(lái)要做的就是將一條 SQL 語(yǔ)句轉(zhuǎn)換成一個(gè)執(zhí)行計(jì)劃,再依照這個(gè)執(zhí)行計(jì)劃和存儲(chǔ)引擎進(jìn)行交互。這包括多個(gè)子階段:解析 SQL、預(yù)處理、優(yōu)化 SQL 執(zhí)行計(jì)劃。這個(gè)過(guò)程中任何錯(cuò)誤(例如語(yǔ)法錯(cuò)誤)都可能終止查詢。
其中解析 SQL 和預(yù)處理就是解析器做的事情,優(yōu)化 SQL 執(zhí)行計(jì)劃就是優(yōu)化器做的事情。這里我們先說(shuō)解析器。
這里《高性能 MySQL - 第 3 版》書中分得更細(xì)致點(diǎn),解析器用來(lái)解析 SQL,預(yù)處理器則用來(lái)預(yù)處理,我暫且把它們都?xì)w為解析器吧
所謂解析 SQL 就是說(shuō),MySQL 通過(guò)關(guān)鍵字對(duì) SQL 語(yǔ)句進(jìn)行解析,并生成一棵對(duì)應(yīng)的 “解析樹”,用于根據(jù)語(yǔ)法規(guī)則來(lái)驗(yàn)證語(yǔ)句是否正確。例如,它將驗(yàn)證是否使用錯(cuò)誤的關(guān)鍵字,或者使用關(guān)鍵字的順序是否正確等,再或者它還會(huì)驗(yàn)證引號(hào)是否能前后正確匹配。
而預(yù)處理則會(huì)進(jìn)一步檢查解析樹是否合法,例如,檢查數(shù)據(jù)表和數(shù)據(jù)列是否存在,檢查表名和字段名是否正確等。
五、優(yōu)化器(Optimizer)
現(xiàn)在,解析樹是合法的了,MySQL 已經(jīng)知道你要做什么了。不過(guò),一條查詢可以有很多種執(zhí)行計(jì)劃,最后都返回相同的結(jié)果,那到底該選擇哪種執(zhí)行計(jì)劃呢?
舉個(gè)簡(jiǎn)單的例子:
mysql> select * from t1 where id = 10 and name = "good";
對(duì)于上面這個(gè)語(yǔ)句,可以先查找 name = good 再查找 id = 10,也可以先查找 id = 10 再查找 name = good,這兩種不同的執(zhí)行計(jì)劃可能耗費(fèi)的時(shí)間成本是不一樣的。
那么優(yōu)化器的作用就是找到這其中最好的執(zhí)行計(jì)劃。需要注意的是,這里的執(zhí)行計(jì)劃是一個(gè)數(shù)據(jù)結(jié)構(gòu),而不是和很多其他的關(guān)系型數(shù)據(jù)庫(kù)那樣會(huì)生成對(duì)應(yīng)的字節(jié)碼。
另外,優(yōu)化器并不關(guān)心表使用的是什么存儲(chǔ)引擎,但存儲(chǔ)引擎對(duì)于優(yōu)化查詢是有影響的。優(yōu)化器會(huì)請(qǐng)求存儲(chǔ)引擎提供容量或某個(gè)具體操作的開銷信息,以及表數(shù)據(jù)的統(tǒng)計(jì)信息等。
當(dāng)優(yōu)化器階段完成后,這個(gè)語(yǔ)句的執(zhí)行計(jì)劃就確定下來(lái)了,就可以進(jìn)入執(zhí)行器階段了。
六、執(zhí)行器
和命中查詢緩存一樣,在開始執(zhí)行 SQL 語(yǔ)句之前,執(zhí)行器會(huì)先判斷一下當(dāng)前用戶對(duì)這個(gè)表有沒(méi)有執(zhí)行查詢的權(quán)限,如果沒(méi)有,就會(huì)返回沒(méi)有權(quán)限的錯(cuò)誤。
權(quán)限認(rèn)證完成后,MySQL 就會(huì)根據(jù)執(zhí)行計(jì)劃給出的指令逐步執(zhí)行。在根據(jù)執(zhí)行計(jì)劃逐步執(zhí)行的過(guò)程中,有大量的操作需要通過(guò)調(diào)用存儲(chǔ)引擎實(shí)現(xiàn)的接口來(lái)完成,這些接口也就是我們稱為 “handler API” 的接口。
查詢中的每一個(gè)表由一個(gè) handler 的實(shí)例表示。實(shí)際上,MySQL 在優(yōu)化階段就為每個(gè)表創(chuàng)建了一個(gè) handler 實(shí)例,優(yōu)化器根據(jù)這些實(shí)例的接口可以獲取表的相關(guān)信息,包括表的所有列名、索引統(tǒng)計(jì)信息,等等。
舉個(gè)例子:
mysql> select * from t1 where id = 10;
假設(shè)我們使用默認(rèn)的 InnoDB 引擎,則執(zhí)行器的執(zhí)行流程大概是這樣的(注意,如果 id 不是索引則會(huì)進(jìn)行全表掃描,一行一行的查找,如果是索引則會(huì)在索引組織表中查詢,比較負(fù)責(zé)。這里以非索引舉例):
1)調(diào)用 InnoDB 引擎接口獲取這個(gè)表的第一行記錄,判斷 id 值是不是 10,如果是則將這行記錄存在一個(gè)集合中;如果不是則進(jìn)入下一行的判斷,直到取到這個(gè)表的最后一行
2)執(zhí)行器將上述遍歷過(guò)程中所有滿足條件的行組成的記錄集作為結(jié)果返回給客戶端
七、小結(jié)
總結(jié)下一條查詢語(yǔ)句的執(zhí)行過(guò)程:
1.MySQL 客戶端與服務(wù)器間建立連接,客戶端發(fā)送一條查詢給服務(wù)器;
2.服務(wù)器先檢查查詢緩存,如果命中了緩存,則立刻返回存儲(chǔ)在緩存中的結(jié)果;否則進(jìn)入下一階段;
3.服務(wù)器端進(jìn)行 SQL 解析、預(yù)處理,生成合法的解析樹;
4.再由優(yōu)化器生成對(duì)應(yīng)的執(zhí)行計(jì)劃;
5.MySQL 根據(jù)優(yōu)化器生成的執(zhí)行計(jì)劃,調(diào)用相應(yīng)的存儲(chǔ)引擎的 API 來(lái)執(zhí)行,并將執(zhí)行結(jié)果返回給客戶端。
以上就是分析mysql中一條SQL查詢語(yǔ)句是如何執(zhí)行的的詳細(xì)內(nèi)容,更多關(guān)于mysql查詢語(yǔ)句是如何執(zhí)行的的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
您可能感興趣的文章:- 一篇文章弄懂MySQL查詢語(yǔ)句的執(zhí)行過(guò)程
- 詳解MySQL 查詢語(yǔ)句的執(zhí)行過(guò)程
- Python使用sql語(yǔ)句對(duì)mysql數(shù)據(jù)庫(kù)多條件模糊查詢的思路詳解
- mysql查詢的控制語(yǔ)句圖文詳解
- Mysql將查詢結(jié)果集轉(zhuǎn)換為JSON數(shù)據(jù)的實(shí)例代碼
- 使用Visual Studio Code連接MySql數(shù)據(jù)庫(kù)并進(jìn)行查詢
- MySQL查詢優(yōu)化之查詢慢原因和解決技巧
- mysql聚合統(tǒng)計(jì)數(shù)據(jù)查詢緩慢的優(yōu)化方法
- MySQL多表查詢的具體實(shí)例
- mysql從一張表查詢批量數(shù)據(jù)并插入到另一表中的完整實(shí)例