基礎(chǔ)架構(gòu)
MySQL的基礎(chǔ)架構(gòu)如下圖:
大體來(lái)說(shuō)沪么,MySQL 可以分為 Server 層和存儲(chǔ)引擎層兩部分硼婿。
大多數(shù)的MySQL服務(wù)功能都在Server層,包括查詢解析禽车、緩存寇漫、分析刊殉、優(yōu)化以及所有內(nèi)置函數(shù),所有跨存儲(chǔ)引擎的功能都在這一層實(shí)現(xiàn)州胳,比如存儲(chǔ)過(guò)程记焊、觸發(fā)器、視圖等栓撞。
而存儲(chǔ)引擎則是負(fù)責(zé)存儲(chǔ)數(shù)據(jù)遍膜,提供讀寫(xiě)接口。有多種存儲(chǔ)引擎瓤湘,例如InnoDB瓢颅、MyISAM等,各有各的優(yōu)勢(shì)和劣勢(shì)岭粤,針對(duì)不同的應(yīng)用場(chǎng)景惜索。最常用的是 InnoDB,在5.5.5版本以后剃浇, InnoDB是默認(rèn)引擎巾兆,也可以在創(chuàng)建表時(shí)使用“engine=存儲(chǔ)引擎”來(lái)指定特定的引擎。
存儲(chǔ)引擎不會(huì)解析SQL虎囚,但是InnoDB除外角塑,它會(huì)解析外鍵定義,因?yàn)镾erver層沒(méi)有實(shí)現(xiàn)該功能淘讥。
連接器
我們?cè)诓樵償?shù)據(jù)之前圃伶,需要先連接 MySQL。我們可以通過(guò)以下命令連接數(shù)據(jù)庫(kù):
mysql -h ip -P port -u user -p password
其中mysql 是客戶端工具蒲列,用來(lái)跟服務(wù)端建立連接窒朋。在完成TCP握手后,連接器開(kāi)始認(rèn)證你的身份蝗岖,認(rèn)證基于用戶名侥猩、密碼和原始主機(jī)信息。如果使用SSL連接抵赢,還可以使用X.509證書(shū)認(rèn)證欺劳。如果用戶名或密碼出錯(cuò),則收到"Access denied for user"的錯(cuò)誤铅鲤,認(rèn)證通過(guò)則連接器到權(quán)限表里面查出你擁有的權(quán)限划提,之后進(jìn)行的查詢都將依賴于此權(quán)限。也就是說(shuō)邢享,一個(gè)用戶成功建立連接后鹏往,即使你用管理員賬號(hào)這個(gè)用戶的權(quán)限做了修改,也不會(huì)影響已經(jīng)存在的連接的權(quán)限骇塘。
如果客戶端太久沒(méi)有動(dòng)作伊履,則連接器會(huì)自動(dòng)將它斷開(kāi)袜漩,這個(gè)時(shí)間是由參數(shù) wait_timeout 控制的,默認(rèn)值是8小時(shí)湾碎,5.7 版本 斷開(kāi)后可以自動(dòng)重連宙攻。
由于建立連接是相對(duì)復(fù)雜的操作,所以我們一般會(huì)使用長(zhǎng)連接介褥,但是 mysql 在執(zhí)行的過(guò)程中臨時(shí)使用的內(nèi)存是管理在連接對(duì)象中的座掘,這些資源會(huì)等到連接斷開(kāi)才釋放。所以如果連接使用的時(shí)間過(guò)長(zhǎng)柔滔,會(huì)導(dǎo)致內(nèi)存占用過(guò)高溢陪。在 MySQL 5.7 版本之后,可以通過(guò)mysql_reset_connection
重新初始化連接資源睛廊,該命令會(huì)釋放資源形真,但是不需要重新連接和重新做權(quán)限驗(yàn)證。
但是現(xiàn)在開(kāi)發(fā)者并不需要注意在程序中是使用短連接或是長(zhǎng)連接了超全,而是將連接交由連接池管理咆霜。
查詢緩存
MySQL 拿到一個(gè)查詢請(qǐng)求后,如果查詢緩存是打開(kāi)的會(huì)先到查詢緩存看看嘶朱,之前是不是執(zhí)行過(guò)這條語(yǔ)句蛾坯。之前執(zhí)行過(guò)的語(yǔ)句及其結(jié)果MySQL將其放在一個(gè)引用表中,類似 key-value 對(duì)的形式疏遏。key 是查詢的語(yǔ)句脉课、當(dāng)前要查詢的數(shù)據(jù)庫(kù)、客戶端協(xié)議版本等等财异,value 是查詢的結(jié)果倘零。如果你的查詢能夠直接在這個(gè)緩存中找到 key,那么這個(gè) value 就會(huì)被直接返回給客戶端(返回之前會(huì)驗(yàn)證權(quán)限)戳寸。
如果語(yǔ)句不在查詢緩存中呈驶,就會(huì)繼續(xù)后面的執(zhí)行階段。執(zhí)行完成后庆揩,執(zhí)行結(jié)果會(huì)被存入查詢緩存中俐东。
但是只要對(duì)表進(jìn)行更新跌穗,那么該表相關(guān)的所有查詢緩存就會(huì)被清空订晌。因此,如果更新頻繁的表蚌吸,不建議進(jìn)行查詢緩存锈拨,除非是配置表這類的,會(huì)比較適合使用查詢緩存羹唠。
在使用查詢緩存時(shí)奕枢,需要注意的是 MySQL 中判斷緩存是否命中時(shí)娄昆,并不會(huì)解析、或者格式化缝彬、參數(shù)化查詢語(yǔ)句(實(shí)際上萌焰,在檢查查詢緩存之前, MySQL 只做了一件事情谷浅,就是通過(guò)一個(gè)大小寫(xiě)不敏感的檢查看看 SQL 語(yǔ)句是否是以 SEL 開(kāi)頭的扒俯,以此來(lái)判斷是否是查詢語(yǔ)句),而是直接使用 SQL 語(yǔ)句和客戶端發(fā)送過(guò)來(lái)的其他原始信息與緩存 key 直接匹配一疯,所以 SQL 語(yǔ)句上的任何不同都會(huì)導(dǎo)致緩存的不命中撼玄。不止如此,但查詢語(yǔ)句出現(xiàn)不確定的數(shù)據(jù)(任何用戶定義的函數(shù)墩邀、存儲(chǔ)函數(shù)掌猛、用戶變量、臨時(shí)表眉睹、mysql 庫(kù)中的系統(tǒng)表荔茬、任何包含列級(jí)別權(quán)限的表)時(shí),不會(huì)被緩存竹海,例如:
DATA_SUB(CURRENT_DATE, INTERVAL 1 DAY) -- 不會(huì)被緩存
DATA_SUB('2020-3-22', INTERVAL 1 DAY) -- 會(huì)被緩存
將參數(shù) query_cache_type 設(shè)置成 DEMAND兔院,這樣對(duì)于默認(rèn)的 SQL 語(yǔ)句都不使用查詢緩存。而對(duì)于你確定要使用查詢緩存的語(yǔ)句站削,可以用 SQL_CACHE 顯式指定坊萝,像下面這個(gè)語(yǔ)句一樣:
select SQL_CACHE * from user;
查詢緩存默認(rèn)是開(kāi)啟的许起,可以關(guān)閉查詢緩存十偶。
- 臨時(shí)的直接命令行執(zhí)行
set global query_cache_size=0
set global query_cache_type=0
- 永久的修改配置文件my.cnf ,添加下面的配置query_cache_type=0
query_cache_size=0
需要注意的是在 MySQL 8.0 版本,查詢緩存的整塊功能已經(jīng)被刪除了园细。
解析器
在執(zhí)行 SQL 前惦积,需要先對(duì) SQL 語(yǔ)句進(jìn)行解析。
解析器先對(duì) SQL 語(yǔ)句做詞法分析猛频,先識(shí)別出哪些是關(guān)鍵字狮崩,代表什么(將輸入轉(zhuǎn)化為一個(gè)個(gè) Token,然后分析哪些 Token 是關(guān)鍵字鹿寻,哪些不是)睦柴。
然后再做語(yǔ)法分析,通過(guò)語(yǔ)法規(guī)則來(lái)驗(yàn)證和解析查詢毡熏,比如 SQL 中是否使用了錯(cuò)誤的關(guān)鍵字或者關(guān)鍵字的順序是否正確坦敌、及符號(hào)是否正確,并生成語(yǔ)法樹(shù)。
預(yù)處理器則會(huì)根據(jù) MySQL 規(guī)則進(jìn)一步檢查語(yǔ)法樹(shù)是否合法狱窘。比如檢查要查詢的數(shù)據(jù)表和數(shù)據(jù)列是否存在杜顺,解析名字和別名是否有歧義等等。
接下來(lái)預(yù)處理器還會(huì)驗(yàn)證權(quán)限(precheck)蘸炸。
如果語(yǔ)句不對(duì)躬络,就會(huì)收到“You have an error in yourSQL syntax”的錯(cuò)誤提醒。一般語(yǔ)法錯(cuò)誤會(huì)提示第一個(gè)出現(xiàn)錯(cuò)誤的位置搭儒,所以你要關(guān)注的是緊接“use near”的內(nèi)容洗鸵。
優(yōu)化器
通過(guò)解析器的解析后,優(yōu)化器就要對(duì)SQL語(yǔ)句進(jìn)行優(yōu)化仗嗦。優(yōu)化器并不關(guān)心表使用的存儲(chǔ)引擎是什么膘滨,但是存儲(chǔ)引擎對(duì)于查詢優(yōu)化是有影響的,因?yàn)閮?yōu)化器會(huì)請(qǐng)求存儲(chǔ)引擎提供容量或是某個(gè)具體操作的開(kāi)銷信息以及表數(shù)據(jù)的統(tǒng)計(jì)信息稀拐,這些都會(huì)影響優(yōu)化器對(duì)查詢語(yǔ)句的優(yōu)化火邓。
優(yōu)化器對(duì)查詢語(yǔ)句的優(yōu)化有,優(yōu)化多表關(guān)聯(lián)的時(shí)候德撬,決定各個(gè)表的連接的順序铲咨;有多個(gè)索引的時(shí)候,決定使用哪個(gè)索引蜓洪;還有重寫(xiě)查詢等等纤勒。
可以使用explain來(lái)查看執(zhí)行計(jì)劃,然后調(diào)整SQL語(yǔ)句隆檀、修改配置等等摇天。
執(zhí)行器
優(yōu)化完 SQL 語(yǔ)句后便開(kāi)始執(zhí)行,先判斷一下是否有該表的查詢權(quán)限恐仑,沒(méi)有則返回沒(méi)有權(quán)限的錯(cuò)誤泉坐。(因?yàn)榭赡苓€有觸發(fā)器這種需要在執(zhí)行器階段才能確定的情況,預(yù)處理器的 precheck 是不能對(duì)這種運(yùn)行時(shí)才涉及到的表進(jìn)行權(quán)限校驗(yàn)的裳仆,所以需要在執(zhí)行時(shí)再進(jìn)行權(quán)限檢查腕让。)
如果有權(quán)限,打開(kāi)表歧斟,然后調(diào)用存儲(chǔ)引擎的接口獲取數(shù)據(jù)返回纯丸。
本文介紹了MySQL的基礎(chǔ)架構(gòu),這也是一條SQL查詢語(yǔ)句執(zhí)行的流程静袖。