在深入MySQL之前, 不妨分析一下如下的sql語句是如何在MySQL執(zhí)行的呢?
? ? ? ? select * from t where id = 10 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?語句1
? ? ? ? 想要清晰的了解這條語句在MySQL的內(nèi)部執(zhí)行流程,則必須要先了解MySQL的基礎(chǔ)架構(gòu)組成
MySQL由以下幾個部分組成:
? ? ? ? 1、客戶端: 客戶端連接器(Client)對MySQL的服務(wù)層(MySQL Server)進行接入
? ? ? ? 2、MySQL Server: 包括SQL接口、連接池、緩存、解析器、優(yōu)化器、執(zhí)行器等, 所有跨存儲引擎的功能都在這一層實現(xiàn),比如存儲過程、觸發(fā)器、視圖等
? ? ? ? 3、存儲引擎: MySQL Server通過接口, 實現(xiàn)對各類不同存儲引擎的支持
? ? ? ? 4、生成系統(tǒng)文件, ?包括文件和日志
? ? ? ? MySQL Server層中的各種應(yīng)用又提供了哪些功能呢??
? ? ? ? 1、連接池:
? ? ? ? ?顧名思義, 它提供了客戶端與MySQL建立連接的功能, ?MySQL既支持長連接, 也支持短連接; ?通常我們在連接池中采用長連接的方式以提升連接的復(fù)用效率, 但是保持長連接會消耗內(nèi)存, 當內(nèi)存消耗達到一定程度時, MySQL會被系統(tǒng)強制crash, 因此在開發(fā)過程中, 我們通常采用以下兩種方案來解決此類問題:
? ? ? ? (1) 定期斷開長連接。使用一段時間,或者程序里面判斷執(zhí)行過一個占用內(nèi)存的大查詢后,斷開 連接,之后要查詢再重連。
? ? ? ? (2) 如果MySQL的版本是MySQL5.7及以上,可以在每次執(zhí)行一個比較大的操作后,通過執(zhí)行 mysql_reset_connection來重新初始化連接資源。這個過程不需要重連和重新做權(quán)限驗證, 但是會將連接恢復(fù)到剛剛創(chuàng)建完時的狀態(tài)。
? ? ? ? 2、SQL接口: ?
? ? ? ? SQL接口中涵蓋MySQL的大多數(shù)核心服務(wù)功能,以及所有的內(nèi)置函數(shù)(如日期、時間、數(shù)學和加密函數(shù)等)
? ? ? ? 3、緩存:
? ? ? ? 連接建立完成后,就可以執(zhí)行sql語句了。此時執(zhí)行邏輯就會來到第二步: 查詢緩存。MySQL拿到語句1請求后,會先到查詢緩存看看,之前是不是執(zhí)行過這條語句。之前執(zhí)行過的語句及其結(jié)果以key-value對的形式,被直接緩存在內(nèi)存中。如果語句1之前被查詢過,則語句1作為key,語句1的查詢結(jié)果作為value。這樣就可以通過查詢緩存中的key快速的取到與之對應(yīng)的value并返回給客戶端,如果語句1不在查詢緩存中,就會繼續(xù)后面的執(zhí)行階段。執(zhí)行完成后,執(zhí)行結(jié)果會被存入查詢緩存中。
? ? ? ? 但是大多數(shù)情況下不會使用查詢緩存,因為緩存的失效非常頻繁,只要有對一個表的更新,這個表上所有的查詢緩存都會被清空。那么在什么場景下我們會使用查緩存的方式呢?
? ? ? ? 當你的業(yè)務(wù)就是有一張靜態(tài)表,很長時間才會更新一次。 比如,一個系統(tǒng)配置表,那這張表上的查詢才適合使用查詢緩存。 在MySQL8.0之后,緩存應(yīng)用塊就已經(jīng)被移除。
? ? ? ? 4、解析器
? ? ? ? 當語句1沒有命中查詢緩存時,就開始真正的對語句1進行執(zhí)行,此時解析器開始工作。解析器的主要工作就是對sql語句進行解析,首先判斷sql語句的合法性,其次判斷sql要實現(xiàn)什么功能,比如增刪改查等。
? ? ? ? 5、優(yōu)化器
? ? ? ? 在sql語句經(jīng)過解析器之后,還需要經(jīng)過優(yōu)化器來對此語句進行優(yōu)化處理。
? ? ? ? 優(yōu)化器是在表里面有多個索引的時候,決定使用哪個索引;或者在一個語句有多表關(guān)聯(lián)(join)
的時候,決定各個表的連接順序。
? ? ? ? 比如你執(zhí)行語句2的連表查詢時,
? ? ? ? select * from t1 join t2 using(ID) where t1.c=10 and t2.d=20 ? ? ? ? ? ? 語句2
? ? ? ? 既可以先從表t1里面取出c=10的記錄的ID值,再根據(jù)ID值關(guān)聯(lián)到表t2,再判斷t2里面d的值是 否等于20。 也可以先從表t2里面取出d=20的記錄的ID值,再根據(jù)ID值關(guān)聯(lián)到t1,再判斷t1里面c的值是否 等于10。
? ? ? ? 這兩種執(zhí)行方法的邏輯結(jié)果是一樣的,但是執(zhí)行的效率會有不同,而優(yōu)化器的作用就是決定選擇使用哪一個方案。
? ? ? ? 6、執(zhí)行器
? ? ? ? MySQL通過解析器知道了你要做什么,通過優(yōu)化器知道了該怎么做,于是就進入了執(zhí)行器階 段,開始執(zhí)行語句。
? ? ? ? 開始執(zhí)行的時候,要先判斷一下你對這個表T有沒有執(zhí)行查詢的權(quán)限,如果沒有,就會返回沒有 權(quán)限的錯誤,如下所示。
? ? ? ? 如果有權(quán)限,就打開表繼續(xù)執(zhí)行。打開表的時候,執(zhí)行器就會根據(jù)表的引擎定義,去使用這個引 擎提供的接口。
? ? ? ? 比如我們這個例子中的表T中,ID字段沒有索引,那么執(zhí)行器的執(zhí)行流程是這樣的:
? ? ? ? 1. 調(diào)用InnoDB引擎接口取這個表的第一行,判斷ID值是不是10,如果不是則跳過,如果是則將這行存在結(jié)果集中;
? ? ? ? 2. 調(diào)用引擎接口取“下一行”,重復(fù)相同的判斷邏輯,直到取到這個表的最后一行。
? ? ? ? 3. 執(zhí)行器將上述遍歷過程中所有滿足條件的行組成的記錄集作為結(jié)果集返回給客戶端。
? ? ? ? 至此,這個語句就執(zhí)行完成了。 對于有索引的表,執(zhí)行的邏輯也差不多。第一次調(diào)用的是“取滿足條件的第一行”這個接口,之后循環(huán)取“滿足條件的下一行”這個接口,這些接口都是引擎中已經(jīng)定義好的。 你會在數(shù)據(jù)庫的慢查詢?nèi)罩局锌吹揭粋€rows_examined的字段,表示這個語句執(zhí)行過程中掃描了多少行。這個值就是在執(zhí)行器每次調(diào)用引擎獲取數(shù)據(jù)行的時候累加的。
?
?