MySQL 在整體架構(gòu)上分為 Server 層和存儲(chǔ)引擎層。其中 Server 層,包括連接器、查詢緩存、分析器、優(yōu)化器、執(zhí)行器等,存儲(chǔ)過(guò)程、觸發(fā)器、視圖和內(nèi)置函數(shù)都在這層實(shí)現(xiàn)。數(shù)據(jù)引擎層負(fù)責(zé)數(shù)據(jù)的存儲(chǔ)和提取,如 InnoDB、MyISAM、Memory 等引擎。在客戶端連接到 Server 層后,Server 會(huì)調(diào)用數(shù)據(jù)引擎提供的接口,進(jìn)行數(shù)據(jù)的變更。
連接器
負(fù)責(zé)和客戶端建立連接,獲取用戶權(quán)限以及維持和管理連接。
通過(guò)show processlist來(lái)查詢連接的狀態(tài)。在用戶建立連接后,即使管理員改變連接用戶的權(quán)限,也不會(huì)影響到已連接的用戶。默認(rèn)連接時(shí)長(zhǎng)為 8 小時(shí),超過(guò)時(shí)間后將會(huì)被斷開(kāi)。
簡(jiǎn)單說(shuō)下長(zhǎng)連接:
1. 優(yōu)勢(shì):在連接時(shí)間內(nèi),客戶端一直使用同一連接,避免多次連接的資源消耗。
2. 劣勢(shì):在MySQL執(zhí)行時(shí),使用的內(nèi)存被連接對(duì)象管理,由于長(zhǎng)時(shí)間沒(méi)有被釋放,會(huì)導(dǎo)致系統(tǒng)內(nèi)存溢出,被系統(tǒng)kill. 所以需要定期斷開(kāi)長(zhǎng)連接,或執(zhí)行大查詢后,斷開(kāi)連接。MySQL 5.7 后,可以通過(guò)mysql_rest_connection初始化連接資源,不需要重連或者做權(quán)限驗(yàn)證。
查詢緩存
當(dāng)接受到查詢請(qǐng)求時(shí),會(huì)現(xiàn)在查詢緩存中查詢(key/value保存),是否執(zhí)行過(guò)。沒(méi)有的話,再走正常的執(zhí)行流程。
但在實(shí)際情況下,查詢緩存一般沒(méi)有必要設(shè)置。因?yàn)樵诓樵兩婕暗降谋肀桓聲r(shí),緩存就會(huì)被清空。所以適用于靜態(tài)表。在MySQL8.0后,查詢緩存被廢除。
分析器
1. 詞法分析:如識(shí)別select,表名,列名,判斷其是否存在等。
2. 語(yǔ)法分析:判斷語(yǔ)句是否符合MySQL語(yǔ)法。
優(yōu)化器
確定索引的使用,join表的連接順序等,選擇最優(yōu)化的方案。
執(zhí)行器
在具體執(zhí)行語(yǔ)句前,會(huì)先進(jìn)行權(quán)限的檢查,通過(guò)后使用數(shù)據(jù)引擎提供的接口,進(jìn)行查詢。如果設(shè)置了慢查詢,會(huì)在對(duì)應(yīng)日志中看到rows_examined來(lái)表示掃描的行數(shù)。在一些場(chǎng)景下(索引),執(zhí)行器調(diào)用一次,但在數(shù)據(jù)引擎中掃描了多行,所以引擎掃描的行數(shù)和rows_examined并不完全相同。
不預(yù)先檢查權(quán)限的原因:如像觸發(fā)器等情況,需要在執(zhí)行器階段才能確定權(quán)限,在優(yōu)化器階段無(wú)法驗(yàn)證。
MySQL 日志模塊
如前面所說(shuō),MySQL整體分為Server層和數(shù)據(jù)引擎層,而每層也對(duì)應(yīng)了自己的日志文件。如果選用的是InnoDB引擎,對(duì)應(yīng)的是redo log文件。Server層則對(duì)應(yīng)了binlog文件。至于為什么存在了兩種日志系統(tǒng),咱們往下看。
1. redo log
redo log是InnoDB特有日志,為什么要引入redo log呢,想象這樣一個(gè)場(chǎng)景,MySQL為了保證持久性是需要把數(shù)據(jù)寫(xiě)入磁盤(pán)文件的。我們知道,在寫(xiě)入磁盤(pán)時(shí),會(huì)進(jìn)行文件的 IO,查找操作,如果每次更新操作都這樣的話,整體的效率就會(huì)特別低,根本沒(méi)法使用。
既然直接寫(xiě)入磁盤(pán)不行,解決方法就是先寫(xiě)進(jìn)內(nèi)存,在系統(tǒng)空閑時(shí)再更新到磁盤(pán)就可以了。但光更新內(nèi)存不行,假如系統(tǒng)出現(xiàn)異常宕機(jī)和重啟,內(nèi)存中沒(méi)有被寫(xiě)入磁盤(pán)的數(shù)據(jù)就會(huì)被丟掉,數(shù)據(jù)的一致性就出現(xiàn)問(wèn)題了。
這時(shí)redo log就發(fā)揮了作用,在更新操作發(fā)生時(shí),InnoDb會(huì)先寫(xiě)入redo log日志(記錄了數(shù)據(jù)發(fā)生了怎么樣的改變),然后更新內(nèi)存,最后在適當(dāng)?shù)臅r(shí)間再寫(xiě)入磁盤(pán)。先寫(xiě)日志,在寫(xiě)磁盤(pán)的操作,就是常說(shuō)到的WAL(Write-Ahead- Logging)技術(shù)。
redo log的出現(xiàn),除了在效率上有了很大的改善,還保證了MySQL具有了crash-safe的能力,在發(fā)生異常情況下,不會(huì)丟失數(shù)據(jù)。
在具體實(shí)現(xiàn)上redo log的大小是固定的,可配置一組為 4 個(gè)文件,每個(gè)文件1GB,更新時(shí)對(duì)四個(gè)文件進(jìn)行循環(huán)寫(xiě)入。
write pos記錄當(dāng)前寫(xiě)入的位置,寫(xiě)完就后移,當(dāng)?shù)趯?xiě)入第4個(gè)文件的末尾時(shí),從第0號(hào)位置重新寫(xiě)入。
check point表示當(dāng)前可以擦除的位置,當(dāng)數(shù)據(jù)更新到磁盤(pán)時(shí),check point就向后移動(dòng)。
write pos和check point之間的位置,就是可以記錄更新操作的空間。當(dāng)write pos追上check point ,不在能執(zhí)行新的操作,先讓check point去寫(xiě)入一些數(shù)據(jù)。
可以將innodb_flush_log_at_trx_commit設(shè)置成1,開(kāi)啟redo log持久化的能力。
2. binlog
binlog則是Server層的日志,主要用于歸檔,在備份,主備同步,恢復(fù)數(shù)據(jù)時(shí)發(fā)揮作用,常見(jiàn)的日志格式有row, mixed, statement三種。
可以通過(guò)sync_binlog=1開(kāi)啟binlog寫(xiě)入磁盤(pán)。
這里對(duì)binlog和 redo進(jìn)行下區(qū)分:
-
所有者不同:binlog是 Server層,所有引擎都可使用。redo log是 InnoDB特有的。
-
類(lèi)型不同:binlog是邏輯日志,記錄的是語(yǔ)句的原始邏輯(比 statement)。redo log是物理日志,記錄某個(gè)數(shù)據(jù)頁(yè)被做了怎樣的修改。
-
數(shù)據(jù)寫(xiě)入的方式不同:binog日志會(huì)一直追加,而redo log是循環(huán)寫(xiě)入。
-
功能不同:binlog用于歸檔,而redo log用于保證crash-safe。
3. 兩階段提交
一條更新語(yǔ)句,在InnoDB引擎下的更新過(guò)程如下。在更新內(nèi)存后,將寫(xiě)入redolog和寫(xiě)入 binlog放在一起成為一個(gè)事務(wù)最后一起寫(xiě)入redo log和 binlog的過(guò)程就是常說(shuō)的兩階段提交。用于保證當(dāng)有意外情況發(fā)生時(shí),數(shù)據(jù)的一致性。
這里假設(shè)下,如果不采用兩階段提交會(huì)發(fā)生什么?
-
先寫(xiě)redo log后寫(xiě)binlog假設(shè)在寫(xiě)入redo log后,MySQL發(fā)生異常重啟,此時(shí)binlog沒(méi)有寫(xiě)入。在重啟后,由于redolog已經(jīng)寫(xiě)入,此時(shí)數(shù)據(jù)庫(kù)的內(nèi)容是沒(méi)有問(wèn)題的。但此時(shí),如果想要拿binlog進(jìn)行備份或恢復(fù),發(fā)現(xiàn)會(huì)少了最后一條的更新邏輯,導(dǎo)致數(shù)據(jù)不一致。
-
先寫(xiě)binlog和redo log. binlog寫(xiě)入后,MySQL異常重啟,redo log沒(méi)有寫(xiě)入。此時(shí)重啟后,發(fā)現(xiàn)redo log沒(méi)有成功寫(xiě)入,認(rèn)為這個(gè)事務(wù)無(wú)效,而此時(shí)binlog卻多了一條更新語(yǔ)句,拿去恢復(fù)后自然數(shù)據(jù)也是不一致的。
再分析下兩階段提交的過(guò)程:
-
在寫(xiě)redo log prepare階段奔潰,重啟后,發(fā)現(xiàn)redo log沒(méi)寫(xiě)入,回滾此次事務(wù)。
-
如果在寫(xiě)binlog時(shí)奔潰,重啟后,發(fā)現(xiàn)binlog未被寫(xiě)入,回滾操作
-
如果在寫(xiě)入redo log和binlog后崩潰,重啟后,發(fā)現(xiàn)沒(méi)提交,則進(jìn)行commit。
總結(jié)
在文章開(kāi)始部分,說(shuō)明了MySQL的整體架構(gòu)分為Server層和引擎層,并簡(jiǎn)要說(shuō)明了一條語(yǔ)句的執(zhí)行過(guò)程。接著MySQL在5.5后選用InnoDB作為默認(rèn)的引擎,就是因?yàn)楸仍腗yISAM多了事務(wù)以及crash-safe的能力。
而crash-safe就是由redo log實(shí)現(xiàn)的。與redo log類(lèi)似的日志文件還有binlog,是Server引擎的日志,用于歸檔和備份數(shù)據(jù)。
最后提到了,為了保證數(shù)據(jù)的一致性,將redo log和binlog放入相同的事務(wù)中,也就是常提到的兩階段提交操作。
End.
作者:以終為始
來(lái)源:博客園
本文為轉(zhuǎn)載分享,如侵權(quán)請(qǐng)聯(lián)系后臺(tái)刪除