作為一名 JAVA開發(fā)人員,寫 SQL 語句是常有的事,但是你知道 SQL 語句背后的處理邏輯嗎?比如下面這條 SQL 語句:
select * from user where id=1
執(zhí)行完這條語句后,我們就會得到 id 為 1 的用戶信息。那么對于這一條 SQL 語句,MySQL服務(wù)器做了哪些處理呢?這篇文章我們就一起打卡 MySQL 數(shù)據(jù)庫中對 SQL 語句的處理邏輯。
了解 MySQL 數(shù)據(jù)庫的 SQL 語句內(nèi)部處理邏輯有什么好處?當(dāng)我們碰到 MySQL 的一些異常或者問題時,就能夠直戳本質(zhì),更為快速地定位并解決問題。
想要更好的了解 SQL 語句的內(nèi)部處理邏輯,我們可以先看 MySQL 的基本架構(gòu)圖,這樣我們可以站在更高的角度去俯瞰 MySQL 數(shù)據(jù)庫,MySQL 的基本架構(gòu)示意圖如下:
從圖中,我們可以清晰的看出 MySQL 的架構(gòu)和各個模塊以及 SQL 語句的執(zhí)行過程,MySQL 數(shù)據(jù)庫整體可以分為 Server 層和存儲引擎層兩部分,其中 Server 層是共有的,而存儲引擎層則是可以以插件的形式進(jìn)行擴展。一條 SQL 語句大概會經(jīng)歷鏈接管理、解析與優(yōu)化、最后到存儲引擎,這三個模塊。接下來我們就來聊一聊這三個模塊。
連接管理
連接管理是 SQL 語句執(zhí)行過程中碰到的第一關(guān),鏈接管理就像一扇大門一樣,控制著客戶端與 Server 服務(wù)端的交互,連接管理主要工作是客戶端的身份認(rèn)證和連接線程的管理。
每個客戶端與 Server 建立連接時,服務(wù)端都會創(chuàng)建一個線程來與客戶端進(jìn)行交互,交互的第一項內(nèi)容就是驗證客戶端的身份,認(rèn)證憑據(jù)是基于客戶端發(fā)起連接請求時攜帶的主機信息、用戶名、密碼。如果認(rèn)證失敗,則結(jié)束連接任務(wù),并且返回的 Access denied for user 錯誤。
如果認(rèn)證成功,連接管理還會做一件事情,到權(quán)限表中查詢出該用戶的權(quán)限,在這次連接下,后續(xù)的權(quán)限判斷都是基于此時讀取的權(quán)限為依據(jù),也就是說連接成功后,即使管理員對這個用戶做了權(quán)限修改,也不會影響這次連接的權(quán)限驗證。
連接管理需要做的事情就比較簡單,主要是負(fù)責(zé)客戶端與服務(wù)端進(jìn)行連接,當(dāng)然在連接線程上,連接管理也做了優(yōu)化,并不是每個客戶端執(zhí)行完任務(wù)之后,就把該線程銷毀,連接管理會把這些線程緩存起來,等待新的連接,這也就不會頻繁的創(chuàng)建和銷毀線程,從而節(jié)約了開銷。
解析與優(yōu)化
完成連接管理之后,SQL 語句執(zhí)行的第二步就是解析和優(yōu)化,這一步就非常的復(fù)雜,SQL 語句查詢的所有操作都在這里了。我們可以將這一步細(xì)分為 4 小步。
查詢緩存
在 MySQL 服務(wù)端也有緩存,這是一個非常雞肋的功能,為什么呢?看完了你就知道了。
MySQL 服務(wù)器拿到查詢請求后,會先到查詢緩存看看,之前是不是執(zhí)行過這條語句。之前執(zhí)行過的語句及其結(jié)果可能會以 key-value 對的形式,被直接緩存在內(nèi)存中。key 是查詢的語句,value 是查詢的結(jié)果。如果你的查詢能夠直接在這個緩存中找到 key,那么這個 value 就會被直接返回給客戶端。如果語句不在查詢緩存中,就會繼續(xù)后面的執(zhí)行階段。執(zhí)行完成后,執(zhí)行結(jié)果會被存入查詢緩存中。
看上去沒毛病,這樣做會大大提升 MySQL 的性能,然而,你想多了,MySQL 的查詢緩存命中率非常的低,主要原因是如果兩個查詢請求在任何字符上的不同(例如:空格、注釋、大小寫),都會導(dǎo)致緩存不會命中。
還有就是緩存有可能獲取到錯誤的數(shù)據(jù),以某些系統(tǒng)函數(shù)舉例,可能同樣的函數(shù)的兩次調(diào)用會產(chǎn)生不一樣的結(jié)果,比如函數(shù)NOW,每次調(diào)用都會產(chǎn)生最新的當(dāng)前時間,如果在一個查詢請求中調(diào)用了這個函數(shù),那即使查詢請求的文本信息都一樣,那不同時間的兩次查詢也應(yīng)該得到不同的結(jié)果,如果在第一次查詢時就緩存了,那第二次查詢的時候直接使用第一次查詢的結(jié)果就是錯誤的!
除了這些之外,MySQL 緩存的失效也非常的頻繁,MySQL的緩存系統(tǒng)會監(jiān)測涉及到的每張表,只要該表的結(jié)構(gòu)或者數(shù)據(jù)被修改,如對該表使用了 INSERT、 UPDATE、DELETE、TRUNCATE TABLE、ALTER TABLE、DROP TABLE 或 DROP DATABASE 語句,那使用該表的所有高速緩存查詢都將變?yōu)闊o效并從高速緩存中刪除!
看到這里你知道查詢緩存很雞肋了吧,緩存對 MySQL 數(shù)據(jù)庫來說弊大于利,所以在 MySQL 8.0 版本直接將查詢緩存的整塊功能刪掉了
語法解析和預(yù)處理
如果查詢緩存沒有命中,接下來就需要進(jìn)入正式的查詢階段了。因為客戶端程序發(fā)送過來的請求只是一段文本而已,所以 MySQL 服務(wù)器程序首先要對這段文本做語法解析。
首先通過關(guān)鍵字將 SQL 語句進(jìn)行解析,并且生成一個“解析樹”。MySQL 解析器將使用 MySQL 語法規(guī)則驗證和解析查詢,例如,關(guān)鍵字是否使用正確、關(guān)鍵字的順序是否正確或者引號是否前后匹配等。
預(yù)處理是根據(jù)一些 MySQL 規(guī)則進(jìn)一步檢查解析樹是否合法,例如數(shù)據(jù)表和數(shù)據(jù)列是否存在,還會解析名字和別名,看看他們是否有歧義等。
查詢優(yōu)化
語法解析和預(yù)處理之后,你的需求就明白了,需要查詢哪張表,查詢的數(shù)據(jù)列是哪些、條件是什么等等。但是使用怎么樣的方式是最優(yōu)查詢方式呢?查詢優(yōu)化就是來干這個事的,MySQL 的優(yōu)化程序會對我們的語句做一些優(yōu)化,如外連接轉(zhuǎn)換為內(nèi)連接、表達(dá)式簡化、子查詢轉(zhuǎn)為連接等等。優(yōu)化的結(jié)果就是生成一個執(zhí)行計劃,這個執(zhí)行計劃表明了應(yīng)該使用哪些索引進(jìn)行查詢,表之間的連接順序是啥樣的。
執(zhí)行器
執(zhí)行器會執(zhí)行查詢優(yōu)化后的執(zhí)行計劃,通過與存儲引擎交互,完成數(shù)據(jù)的查詢操作,返回最終的數(shù)據(jù)結(jié)果。
開始執(zhí)行的時候,要先判斷一下你對這個表 T 有沒有執(zhí)行查詢的權(quán)限,如果沒有,就會返回沒有權(quán)限的錯誤,如下所示 (在工程實現(xiàn)上,如果命中查詢緩存,會在查詢緩存返回結(jié)果的時候,做權(quán)限驗證。查詢也會在優(yōu)化器之前調(diào)用 precheck 驗證權(quán)限)。
mysql> select * from user where ID=1;
ERROR 1142 (42000): SELECT command denied to user 'b'@'localhost' for table 'T'
如果有權(quán)限,就打開表繼續(xù)執(zhí)行。打開表的時候,執(zhí)行器就會根據(jù)表的引擎定義,去使用這個引擎提供的接口。
比如我們這個例子中的表 user 中,假設(shè) ID 字段沒有索引,那么執(zhí)行器的執(zhí)行流程是這樣的:
1、調(diào)用 InnoDB 引擎接口取這個表的第一行,判斷 ID 值是不是 10,如果不是則跳過,如果是則將這行存在結(jié)果集中;
2、調(diào)用引擎接口取“下一行”,重復(fù)相同的判斷邏輯,直到取到這個表的最后一行。
3、執(zhí)行器將上述遍歷過程中所有滿足條件的行組成的記錄集作為結(jié)果集返回給客戶端。
到這里,執(zhí)行 SQL 語句就執(zhí)行完了,其實這內(nèi)部還是非常復(fù)雜的。
存儲引擎
到上面為止,SQL 語句就執(zhí)行完了,但是與真實數(shù)據(jù)打交道的是存儲引擎,存儲引擎是 MySQL服務(wù)器對數(shù)據(jù)的存儲和提取操作的封裝模塊。我們知道表是由一行一行的記錄組成的,但這只是一個邏輯上的概念,物理上如何表示記錄,怎么從表中讀取數(shù)據(jù),怎么把數(shù)據(jù)寫入具體的物理存儲器上,這都是存儲引擎負(fù)責(zé)的事情。
為了實現(xiàn)不同的功能,MySQL提供了各式各樣的存儲引擎,不同存儲引擎管理的表具體的存儲結(jié)構(gòu)可能不同,采用的存取算法也可能不同。比如,MySQL5.7 之后默認(rèn)的 InnoDB 存儲引擎。
可以看出一條 SQL 語句的執(zhí)行還是非常復(fù)雜的,涉及到了很多的模塊,文章到這里就結(jié)束了,感謝您的閱讀,希望這篇文章對你的學(xué)習(xí)和工作有所幫助,如果您覺得文章有用,歡迎點贊+轉(zhuǎn)發(fā)。
最后
覺得此文不錯的大佬們可以多多關(guān)注或者幫忙轉(zhuǎn)發(fā)分享一下哦,感謝!!!!






