MySQL基礎架構


前段時間訂閱了《Mysql實戰45講》(從原理到實戰),新的一年為自己充充電。對於這部分內容,我所知道的只來源於我大學里學習的課程《數據庫原理》,在大學里學習的只是簡單的查詢,增加,刪除,索引,鎖,觸發器,視圖等內容。幾乎沒有基礎的架構知識。因此在這里鞏固一下原理,學習一下數據庫Mysql在原理上究竟是怎樣的。

首先我們一起看一個Mysql的基本架構示意圖:

1、Mysql可以分為Server層和存儲引擎層兩部分。

2、Server層:包含連接器,查詢緩存,分析器,優化器,執行器等,涵蓋了MySQL的大多數核心功能區以及所有的內置函數。

①內置函數:日期,時間,數學和加密函數等;

②所有跨存儲引擎的功能都在這一層實現,例如存儲過程,觸發器,視圖等;

3、存儲引擎層:負責數據的存儲和提取。

①架構模式:插件式的,支持InnoDB(目前也是最常用的,MySQL5.5后默認存儲引擎)、MyISAM、Memory等多個存儲引擎。

②create table 時不指定引擎類型,默認使用的就是InnoDB.如果使用其他類型,可以利用create table語句中使用engine=memory,等來指定使用內存引擎來創建表。不同的存儲引擎數據存儲方式不同,支持的功能也不同。

4、不同的存儲引擎共用一個server層,結合一個例子我們從總體上了解一下各部件的功能。

mysql> select * from T where ID=;

 開啟探索之旅:

一  連接器負責跟客戶端建立連接,獲取權限,維持和管理連接,這個連接器位於server層,我們通過連接器可以讓客戶端和server層連接起來,從而登錄到mysql。連接命名一般就這樣搞:

mysql -h$ip -P$port -u$user -p

  在命令輸完之后,我們需要在交互界面輸入密碼。雖然在-p后面我們可以把密碼直接寫上,但是安全性你懂得,如果我們連接的是生產服務器,強烈建議不要這樣干!

連接過程是怎樣的呢?

  鏈接命令中的mysql是客戶端工具,用來跟服務器建立連接。(本菜鳥一直使用圖形話的工具很少思考這方面的內容,實在是羞愧啊!只看到了表面,很少思考內在的東西!)

經典的TCP握手之后,服務器開始驗證身份。我們這個時候要用到輸入的用戶名和密碼:

①如果用戶名和密碼不對,我們會被反饋“Access denied for user”的錯誤,然后就死翹翹了,(客戶端程序結束執行)。

②如果用戶名和密碼正確,連接器回到權限表里找到我們所擁有的權限。之后在這個連接面里的權限判斷邏輯,都會依賴於此時讀到的權限。(如果一個用戶成功建立連接后,即使你用管理員賬號對這個用戶的權限做了修改,也不會影響到已經存在連接的權限,修改完成后,只有在新建的連接才會使用新的權限設置);

連接完成之后呢?

  ①如果你沒有后續的動作,這個連接就會空閑下來,使用show processlist 命令中看到它。

  ②客戶端如果太長時間沒用動靜,連接器就會自動斷開,這個時間是由wait_timeout控制的,默認是8小時。

  如果在連接被斷開之后,客戶端再次發送請求的話,就會收到一個錯誤的提醒:Lost connection to Mysql server during query。這時候如果要繼續,就需要重連,然后在執行請求了。

長連接和短連接:

  長連接:在數據庫里面,長連接是指連接成功之后,如果客戶端持續有請求,則一直使用同一個連接。

  短連接:指的是每次執行完很少的幾次查詢就斷開連接,下次查詢再重新建立一個。

  建立連接的過程很復雜,建議在使用中盡量減少建立連接的動作,也就是盡量使用長連接;

問題:但是如果全部使用長連接的話,你可能會發現Mysql占用內存特別快。

  原因:MySQL在執行的時候臨時使用的內存是管理在連接對象里面的,這些資源會在連接斷開的時候釋放,所以如果長連接列累積下來,可能會導致內存占用太大,被系統強制殺死(OOM):

  表現:MySQL異常重啟。

  解決方案①定期斷開長連接,使用一段時間,或者程序里面判斷執行過一個占內存的大查詢后,斷開連接,之后要查詢再重連。

  ②如果使用的是MySQL 5.7或者更高的版本,可以在每次執行一個比較大的操作后,通過執行mysql_reset_connection來重新初始化鏈接資源。這個過程不需要重連和重新做權限驗證,但是會將連接恢復到剛剛創建完時的狀態。

二  查詢緩存

查詢緩存:在連接建立完成之后,我們就可以執行select語句了。具體過程是怎樣的呢?

  MySQL拿到一個查詢請求之后,會到查詢緩存看看,之前是否執行過這樣的一條語句,之前執行過的語句及其結果可能會以key-value對的形式,被直接存放到內存中,key是查詢的語句,value是查詢的結果,如果你的查詢能夠直接在這個緩存中找到key,那么這個value就會被直接返回給客戶端。如果語句不在查詢緩存中,就會繼續后面的執行階段,執行完成后,執行結果會被存入到查詢緩存中。你可以看到,如果查詢命中緩存,MySQL不需要執行后面的復雜操作,就可以直接返回結果,這個效率會很高。

但是大多數情況下我會建議你不要使用查詢緩存,為什么呢?

  因為查詢緩存往往弊大於利。 查詢緩存的失效非常頻繁,只要有對一個表的更新,這個表上所有的查詢緩存都會被清空。因此很可能你費勁地把結果存起來,還沒使用呢,就被一個更新全清空了。對於更新壓力大的數據庫來說,查詢緩存的命中率會非常低。除非你的業務就是有一張靜態表,很長時間才會更新一次。比如,一個系統配置表,那這張表上的查詢才適合使用查詢緩存。

好在MySQL也提供了這種“按需使用”的方式。你可以將參數query_cache_type設置成DEMAND,這樣對於默認的SQL語句都不使用查詢緩存。而對於你確定要使用查詢緩存的語句,可以用SQL_CACHE顯式指定,像下面這個語句一樣:

 mysql> select SQL_CACHE * from T where ID=10

需要注意的是,MySQL 8.0版本直接將查詢緩存的整塊功能刪掉了,也就是說8.0開始徹底沒有這個功能了。 

三 分析器(關於詞法分析和語法分析的內容可以看《編譯原理》)

如果沒有命中緩存,就要開始真正的執行SQL語句了,首先SQL語句需要知道你在做什么,因此需要對SQL語句做出解析。

分析器做的工作如下:

mysql> select * from T where ID=;(以這個語句為例子)

詞法分析:你輸入的是由多個字符串和空格組成的一條SQL語句,Mysql需要識別出里面的字符分別是什么,代表什么。MySQL從你輸入的“select”這個關鍵子識別出來,這是一條查詢語句,他也要把字符串“T”識別成“表名T”,把字符串“ID”識別成“列ID”。

語法分析:根據詞法分析的結果,語法分析器會根據語法規則,判斷你輸入的這個SQL語句是否滿足MySQL語法。如果你輸入的語句不對,你會收到“you have an error in your SQL syntax”的錯誤提醒,比如下面這個語句select少打了一個字母"S".

mysql> elect * from t where ID=1; ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'elect * from t where ID=1' at line 1

一般來說語法錯誤會提示第一個出現錯誤的位置,所以你要關注的是緊接“use near”的內容。

四 優化器

經過了分析器,MySQL就知道你要做什么了,在開始執行之前,還要經過優化器的處理。

  1.   優化器是在表里面有多個索引的時候,決定使用哪個索引;
  2.   在一個語句有多表關聯(join)的時候,決定各個表的連接順序。

比如你執行下面這樣的語句,這個語句是執行兩個表的join:

mysql> select * from t1 join t2 using(ID) where t1.c=10 and t2.d=20;
  • 既可以先從表t1里面取出c=10的記錄的ID值,再根據ID值關聯到表t2,再判斷t2里面d的值是否等於20。
  • 也可以先從表t2里面取出d=20的記錄的ID值,再根據ID值關聯到t1,再判斷t1里面c的值是否等於10。

 

這兩種執行方法的邏輯結果是一樣的,但是執行的效率會有不同,而優化器的作用就是決定選擇使用哪 一個方案。 優化器階段完成后,這個語句的執行方案就確定下來了,然后進入執行器階段.

五 執行器

MySQL通過分析器知道你要干啥了,通過優化器知道該怎么去干了。於是就進入到執行器階段了,開始干活!

其中的流程如下:

  1. 判斷你是否對這個表T有沒有查詢的權限,如果沒有,就會返回沒有權限的錯誤,如下所示:
    mysql> select * from T where ID=10;ERROR 1142 (42000): SELECT command denied to user 'b'@'localhos' for table 'T'
  2.  判斷如果你有權限,那就打開表繼續執行。打開表的時候,執行器就會根據表的引擎定義,去使用這個引擎提供的接口。例如我們的這個例子中的表T中,ID字段沒有索引,那么執行器的執行流程是這樣的:

    1、調用InnoDB引擎接口取這個表的第一行,判斷ID的值是不是10,如果不是則跳過,如果是則將這行存在結果集中;

    2、調用引擎接口取“下一行”,重復相同的判斷邏輯,直到取到這個表的最后一行;

    3、執行器將上述遍歷過程中所有滿足條件的行組成記錄集作為結果集返回給客戶端。

至此這個語句結束了。

對於有索引的表,執行的邏輯也差不多,第一次調用的是“取滿足條件的第一行”這個接口,之后循環取“滿足條件的下一行”這個接口,這寫接口都是引擎中定義好的。

你會在數據庫的慢查詢日志中看到看一個rows_examined字段,表示這個語句執行過程中掃描了多少行,這個值就是在執行器每次調用引擎獲取數據行的時候累加的。

在有些場景下,執行器調用一次,在引擎內部則掃描了很多行,因此引擎掃描行數跟rows_examined並不是完全相同的。

邏輯架構大體就是這樣子了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM