1 . Server 層:一條SQL查詢(xún)語(yǔ)句執(zhí)行流程


MySQL 可以分為 Server 層和存儲(chǔ)引擎層兩部分。

Server:

涵蓋 MySQL 的大多數(shù)
核心服務(wù)功能,內(nèi)置函數(shù)(如日期、時(shí)間、數(shù)學(xué)和加密函數(shù)等),
所有跨存儲(chǔ)引擎的功能都在這一層實(shí)現(xiàn),比如 存儲(chǔ)過(guò)程、觸發(fā)器、視圖

存儲(chǔ)引擎層

數(shù)據(jù)的存儲(chǔ)和提取。
插件式的,支持 InnoDB、MyISAM、Memory 等多個(gè)存儲(chǔ)引擎。

InnoDB,從 MySQL 5.5.5 版本開(kāi)始成為了默認(rèn)存儲(chǔ)引擎。
create table 建表的時(shí)候,如果不指定引擎類(lèi)型,默認(rèn)使用的就是 InnoDB。

使用 engine=memory, 來(lái)指定使用內(nèi)存引擎創(chuàng)建表。

第一步: 連接器

用戶名密碼登錄,連接器會(huì)到權(quán)限表里面查出你擁有的權(quán)限。之后,這個(gè)連接里面的權(quán)限判斷邏輯,都將依賴(lài)于此時(shí)讀到的權(quán)限。
成功建立連接后,即使管理員賬號(hào)對(duì)這個(gè)用戶的權(quán)限做了修改,也不會(huì)影響已經(jīng)存在連接的權(quán)限。
因此,連接完成后,如果你沒(méi)有后續(xù)的動(dòng)作,這個(gè)連接就處于空閑狀態(tài)


這里是Query, 空閑會(huì)變成"sleep"

客戶端如果太長(zhǎng)時(shí)間(參數(shù) wait_timeout 控制的,默認(rèn)值是 8 小時(shí))沒(méi)動(dòng)靜,連接器就會(huì)自動(dòng)將它斷開(kāi)。

如果在連接被斷開(kāi)之后,客戶端再次發(fā)送請(qǐng)求的話,就會(huì)收到一個(gè)錯(cuò)誤提醒: Lost connection to MySQL server during query。就需要重連

長(zhǎng)連接 : 連接成功后,如果客戶端持續(xù)有請(qǐng)求,則一直使用同一個(gè)連接。
短連接: 每次執(zhí)行完很少的幾次查詢(xún)就斷開(kāi)連接,下次查詢(xún)?cè)僦匦陆⒁粋€(gè)。

盡量使用長(zhǎng)連接, 但是MySQL 在執(zhí)行過(guò)程中臨時(shí)使用的內(nèi)存是管理在連接對(duì)象里面的, 占用內(nèi)存漲得特別快,這些資源會(huì)在連接斷開(kāi)的時(shí)候才釋放。
如果長(zhǎng)連接累積下來(lái),可能導(dǎo)致內(nèi)存占用太大,被系統(tǒng)強(qiáng)行殺掉(OOM),從現(xiàn)象看就是 MySQL 異常重啟了。

以下兩種方案:

  • 1.定期斷開(kāi)長(zhǎng)連接。使用一段時(shí)間,或者程序里面判斷執(zhí)行過(guò)一個(gè)占用內(nèi)存的大查詢(xún)后,斷開(kāi)連接,之后要查詢(xún)?cè)僦剡B。

    1. MySQL 5.7 或更新版本,每次執(zhí)行一個(gè)比較大的操作后,執(zhí)行mysql_reset_connection 重新初始化連接資源。這個(gè)過(guò)程不需要重連和重新做權(quán)限驗(yàn)證,但是會(huì)將連接恢復(fù)到剛剛創(chuàng)建完時(shí)的狀態(tài)。

可能有的第二步: 查詢(xún)緩存
MySQL 8.0 版本直接將查詢(xún)緩存的整塊功能刪掉了.
沒(méi)刪掉也不要用,除非你的業(yè)務(wù)就是有一張靜態(tài)表,很長(zhǎng)時(shí)間才會(huì)更新一次。比如,一個(gè)系統(tǒng)配置表,那這張表上的查詢(xún)才適合使用查詢(xún)緩存。
因?yàn)?查詢(xún)緩存的失效非常頻繁,只要有對(duì)一個(gè)表的更新,這個(gè)表上所有的查詢(xún)緩存都會(huì)被清空

第二步: 分析器

開(kāi)始真正執(zhí)行語(yǔ)句了。需要對(duì) SQL 語(yǔ)句做解析,知道到底要干啥

法分析:

輸入的是由多個(gè)字符串和空格組成的一條 SQL 語(yǔ)句,MySQL 需要識(shí)別出里面的字符串分別是什么,代表什么。
"select"這個(gè)關(guān)鍵字識(shí)別出來(lái),這是一個(gè)查詢(xún)語(yǔ)句。它也要把字符串“T”識(shí)別成“表名 T”,把字符串“ID”識(shí)別成“列 ID”。

語(yǔ)法分析:

根據(jù)法分析的結(jié)果,這個(gè) SQL 語(yǔ)句是否滿足 MySQL 語(yǔ)法。
如果不對(duì),就會(huì)收到的錯(cuò)誤提醒,就是平時(shí)看到的那種報(bào)錯(cuò)

然后,生成一課對(duì)應(yīng)的解析樹(shù)。
預(yù)處理器進(jìn)一步檢查解析樹(shù)的合法。比如: 數(shù)據(jù)表和數(shù)據(jù)列是否存在, 別名是否有歧義等。如果通過(guò)則生成新的解析樹(shù),再提交給優(yōu)化器。

查詢(xún)沒(méi)的列報(bào)錯(cuò), 是在這步

select * from T where k=1;
Unknown column ‘k’ in ‘where clause

第三步:優(yōu)化器

經(jīng)過(guò)了分析器,MySQL 就知道你要做什么了。
MySQL 要決定好怎么樣做最優(yōu).
表里面有多個(gè)索引的時(shí)候,決定使用哪個(gè)索引;
在一個(gè)語(yǔ)句有多表關(guān)聯(lián)(join)的時(shí)候,決定各個(gè)表的連接順序。

比如

mysql> select * from t1 join t2 using(ID) where t1.c=10 and t2.d=20;
先查詢(xún)t1表t2表結(jié)果是一樣的,效率會(huì)有不同

優(yōu)化器階段完成后,這個(gè)語(yǔ)句的執(zhí)行方案就確定下來(lái)了

第四步: 執(zhí)行器

重要要開(kāi)始執(zhí)行語(yǔ)句。

要先判斷一下你對(duì)這個(gè)表 T 有沒(méi)有執(zhí)行查詢(xún)的權(quán)限,如果沒(méi)有

mysql> select * from T where ID=10;
 ERROR 1142 (42000): SELECT command denied to user 'b'@'localhost' for table 'T'

如果有權(quán)限,打開(kāi)表繼續(xù)執(zhí)行。
打開(kāi)表的時(shí)候,根據(jù)表的引擎定義,去使用這個(gè)引擎提供的接口。

mysql> select * from T where ID=10;
例如: 表 T 中,ID 字段沒(méi)有索引,執(zhí)行流程是這樣的:

  • 調(diào)用 InnoDB 引擎接口:
    取這個(gè)表的第一行,判斷 ID 值是不是 10,不是跳過(guò),
    是將這行存在結(jié)果集中;

  • 調(diào)用引擎接口:
    取“下一行”,重復(fù)相同的判斷邏輯,直到取到這個(gè)表的最后一行。

  • 結(jié)果集返回給客戶端。結(jié)束

至此,這個(gè)語(yǔ)句就執(zhí)行完成了。

對(duì)于有索引的表,“取滿足條件的第一行”這個(gè)接口,之后循環(huán)取“滿足條件的下一行”這個(gè)接口
這些接口都是引擎中已經(jīng)定義好的。

慢查詢(xún)?nèi)罩局?code>rows_examined 的字段,表示這個(gè)語(yǔ)句執(zhí)行過(guò)程中掃描了多少行。這個(gè)值就是在執(zhí)行器每次調(diào)用引擎獲取數(shù)據(jù)行的時(shí)候累加的。

在有些場(chǎng)景下,執(zhí)行器調(diào)用一次,在引擎內(nèi)部則掃描了多行,因此引擎掃描行數(shù)rows_examined 并不是完全相同的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容