
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)

客戶端如果太長(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。
- MySQL 5.7 或更新版本,每次執(zhí)行一個(gè)比較大的操作后,執(zhí)行
mysql_reset_connection重新初始化連接資源。這個(gè)過(guò)程不需要重連和重新做權(quán)限驗(yàn)證,但是會(huì)將連接恢復(fù)到剛剛創(chuàng)建完時(shí)的狀態(tài)。
- MySQL 5.7 或更新版本,每次執(zhí)行一個(gè)比較大的操作后,執(zhí)行
可能有的第二步: 查詢(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 并不是完全相同的。