MySQL 通信協議介紹
1、數據類型
了解MySQL協議包之前必需先知道其數據類型
1.1 Integer Types 整數類型
(1)定長整型
固定長度, 小端編碼, 有下面幾種(括號內的代表所占字節數):
int<1>
int<2>
int<3>
int<4>
int<6>
int<8>
(2)變長整型
可能長度為1, 3, 4, 9 個字節, 實際長度取決於數值的大小.記為 int<lenenc>
編碼過程, 記數值為N:
1.若 N < 251, 則編碼為單字節
2.若 251 <= N < 2^16, 則編碼為 0xfc + 2-byte integer
3.若 2^16 <= N < 2^24, 則編碼為 0xfd + 3-byte integer
4.若 2^24 <= N < 2^64, 則編碼為0xfe + 8-byte integer
解碼過程, 先看第一個字節, 記其值為N1,
1.若 N1 <= 0xfb, 則說明該整數就是N1
2.若 N1 = 0xfc,則說明接下來2字節也是該整數的部分, 取出加上0xfc即為原數值
3.若 N1 = 0xfd, ...
4.若 N1 = 0xfe, ...
值得注意的是, 當協議包的第一個字節為0xfe時, 應該檢查接下來是否有8個字節, 若沒有則說明該協議包可能是EOF_PACKET
1.2 String Types 字符串類型
(1)FixedLengthString
定長字符串,記為 string<fix>
(2)NulTerminatedString
由NUL標識結束的字符串, NUL即0x00,記為string<NUL>
(3)VariableLengthString
變長字符串, 長度由別的其它字段決定或者運行時動態計算出來的,記為string<var>
(4)LengthEncodedString
長度編碼的字符串, 即其前面有個整型int<lenenc>說明接下來的字符串的長度, 起始就是VariableLengthString,記為string<lenenc>
(5)RestOfPacketString
若一個字符串是一個協議包的最后一個字段, 那該字符串的長度當然就等於包長 - 當前位置,記為string<EOF>
2.MySQL協議包
客戶端或服務端要發消息給對方時:
1.把消息分成若干個長度小於 2^24-1(即16 M)的消息
2.給每個消息前面加一個4字節包頭.
如下:
Type | Name | Description |
int<3> | payload_length | payload字段的長度 |
int<1> | sequence_id | 序列號 |
string<var> | payload | payload包體 |
例如CMD_QUIT的整包字節序列為:01 00 00 00 01
1.通用響應報文
(1) OK_Packet
表示操作成功的報文,5.7開始用該報文替代EOF_Packet
報文格式:
判斷時OK還是EOF規則:
OK: header
= 0 and length of packet > 7
EOF: header
= 0xfe and length of packet < 9
(2) ERR_Packet
表示操作失敗的報文. It contains a SQL state value if CLIENT_PROTOCOL_41
is enabled.
報文格式:
(3) EOF_Packet
If CLIENT_PROTOCOL_41
is enabled, the EOF packet contains a warning count and status flags.
EOF報文用來表示多個報文組成的一個報文簇的結束,比如當客戶端發送查詢命令到mysql server時,mysql會按順序回復以下報文:
1.ResultSetHeaderPacket --報文中攜帶了查詢結果包含的字段的個數N,以及其它一些信息
2.FieldPacket * N -- N個FieldPacket,每一個都表示一個字段的定義
3.EOF Packet -- 表示字段定義的所有報文已發送完畢
4.RowDataPacket * M -- 每個報文表示一行數據
5.EOF Packet -- 表示行數據報文發送完畢。
報文格式:
EOF_Packet和OK_Packet 都是用來表示一個query的結果的尾包,5.7.5版本EOF_Packet就不在使用了
上面的報文中涉及到的statu_flag取值及含義如下:
2.連接建立階段報文
(1) Handshake Packet(Server -> Client)
握手報文,當客戶端連接服務端時,當TCP連接建立完成后,Server會主動向Client發送一個握手協議報文,里面攜帶了Server的相關信息和權能標識,關於權能標識(capabilities flag),具體可以去這里查
- 1 [0a] protocol version -- 協議版本
- string[NUL] server version -- Server版本
- 4 connection id
- string[8] auth-plugin-data-part-1
- 1 [00] filler -- 填充,總是0x00
- 2 capability flags (lower 2 bytes) --Server權能標識
- if more data in the packet:
- 1 character set -- 連接所使用的字符集
- 2 status flags
- 2 capability flags (upper 2 bytes)
- if capabilities & CLIENT_PLUGIN_AUTH {
- 1 length of auth-plugin-data
- } else {
- 1 [00]
- }
- string[10] reserved (all [00])
- if capabilities & CLIENT_SECURE_CONNECTION {
- string[$len] auth-plugin-data-part-2 ($len=MAX(13, length of auth-plugin-data - 8))
- if capabilities & CLIENT_PLUGIN_AUTH {
- string[NUL] auth-plugin name
- }
(2)HandShake Response Packet(Client -> Server)
客戶端收到Handshake Packet后,生成一個Response並發給Server,里面攜帶客戶端的權能標識,用戶名等相關的信息,如果Handshake Packet中的權能標識中帶有client_protocol_41標識位,那么客戶端將生成的Response為HandShake Response41,否則生成的為HandShake Response320,我們這里就不管HandShake Response320,因為現在的Server基本都會支持這個
HandShake Response41格式:
- 4 capability flags, CLIENT_PROTOCOL_41 always set -- 客戶端的權能標識
- 4 max-packet size -- 最大包大小
- 1 character set -- 字符集
- string[23] reserved (all [0]) -- 保留,總是0
- string[NUL] username -- 用戶名
- if capabilities & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA { -- 看了mycat的源碼才知道,這個其實是密碼
- lenenc-int length of auth-response
- string[n] auth-response
- } else if capabilities & CLIENT_SECURE_CONNECTION {
- 1 length of auth-response
- string[n] auth-response
- } else {
- string[NUL] auth-response
- }
- if capabilities & CLIENT_CONNECT_WITH_DB { -- 連接默認數據庫名
- string[NUL] database
- }
- if capabilities & CLIENT_PLUGIN_AUTH {
- string[NUL] auth plugin name
- }
- if capabilities & CLIENT_CONNECT_ATTRS {
- lenenc-int length of all key-values
- lenenc-str key
- lenenc-str value
- if-more data in 'length of all key-values', more keys and value pairs
- }
3.普通請求/響應報文
在連接建立后,Client就可以發送普通的查詢或者其它命令給Server了,下面對這些命令進行介紹
1.COM_SLEEP 請求
這個是Server內部的命令,不是客戶端給服務端發的,忽略即可
- 1 [00] COM_SLEEP
2.COM_QUIT 請求
當客戶端要關閉連接時,發送該命令到Server
- 1 [01] COM_QUIT
返回:要么是OK Packet,要么就連接關閉
3.COM_INIT_DB 請求
修改連接默認的schema
- 1 [02] COM_INIT_DB
- string[EOF] schema name -- 數據庫名
返回:OK Packet 或者ERR Packet
4.COM_QUERY 請求
發送普通查詢請求,這個查詢是立即執行的,因此預編譯不是發這個命令的
- 1 [03] COM_QUERY
- string[EOF] the query the server shall execute -- sql語句
返回:COM_QUERY_RESPONSE Packet
5.COM_QUERY_PACKET 響應
COM_QUERY的響應報文,該報文只是個邏輯上的報文,其組成為以下幾個中的一個:
- ERR Packet
- OK Packet
- ResultSet Packet -- 普通查詢結果
- LOAD DATA INFILE REQUEST Packet
6.ResultSetPacket 響應
普通查詢結果,也是一個邏輯上的報文,其由以下部分組成:
1.ResultSetHeaderPacket -- 只包含一個int<lenenc>的整數(記為N,表示結果字段的個數)和一個8字節的extra附加信息
2.ColumnDefinitionPacket * N -- N個ColumnDefinitionPacket ,每個ColumnDefinitionPacket表示一個字段的信息,其格式如下:
- lenenc_str catalog
- lenenc_str schema
- lenenc_str table
- lenenc_str org_table
- lenenc_str name
- lenenc_str org_name
- lenenc_int length of fixed-length fields [0c]
- 2 character set
- 4 column length
- 1 type
- 2 flags
- 1 decimals
- 2 filler [00] [00]
- if command was COM_FIELD_LIST {
- lenenc_int length of default-values
- string[$len] default values
- }
3.EOF Packet 表示所有ColumnDefinitionPacket都發送完了,接下來將發行數據了
4.任意個RowDataPacket,每一個代表一行數據,其中NULL用0xfb(251)表示,其格式如下:
- lenenc_str field1's value -- 第一個字段的值
- lenenc_str field2's value -- 第二個字段的值
- ...
- lenenc_str fieldN's value -- 第N個字段的值
5.EOF Packet,表示ResultSetPacket完畢,若此包中的SERVER_MORE_RESULT_EXISTS被設置,則則說明接下來還有ResultSetPacket,又回到第一步開始接收...
整個查詢過程如下圖:
7.COM_FIELD_LIST 請求
獲取字段定義,5.7.11開始已棄用
- 1 [04] COM_FIELD_LIST
- string[NUL] table -- 表名
- string[EOF] field wildcard 字段通配符
返回:COM_FIELD_LIST RESPONSE Packet
8.COM_FIELD_LIST 響應
COM_FIELD_LIST的響應報文,該報文是一個邏輯報文,其組成為以下一種:
1.ERR packet
2.任意個ColumnDefinitionPacket加一個EOFPacket
9.COM_CREATE_DB 請求
創建數據庫請求
- 1 [05] COM_CREATE_DB
- string[EOF] schema name -- 數據庫名
返回:OK Packet或者ERR Packet
10.COM_DROP_DB 請求
刪除數據庫請求
- 1 [06] COM_DROP_DB
- string[EOF] schema name -- 數據庫名
返回:OK Packet 或者 ERR Packet
11.COM_REFRESH 請求
Flush ... 和RESET ... 語句的實現命令,從5.7.11版本開始已棄用,改用COM_QUERY,執行FLUSH語句或者
- 1 [07] COM_REFRESH
- 1 sub_command -- 命令,取值見下
sub_command取值:
返回:OK Packet或者ERR Packet
12.COM_SHUTDOWN 請求
shutdown mysql server的請求,5.7.9已棄用,8.0已移除,執行mysqladmin shutdown時發送該命令
- 1 [08] COM_SHUTDOWN
- if more data {
- 1 shutdown type
- }
shutdown type取值:
返回:OK Packet或者ERR packet
13.COM_STATISTICS 請求
獲取human readable的mysql server統計信息,執行mysqladmin status時發送該命令
- 1 [09] COM_STATISTICS
返回:string<EOF>
14.COM_PROCESS_INFO 請求
get a list of active threads,執行show processlist 或者 mysqladminproesslist時發送該命令
- 1 [0a] COM_PROCCESS_INFO
返回:ResultSetPacket 或者ERR Packet
15.COM_CONNECT 請求
mysql server 內部命令,5.7.11版本棄用,使用COM_QUERY替代
16.COM_PROCESS_KILL 請求
關閉連接
- 1 [0c] COM_PROCCESS_KILL
- 4 connection id -- 連接ID,這個是在HandShake Packet傳過來的
返回:OK Packet或者ERR Packet
16.COM_DEBUG 請求
COM_DEBUG triggers a dump on internal debug info to stdout of the mysql-server.The SUPER privilege is required for this operation.執行mysqladmin debug時發送該命令
- 1 [0d] COM_DEBUG
返回:EOF Packet或者ERR Packet
17.COM_PING 請求
心跳檢測
- 1 [0e] COM_PING
返回:OK Packet
18.COM_TIME
mysql server內部命令
19.COM_DELAYED_INSERT
mysql server內部命令
20.COM_CHANGE_USER 請求
更改當前連接的用戶並重置連接狀態,包括user variables、臨時表和預編譯語句等等...
- 1 [11] COM_CHANGE_USER
- string[NUL] user
- if capabilities & SECURE_CONNECTION {
- 1 auth-response-len
- string[$len] auth-response
- } else {
- string[NUL] auth-response
- }
- string[NUL] schema-name
- if more data {
- 2 character-set
- if capabilities & CLIENT_PLUGIN_AUTH {
- string[NUL] auth plugin name
- }
- if capabilities & CLIENT_CONNECT_ATTRS) {
- lenenc-int length of all key-values
- lenenc-str key
- lenenc-str value
- if-more data in 'length of all key-values', more keys and value pairs
- }
- }
21.COM_RESET_CONNECTION 請求
Resets the session state; more lightweight than COM_CHANGE_USER because it does not close and reopen the connection, and does not re-authenticate
- 1 [1f] COM_RESET_CONNECTION
返回:OK Packet或者ERR Packet
22.COM_DAEMON 請求
mysql server內部命令
23.COM_STMT_PREPARE 請求
預編譯語句,執行conn.preparedStatement(sql)時發送該命令
- 1 the COM_STMT_PREPARE command
- string<EOF> query statement -- sql語句
返回:COM_STMT_PREPARED OK Packet或者Err Packet
24.COM_STMT_PREPARE Response 響應
預編譯成功時的響應報文,時邏輯報文,其由多個報文組成:
1.第一個報文包含預編譯成功信息,其格式如下:
- 1 status, [00]=OK --成功標志
- 4 statementId -- 預編譯的語句ID
- 2 number of columns -- 字段個數
- 2 number of params -- 參數個數
- 1 reserved, always 00 -- 保留
- 2 number of warnings -- 警告個數
2.如果上面的參數個數(記為N)大於0的話,那么接下來會有N個ColumnDefinitionPacket加上一個EOF Packet會被發送
3.如果上面的字段個數(記為M)大於0的話,那么接下來會有M個ColumnDefinitionPacket加上一個EOF Packet會被發送
25.COM_STMT_SEND_LONG_DATA請求
發送參數值給預編譯語句,預編譯中有幾個?就得發幾個該請求,並且都得在COM_STMT_EXECUTE之前發送
- 1 [18] COM_STMT_SEND_LONG_DATA
- 4 statement-id -- 預編譯語句ID
- 2 param-id -- 參數ID
- n data -- 參數值
返回:無返回
26.COM_STMT_EXECUTE請求
執行預編譯語句
- 1 [17] COM_STMT_EXECUTE
- 4 stmt-id -- 語句ID
- 1 flags
- 4 iteration-count, always 1
- if num-params > 0:
- n NULL-bitmap, length: (num-params+7)/8
- 1 new-params-bound-flag
- if new-params-bound-flag == 1:
- n type of each parameter, length: num-params * 2
- n value of each parameter
flags取值:
返回:COM_STMT_EXECUTE Response
27.COM_STMT_EXECUTE Response響應
COM_STMT_EXECUTE 的響應報文,是下面三者之一:
1.OK Packet -- 執行update時
2.Err Packet
3.Binary Protocol ResultSet Packet --執行查詢時
Binary Protocol ResultSet
binary protocol result和之前講的ResultSetPacket相似,
28.COM_STMT_CLOSE請求
關閉預編譯語句,該請求無響應
- 1 [19] COM_STMT_CLOSE
- 4 statement-id -- 預編譯語句ID
29.COM_STMT_RESET請求
清空由COM_STMT_SEND_LONG_DATA發送的所有參數值,並關閉由COM_STMT_EXECUTE打開的CURSOR
- 1 [1a] COM_STMT_RESET
- 4 statement-id
返回:OK Packet或者ERR Packet