Mysql 報文格式


我准備從mysql的實現出發,將mysql好好理解一下,從他的邏輯結構一層一層出發,感受一下,所以再學第一層之前,要先對mysql整體的邏輯結構有一個初步認識

mysql邏輯架構

在這里插入圖片描述
整體來說,MySql的邏輯架構分成三個部分,這個之前我以為提到過
1)客戶端:主要是建立連接的過程,交互的過程
2)核心服務
3)存儲引擎

這個可能比較抽象,我們結合MySql的查詢過程,結合着進行學習

MySql查詢過程

在這里插入圖片描述
這個圖其實就是在第一個的基礎上,進行的更加細致的划分,因為上面只是大致畫出了邏輯架構,但是這個就展示了一整個過程。

我說一下大致的過程:

1)客戶端向服務端發起一條請求
2)服務端先檢查查詢緩存,如果命中緩存,則直接返回結果,否則交給下一階段
3)服務器進行SQL解析,預處理,在經過查詢優化形成對應的執行計划
4)mysql根據執行計划,調用API給存儲引擎,進行數據的讀取和存儲
5)將結果返回給客戶端,並緩存查詢結果

大致整體的步驟就是這樣的,我們要把每一步都盡量的深入思考下,我可能考慮的有欠缺,歡迎大家進行留言

今天先不深入數據庫里面,先將mysql的通信協議搞清楚,也就是執行sql語句之前都干了什么

要了解mysql通信協議,就要知道mysql是通過什么連接的,這個怎么考率的,mysql是應用,我們需要實現的是mysql客戶端與服務端進行通信,這里好比http,所以在客戶端找到服務端之前,就需要他們所處的物理機先建立起連接,就如同http建立連接之前,需要tcp先建立連接。

Mysql的主要連接方式包括:Unix套接字,內存共享,命名管道,TCP/IP套接字等。

有的同學可能問了,怎么這么多方式,那我用的哪一種呢,或者是我應該用哪一種呢,其實他們並不是等價的

Unix套接字:

在Linux和Unix環境下,可以使用Unix套接字進行Mysql服務器的連接;Unix套接字其實不是一個網絡協議,只能在客戶端和Mysql服務器在同一台電腦上才可以使用

命名管道和內存共享

在window系統中客戶端和Mysql服務器在同一台電腦上,可以使用命名管道和共享內存的方式,
命名管道開啟:–shared-memory=on/off;
共享內存開啟:–enable-named-pipe=on/off;

TCP/IP套接字

任何系統下都可以使用的方式,也是使用的最多的方式,我主要介紹的也是這種方式

其實熟悉操作系統的朋友應該能體會出來,像前兩種,因為客戶端和服務端在同一台主機上,也就是一台主機的兩個應用,所以這也就是進程間通信的方式,而在不同的主機上就不一樣了,就需要網絡,tcp/ip建立了。

mysql通信過程

了解了mysql基於的就是tcp的底層協議,所以必然,需要經歷tcp的三次握手,沒錯第一步就是tcp的三次握手(因為這里不是重點,就不詳細說明,不太清楚的同學,可以查看我network的相關博客),建立連接之后就可以發送sql命令了嗎,當然不能,細心的同學會發現,我用客戶端登陸的時候,是需要用戶名,密碼的,這才是真正的mysql客戶端與服務端的交互過程,之前還沒有到應用層,下面就說一下,交互過程

mysql客戶端與服務端的交互過程

主要分為兩部分:握手認證階段,命令執行階段
注意哦,這個握手和上面過的握手不一樣哦

1.1握手認證階段

握手認證階段為客戶端與服務器建立連接后進行,交互過程如下:
服務器 -> 客戶端:握手初始化消息
客戶端 -> 服務器:登陸認證消息
服務器 -> 客戶端:認證結果消息

1.2命令執行階段

客戶端認證成功后,會進入命令執行階段,交互過程如下:
客戶端 -> 服務器:執行命令消息
服務器 -> 客戶端:命令執行結果
在這里插入圖片描述
不知道大家看了有沒有產生一些問題,那我就我的一些問題說一下

為什么還要進行三次握手認證

因為tcp三次握手,只是將客戶端與服務端建立起了連接,然后通過端口知道我要訪問的是mysql這個服務,但是mysql它不同於http,只要你知道url就能得到一個響應,mysql必須登陸后你才能進行操作。所以這個過程最重要的就是驗證客戶端的登陸權限。

為什么是服務端主動給客戶端發送認證呢?

http不是說只有客戶端主動與服務端進行請求,服務端不是不能在沒有請求的情況下主動進行響應嗎?
首先,大家也不要陷入誤區,在進行認證的這個交互中,實際上客戶端與服務端還沒有進行任何業務上的往來,只是進行一個認證,所以與上面說的http的不同,如果細心的話你會發現,認證成功后,在命令執行階段,mysql這種通信方式是與http非常類似的,在沒有請求的情況下,服務端的mysql也不會主動給你發送任何數據,所以這里不要混淆。
再說為什么服務端先發送,那肯定是因為他有不得不發送的道理,所以我們就需要理解一下,他發送的是什么東西。

mysql報文

主要分成三個部分:登錄認證報文,客戶端請求報文以及服務器端返回報,基於mysql5.1.73(mysql4.1以后的版本)

登陸認證報文

1)握手初始化報文(服務端->客戶端)
在這里插入圖片描述
協議版本號:服務端所使用的mysql協議的版本號
服務器線程ID:服務端為此客戶端所創建的線程的ID
挑戰隨機數:MySQL數據庫用戶認證采用的是挑戰/應答的方式,服務器生成該挑戰數並發送給客戶端,由客戶端進行處理並返回相應結果,然后服務器檢查是否與預期的結果相同,從而完成用戶認證的過程。
**服務器權能標志:**用於與客戶端協商通訊方式
這個我稍微解釋下,他這個只要發送的就是,我服務端能接受的通訊方式是什么樣的,我們下面詳細說一下

登陸認證報文(客戶端 -> 服務器)

在這里插入圖片描述
客戶端權能標志客戶端收到服務器發來的初始化報文后,會對服務器發送的權能標志進行修改,保留自身所支持的功能,然后將權能標返回給服務器,從而保證服務器與客戶端通訊的兼容性。
消息長度客戶端發送請求時所支持的最大消息長度值
字符編碼表示通訊過程中使用的字符編碼,與服務器在認證報文中發送的相同
用戶名客戶端登陸的用戶名
挑戰認證數據:客戶端用戶密碼使用服務器發送的挑戰隨機數進行加密后,生成挑戰認證數據,返回給服務器用於服務端的認證

服務端認證結果報文(服務端->客戶端)

這個就比較好理解了,服務端主要驗證,用戶名,密碼是否正確存在,如果都是正確的,就返回ok報文,錯誤的話就是ERROR報文

好了,我相信大家應該都有一定的了解了,那么現在再想一下那個問題是不是覺得是非常有道理的,之所以服務端先發送一個握手過去,就是提前通知一下客戶端,你要遵循的一些協議
舉個例子:
小明找工作,投了一個簡歷給某個公司
這時候,某公司就主動打電話了,告訴他,我們需要筆試,筆試的時間,網址,以及一些別的相關信息,規則等

小明接收到這個消息之后,到了那個時間他就會請求那個網址,並將自己的信息告訴他

這時候公司驗證你的信息,驗證成功后,你就可以開始筆試了

大家可以類比這理解一下,就能體會出,這是非常有必要的。

這一章先講這么多,下一張我們講一下,挑戰隨機數和全能標志位的原理,有興趣的同學可以關注一下,有問題也可以隨時交流。

 
 
mysql報文最大可以容納16MB的數據,如果一次發送的數據超過16MB會被分割成多個mysql報文。
 
mysql報文的格式如下:

Payload

Type Name Description
int<3> payload_length Length of the payload. The number of bytes in the packet beyond the initial 4 bytes that make up the packet header.
int<1> sequence_id Sequence ID
string<var> payload [len=payload_length] payload of the packet
payload_length:用前3個字節表示報文真實數據的長度(不包括最前面的4個字節:前3個字節表明真實數據的長度,第4個字節表明該報文的序列)
sequence_id:用第4個字節表示報文唯一序列號,通常來說是相鄰兩個報文的序列號是遞增的
payload:這部分存放真實的數據內容,字節長度為payload_length指定的長度
 
eg:
 
COM_QUIT命令發送的報文如下所示:
 
01 00 00 00 01 
 
payload_length為1
sequence_id為0
payload內容為01,長度為1(由payload_length指定)

 

版本2:

Mysql JDBC的通信協議(報文的格式和基本類型)

 

mysql client和server端之間的的數據根據不同的協議規則的進行組織發送。每包數據在發送的時候都要添加上協議頭。

mysql源碼采用5.7.10版本:

協議頭:
           每個協議頭共4個字節

          包數據長度:

              前三個字節表示數據部分的長度(不包括協議頭),三字節能表示的最大長度是16M-1(2^24 - 1),如果要發送的數據部分大於這個長度,要進行拆包,每16M-1個長度為一包。接收端在接受數據的時候,如果檢測到包的長度是16M-1,說明后續還有數據部分,直到接收到<16M-1長度的數據包結束。這意味着最后一包的數據長度可能為0.

         序號:

                1個字節,從0開始遞增。當發送一個新的sql、數據庫重連,該值清0(函數sql/Net_serv.cc : net_clear).

數據類型:
         除了固定長度的整型或者字符串之外,還有其他幾種類型的數據。(固定長度字段數據的存取:include/Mybyte_order.h : 存值 int*store   取值:int*korr    多字節的處理按照小端優先的方式)

         1. 可變長度的整數   

            對該類數據的存取在函數:sql-common/Pack.c: 存整數: net_store_length     讀整數:net_field_length

            如果數值<251,直接用一個字節存儲這個值。

           如果251<=數值<2^16, 采用3個字節存儲,第一個字節是252, 另外2個字節存儲整數內容

           如果2^16<=數值<2^24,采用4字節存儲,第一個字節是252,另外3個字節存儲整數內容

           如果2^24<=數值<2^64,采用9字節存儲,第一個字節255,另外8字節存儲整數內容

           如果第一個字節為251,表示該整數字段為null

           如果第一個字節為255,表示該字節是ERR包的第一個字節

       2. 可編碼長度的字符串

               字符串的長度采用可變長度的整數進行編碼。

數據長度不固定,長度值由數據前的1-9個字節決定,其中長度值所占的字節數不定,字節數由第1個字節決定,如下表:

第一個字節值 后續字節數 長度值說明
0-250 0 第一個字節值即為數據的真實長度
251 0 空數據,數據的真實長度為零
252 2 后續額外2個字節標識了數據的真實長度
253 3 后續額外3個字節標識了數據的真實長度
254 8 后續額外8個字節標識了數據的真實長度

       3. null結尾的字符串

服務器響應包:
          服務器響應包分為4類: OK包  ERR包    EOF包    數據包

          OK包:在5.7.5之前,ok包首字節為0,;在5.7.5之后,ok包的首字節可能為0xFE,表示EOF。

                              該包包括成功執行后影響的行數,最新的自增id, 告警信息(4.1版本之上),服務器狀態信息:status_flag(該字段要留意,后續后講到)

                           執行函數:sql/protocol_classic.cc :   net_send_ok

                   由於協議的內容容易變更,建議查看官網的最新版格式:https://dev.mysql.com/doc/internals/en/packet-OK_Packet.html

          ERR包:首字節是255,報錯錯誤碼和錯誤的描述信息。4.1版本之上包括錯誤狀態。

                          執行函數:sql/protocol_classic.cc :   net_send_error_packet

                          具體包格式:https://dev.mysql.com/doc/internals/en/packet-ERR_Packet.html

          EOF包:首字節254,包括服務器狀態和告警數量(4.1版本之上)

                          執行函數:sql/protocol_classic.cc :   net_send_eof

                          具體包格式:https://dev.mysql.com/doc/internals/en/packet-EOF_Packet.html

          數據包:和具體的協議類型有關,后續講解

 


免責聲明!

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



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