MySQL-基礎(chǔ)架構(gòu)

Server 層:包括連接器、查詢緩存、分析器、優(yōu)化器、執(zhí)行器等,涵蓋 MySQL 的大多數(shù)核心服務(wù)功能,以及所有的內(nèi)置函數(shù)(如日期、時間、數(shù)學(xué)和加密函數(shù)等),所有跨存儲引擎的功能都在這一層實現(xiàn),比如存儲過程、觸發(fā)器、視圖等。
存儲引擎層:負(fù)責(zé)數(shù)據(jù)的存儲和提取。其架構(gòu)模式是插件式的,支持 InnoDB、MyISAM、Memory 等多個存儲引擎。現(xiàn)在最常用的存儲引擎是 InnoDB,它從 MySQL 5.5.5 版本開始成為了默認(rèn)存儲引擎。
通過一條sql查詢語句,來看看Server層各部分都有些什么作用

1. 連接器
連接器主要負(fù)責(zé)跟客戶端建立連接、獲取權(quán)限、維持和管理連接
與服務(wù)端建立連接,然后開始驗證身份,如果用戶名與密碼認(rèn)證通過,則會查詢出用戶權(quán)限
此時用戶權(quán)限發(fā)生了變更,也不會影響到此時已存在連接的權(quán)限
數(shù)據(jù)庫的連接
在數(shù)據(jù)庫里,連接分為兩種
長連接:連接成功后,如果客戶端持續(xù)有請求,則一直使用同一個連接
短連接:每次執(zhí)行完很少的幾次查詢就斷開連接,下次查詢再重新建立一個
建立連接的過程通常是比較復(fù)雜的,所以在使用中要盡量減少建立連接的動作,也就是盡量使用長連接。
但是全部使用長連接后,有些時候 MySQL 占用內(nèi)存漲得特別快,這是因為 MySQL ?在執(zhí)行過程中臨時使用的內(nèi)存是管理在連接對象里面的。這些資源會在連接斷開的時候才釋放。所以如果長連接累積下來,可能導(dǎo)致內(nèi)存占用太大, MySQL 異常重啟。
如何解決?
定期斷開長連接。使用一段時間,或者程序里面判斷執(zhí)行過一個占用內(nèi)存的大查詢后,斷開連接,之后要查詢再重連。

2. 查詢緩存
連接建立完成后,執(zhí)行邏輯就會來到第二步:查詢緩存。
如果你的查詢能夠直接在這個緩存中找到 key,那么這個 value ?就會被直接返回給客戶端。
但是,緩存有著以下的規(guī)則
sessions共享,一個client查詢的緩存結(jié)果,另一個client也可以使用;
SQL必須完全一致才會導(dǎo)致cache命中;
不確定的函數(shù)將永遠(yuǎn)不會被cache, 比如current_date, now等;
太大的result set不會被cache (< query_cache_limit);
MySQL緩存在分庫分表環(huán)境下是不起作用的;
執(zhí)行SQL里有觸發(fā)器,自定義函數(shù)時,MySQL緩存也是不起作用的;
優(yōu)點:
Query Cache的查詢,發(fā)生在MySQL接收到客戶端的查詢請求、查詢權(quán)限驗證之后和查詢SQL解析之前。
也就是說,當(dāng)MySQL接收到客戶端的查詢SQL之后,僅僅只需要對其進(jìn)行相應(yīng)的權(quán)限驗證之后,就會通過Query Cache來查找結(jié)果,甚至都不需要經(jīng)過Optimizer模塊進(jìn)行執(zhí)行計劃的分析優(yōu)化,更不需要發(fā)生任何存儲引擎的交互。
由于Query Cache是基于內(nèi)存的,直接從內(nèi)存中返回相應(yīng)的查詢結(jié)果,因此減少了大量的磁盤I/O和CPU計算,導(dǎo)致效率非常高。
缺點:
Query Cache的失效問題。如果表的變更比較頻繁,則會造成Query Cache的失效率非常高。表的變更不僅僅指表中的數(shù)據(jù)發(fā)生變化,還包括表結(jié)構(gòu)或者索引的任何變化。
查詢語句不同,但查詢結(jié)果相同的查詢都會被緩存,這樣便會造成內(nèi)存資源的過度消耗。查詢語句的字符大小寫、空格或者注釋的不同,Query Cache都會認(rèn)為是不同的查詢(因為他們的hash值會不同)。
相關(guān)系統(tǒng)變量設(shè)置不合理會造成大量的內(nèi)存碎片,這樣便會導(dǎo)致Query Cache頻繁清理內(nèi)存。
對性能的影響
讀查詢開始之前必須檢查是否命中緩存。如果讀查詢可以緩存,那么執(zhí)行完查詢操作后,會查詢結(jié)果和查詢語句寫入緩存。當(dāng)向某個表寫入數(shù)據(jù)的時候,必須將這個表所有的緩存設(shè)置為失效,如果緩存空間很大,則消耗也會很大,可能使系統(tǒng)僵死一段時間,因為這個操作是靠全局鎖操作來保護(hù)的。
對InnoDB表,當(dāng)修改一個表時,設(shè)置了緩存失效,但是多版本特性會暫時將這修改對其他事務(wù)屏蔽,在這個事務(wù)提交之前,所有查詢都無法使用緩存,直到這個事務(wù)被提交,所以長時間的事務(wù),會大大降低查詢緩存的命中。

3. 分析器
如果沒有命中查詢緩存,就要開始真正執(zhí)行語句了。
首先,MySQL 需要知道你要做什么,因此需要對 SQL 語句做解析
分析器先會做“詞法分析”。你輸入的是由多個字符串和空格組成的一條 SQL 語句,MySQL 需要識別出里面的字符串分別是什么,代表什么。
MySQL ?從你輸入的"select"這個關(guān)鍵字識別出來,這是一個查詢語句。它也要把字符串“T”識別成“表名 T”,把字符串“ID”識別成“列 ?ID”。
做完了這些識別以后,就要做“語法分析”。根據(jù)詞法分析的結(jié)果,語法分析器會根據(jù)語法規(guī)則,判斷你輸入的這個 SQL 語句是否滿足 MySQL 語法。如果你的語句不對,就會收到“You have an error in your SQL syntax”的錯誤提醒。
在Oracle中,Oracle會在分析階判斷語句是否正確,表是否存在,列是否存在等,MySQL在設(shè)計上受Oracle影響頗深,同樣在分析器階段進(jìn)行這些判斷。

4. 優(yōu)化器
MySQL 已經(jīng)知道你要做什么了,在開始執(zhí)行之前,還得知道該怎么做
優(yōu)化器是在表里面有多個索引的時候,決定使用哪個索引;或者在一個語句有多表關(guān)聯(lián)(join)的時候,決定各個表的連接順序。
對于這條sql,有兩種執(zhí)行方法:
先從表 t1 里面取出 c=10 的記錄的 ID 值,再根據(jù) ID 值關(guān)聯(lián)到 t2,再判斷 t2 里面 d 的值是否等于 20
先從表 t2 里面取出 d=20 的記錄的 ID 值,再根據(jù) ID 值關(guān)聯(lián)到 t1,再判斷 t1 里面 c 的值是否等于 10
優(yōu)化器的作用就是決定使用哪個方法,因為這倆種方法的執(zhí)行效率其實是不一樣的

5. 執(zhí)行器
分析器告訴了做什么,優(yōu)化器知道了怎么做,接下來就是開始執(zhí)行
執(zhí)行前,會先判斷對該表是否有查詢的權(quán)限
沒有權(quán)限,則
有權(quán)限,則打開表繼續(xù)執(zhí)行,打開對應(yīng)引擎提供的接口
假如id沒有索引,則執(zhí)行流程如下
調(diào)用 InnoDB ?引擎接口取這個表的第一行,判斷 ID 值是不是 ?10,如果不是則跳過,如果是則將這行存在結(jié)果集中;
調(diào)用引擎接口取“下一行”,重復(fù)相同的判斷邏輯,直到取到這個表的最后一行。
執(zhí)行器將上述遍歷過程中所有滿足條件的行組成的記錄集作為結(jié)果集返回給客戶端。
如果有索引,則是調(diào)用取“滿足條件的第一行”這個接口,之后循環(huán)取“滿足條件的下一行”這個接口
