一、MySQL邏輯架構
1.1 結構分析
整體來說,MySql的邏輯架構分成三個部分。
- 1)客戶端:主要是建立連接的過程,交互的過程
- 2)核心服務
- 3)存儲引擎
這個可能比較抽象,我們結合MySql的查詢過程,結合着進行學習。
1.2 結構圖
二、MySql查詢過程
2.1 流程圖
這個圖其實就是在第一個的基礎上,進行的更加細致的划分,因為上面只是大致畫出了邏輯架構,但是這個就展示了一整個過程。
2.2 過程分析
2.2.1 小結
- 1)客戶端向服務端發起一條請求
- 2)服務端先檢查查詢緩存,如果命中緩存,則直接返回結果,否則交給下一階段
- 3)服務器進行SQL解析,預處理,在經過查詢優化形成對應的執行計划
- 4)mysql根據執行計划,調用API給存儲引擎,進行數據的讀取和存儲
- 5)將結果返回給客戶端,並緩存查詢結果
大致整體的步驟就是這樣的,我們要把每一步都盡量的深入思考下,我可能考慮的有欠缺,歡迎大家進行留言。
2.2.2 mysql客戶端和服務器通訊
2.2.2.1 半雙工通信機制
mysql客戶端和服務器之間的通訊協議是“半雙工”的,這意味着,在任何一個時刻,要么由服務器向客戶端發送數據,要么由客戶端向服務器發送數據,這兩個動作不能同時發生。
2.2.2.2 半雙工通信機制優點和缺點
這種協議讓mysql通信簡單快速,但也限制了mysql。一個明顯的限制是,這意味着沒辦法進行流量限制。一旦一端開始發生消息,另一端要接收完整個消息才能響應他。
2.2.2.3 執行分析
客戶端用一個單獨的數據包將查詢傳給服務器。一旦客戶端發送了請求,它能做的事情就只是等待結果了。一般服務器響應給用戶的數據通常很多,由多個數據包組成。當服務器開始響應客戶端請求時,客戶端必須完整的接受整個返回結果,而不是簡單的只收取前面幾條結果,然后讓服務器停止發送數據。所以,使用limit子句去控制服務器發送給客戶端數據的量。這樣可以提高性能。
如果沒有limit進行限制的話,查詢出所有的數據都會發送給客戶端,比如我只需要10條。但是沒有限制,假如取出了100條數據.就會發送到客戶端100條數據。這其中其實有些是不需要的。在這個過程中,客戶端是無法說:我已經找到我需要的10條數據,剩下的90條數據服務器請不要再發送了。由於是半雙工的通信機制,那么你要做的只能等待服務器發送的100條數據全部發送完畢,你才能進行操作。所以沒有limit進行限制后,是不是增加了客戶端的等待時間。對性能有影響。根據這種半雙工的機制,一般這樣做:客戶端需要多少條數據,我就在服務器操作的時候使用limit進行限制只取出多少條,那么只會發送需要的條數給客戶端。
而且,多數連接mysql的庫函數都可以獲得全部結果並緩存到內存里,還可以逐行獲取所需要的數據。默認一般是獲得全部結果並緩存到內存中。mysql通常需要等所有的數據都已經發送給客戶端才能釋放這條查詢所占用的資源,所以接受全部結果並緩存通常可以減少服務器的壓力,讓查詢能夠早點結束、早點釋放對應的資源。
2.2.3 查詢狀態
對於mysql連接,任何時刻都有一個狀態,該狀態表示了mysql當前正在做什么。使用show full processlist命令查看當前狀態。在一個查詢生命周期中,狀態會變化很多次,下面是這些狀態的解釋:
- sleep:線程正在等待客戶端發送新的請求;
- query:線程正在執行查詢或者正在將結果發送給客戶端;
- locked:在mysql服務器層,該線程正在等待表鎖。在存儲引擎級別實現的鎖,例如InnoDB的行鎖,並不會體現在線程狀態中。對於MyISAM來說這是一個比較典型的狀態。
- analyzing and statistics:線程正在收集存儲引擎的統計信息,並生成查詢的執行計划;
- copying to tmp table:線程在執行查詢,並且將其結果集復制到一個臨時表中,這種狀態一般要么是做group by操作,要么是文件排序操作,或者union操作。如果這個狀態后面還有on disk標記,那表示mysql正在將一個內存臨時表放到磁盤上。
- sorting Result:線程正在對結果集進行排序。
- sending data:線程可能在多個狀態間傳送數據,或者在生成結果集,或者在向客戶端返回數據。
2.2.4 查詢緩存
在解析一個查詢語句之前,如果查詢緩存是打開的,那么mysql會優先檢查這個查詢是否命中查詢緩存中的數據。這個檢查是通過一個對大小寫敏感的哈希查找實現的。查詢和緩存中的查詢即使只有一個字節不同,那也不會匹配緩存結果,這種情況下查詢就會進入下一階段的處理。
如果當前的查詢恰好命中了查詢緩存,那么在返回查詢結果之前mysql會檢查一次用戶權限。這仍然是無須解析查詢SQL語句的,因為在查詢緩存中已經存放了當前查詢需要訪問的表信息。如果權限沒有問題,mysql會跳過所有其他階段,直接從緩存中拿到結果並返回給客戶端。這種情況下,查詢不會被解析,不用生成執行計划,不會被執行。
2.2.5 查詢優化處理
查詢的生命周期的下一步是將一個SQL轉換成一個執行計划,mysql再依照這個執行計划和存儲引擎進行交互。這包含多個子階段:解析SQL、預處理、優化SQL執行計划。這個過程中任何錯誤都可能終止查詢。
- 語法解析器和預處理:首先mysql通過關鍵字將SQL語句進行解析,並生成一顆對應的“解析樹”。mysql解析器將使用mysql語法規則驗證和解析查詢;預處理器則根據一些mysql規則進一步檢查解析數是否合法。
- 查詢優化器:當語法樹被認為是合法的了,並且由優化器將其轉化成執行計划。一條查詢可以有很多種執行方式,最后都返回相同的結果。優化器的作用就是找到這其中最好的執行計划。
- 執行計划:mysql不會生成查詢字節碼來執行查詢,mysql生成查詢的一棵指令樹,然后通過存儲引擎執行完成這棵指令樹並返回結果。最終的執行計划包含了重構查詢的全部信息。
2.2.6 查詢執行引擎
在解析和優化階段,mysql將生成查詢對應的執行計划,mysql的查詢執行引擎則根據這個執行計划來完成整個查詢。這里執行計划是一個數據結構,而不是和很多其他的關系型數據庫那樣對應的字節碼。
mysql簡單的根據執行計划給出的指令逐步執行。在根據執行計划逐步執行的過程中,有大量的操作需要通過調用存儲引擎實現的接口來完成。為了執行查詢,mysql只需要重復執行計划中的各個操作,直到完成所有的數據查詢。
2.2.7 返回結果給客戶端
查詢執行的最后一個階段是將結果返回給客戶端。即使查詢不需要返回結果給客戶端,mysql仍然會返回這個查詢的一些信息,如該查詢影響到的行數。如果查詢可以被緩存,那么mysql在這個階段也會將結果放到查詢緩存中。
mysql將結果集返回客戶端是一個增量、逐步返回的過程。這樣有兩個好處:服務器端無須存儲太多的結果,也就不會因為返回太多結果而消耗太多的內存;這樣處理也讓msyql客戶端第一時間獲得返回的結果。
結果集中的每一行都會以一個滿足mysql客戶端/服務器通信協議的包發送,再通過tcp協議進行傳輸,在tcp傳輸的過程中,可能對mysql的封包進行緩存然后批量傳輸。
三、MySql連接方式
3.1 小結
mysql是應用,我們需要實現的是mysql客戶端與服務端進行通信,這里好比http,所以在客戶端找到服務端之前,就需要他們所處的物理機先建立起連接,就如同http建立連接之前,需要tcp先建立連接。Mysql的主要連接方式包括:Unix套接字,內存共享,命名管道,TCP/IP套接字等。
3.2 Unix套接字
在計算機世界,一個socket是一種內部進程通信形式,它被用於在相同主機上形成進程間的雙向通信連接點(在本地系統上的一個物理文件)。所以在Linux和Unix環境下,可以使用Unix套接字進行Mysql服務器的連接;Unix套接字其實不是一個網絡協議,只能在客戶端和Mysql服務器在同一台電腦上才可以使用。
3.3 命名管道和內存共享
在window系統中客戶端和Mysql服務器在同一台電腦上,可以使用命名管道和共享內存的方式,
- 命名管道開啟:–shared-memory=on/off;
- 共享內存開啟:–enable-named-pipe=on/off;
3.4 命名管道和內存共享
任何系統下都可以使用的方式,也是使用的最多的方式,其實熟悉操作系統的朋友應該能體會出來,像前兩種,因為客戶端和服務端在同一台主機上,也就是一台主機的兩個應用,所以這也就是進程間通信的方式,而在不同的主機上就不一樣了,就需要網絡,tcp/ip建立了。
四、MySql客戶端與服務端的交互過程
4.1 小結
主要分為兩部分:握手認證階段,命令執行階段
注意哦,這個握手和上面過的握手不一樣哦。
4.2 握手認證階段
握手認證階段為客戶端與服務器建立(TCP)連接后進行,交互過程如下:
- 服務器 -> 客戶端:握手初始化消息
- 客戶端 -> 服務器:登陸認證消息
- 服務器 -> 客戶端:認證結果消息
4.3 命令執行階段
客戶端認證成功后,會進入命令執行階段,交互過程如下:
- 客戶端 -> 服務器:執行命令消息
- 服務器 -> 客戶端:命令執行結果
4.4 流程圖
4.5 連接過程分析
4.5.1 為什么還要進行三次握手認證
因為tcp三次握手,只是將客戶端與服務端建立起了連接,然后通過端口知道我要訪問的是mysql這個服務,但是mysql它不同於http,只要你知道url就能得到一個響應,mysql必須登陸后你才能進行操作。所以這個過程最重要的就是驗證客戶端的登陸權限。
4.5.2 為什么是服務端主動給客戶端發送認證呢?
4.5.2.1 服務端主動給客戶端發送認證與請求響應的區別
http不是說只有客戶端主動與服務端進行請求,服務端不是不能在沒有請求的情況下主動進行響應嗎?
首先,大家也不要陷入誤區,在進行認證的這個交互中,實際上客戶端與服務端還沒有進行任何業務上的往來,只是進行一個認證,所以與上面說的http的不同,如果細心的話你會發現,認證成功后,在命令執行階段,mysql這種通信方式是與http非常類似的,是一種半雙工的通信機制,在沒有請求的情況下,服務端的mysql也不會主動給你發送任何數據,所以這里不要混淆。
再說為什么服務端先發送,那肯定是因為他有不得不發送的道理,所以我們就需要理解一下,他發送的是什么東西。
4.5.2.2 MySQL報文
4.5.2.2.1 MySQL報文分類
主要分成三個部分:登錄認證報文,客戶端請求報文以及服務器端返回報文。
4.5.2.2.2 登陸認證報文(服務端->客戶端)
- 協議版本號:服務端所使用的mysql協議的版本號
- 服務器線程ID:服務端為此客戶端所創建的線程的ID
- 挑戰隨機數:MySQL數據庫用戶認證采用的是挑戰/應答的方式,服務器生成該挑戰數並發送給客戶端,由客戶端進行處理並返回相應結果,然后服務器檢查是否與預期的結果相同,從而完成用戶認證的過程。
- **服務器權能標志:**用於與客戶端協商通訊方式
大致就是告訴客戶端服務端能接受的通訊方式是什么樣的。
4.5.2.2.3 登陸認證報文(客戶端 -> 服務器)
- 客戶端權能標志:客戶端收到服務器發來的初始化報文后,會對服務器發送的權能標志進行修改,保留自身所支持的功能,然后將權能標返回給服務器,從而保證服務器與客戶端通訊的兼容性。
- 消息長度:客戶端發送請求時所支持的最大消息長度值
- 字符編碼:表示通訊過程中使用的字符編碼,與服務器在認證報文中發送的相同
- 用戶名:客戶端登陸的用戶名
- 挑戰認證數據:客戶端用戶密碼使用服務器發送的挑戰隨機數進行加密后,生成挑戰認證數據,返回給服務器用於服務端的認證
4.5.2.2.4 服務端認證結果報文(服務端->客戶端)
服務端主要驗證,用戶名,密碼是否正確存在,如果都是正確的,就返回ok報文,錯誤的話就是ERROR報文。
4.5.3 wireshark抓包驗證實現
http://blog.itpub.net/28944233/viewspace-1665982/
五、參考文章
https://blog.csdn.net/gao_yu_long/article/details/74905490
https://www.cnblogs.com/kingle-study/p/9961748.html
https://blog.csdn.net/LYue123/article/details/89256577
https://blog.csdn.net/LYue123/article/details/89256577