mysql 协议说明_MySQL 通信协议介绍
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
编码过程, 记数值为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
(2)NulTerminatedString
由NUL标识结束的字符串, NUL即0x00,记为string
(3)VariableLengthString
动态长度字符串的长度取决于其他字段或其他因素,并且通过运行时计算得出的结果以string表示
(4)LengthEncodedString
采用长度编码的字符串表示法,则在字符串开头有一个整数类型的标识符int来指示后续字符串的实际长度;同时,在变量长度编码下进行操作时,默认将这些字符串标记为VariableLengthString,并用符号string表示。
(5)RestOfPacketString
若一个字符串属于协议包中的最后一个字段,则该字符串自然等于总长度减去当前位置,并将其标记为string
2.MySQL协议包
客户端或服务端要发消息给对方时:
1.把消息分成若干个长度小于 2^24-1(即16 M)的消息
2.给每个消息前面加一个4字节包头.
如下:
Type
Name
Description
int<3>
payload_length
payload字段的长度
int<1>
sequence_id
序列号
string
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
该错误信息包含SQL状态值,当CLIENT_PROTOCOL_41处于启用状态时生效。
报文格式:

(3) EOF_Packet
If CLIENT_PROTOCOL_41 is activated, when the EOF packet is received, it includes a warning count and status flags.
EOF报文用于表示一个由多个报文组成的报文簇的结束。例如,在MySQL服务器上,当客户端发起一条查询指令时,MYSQL将依次回复这些报文:
ResultSetHeaderPacket在报文里包含了查询结果返回的相关字段数量N,并附加了一些额外的信息
2.FieldPacket * N -- N个FieldPacket,每一个都表示一个字段的定义
3.EOF Packet -- 表示字段定义的所有报文已发送完毕
4.RowDataPacket * M -- 每个报文表示一行数据
5.EOF Packet -- 表示行数据报文发送完毕。
报文格式:

EOF_Packet与OK_Packet都被设计用于标识查询结果的尾部数据包,在5.7.5版本中已经不再采用这种设计
上面的报文中涉及到的statu_flag取值及含义如下:

2.连接建立阶段报文
(1) Handshake Packet(Server -> Client)
握手信息会在客户端尝试连接服务端时生成。当TCP连接建立之后, Server会向Client发送一个握手协议报文,其中包含了Server的相关信息以及权能标识。关于权能标识(capabilities flag),具体内容可参考链接获取详细解释
Java代码
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包时,它会生成一个响应并发送给服务器,并在响应中包含客户端的能力标志以及相关的信息。如果该Handshake包中的能力标志包含client_protocol_41字段,则对应的响应会被标记为Handshake Response41;否则则会被标记为Handshake Response320。然而,在我们的处理范围内,则不涉及Handshake Response320的情况。
HandShake Response41格式:
Java代码
4 capability flags, CLIENT_PROTOCOL_41 always set -- 客户端的权能标识
4 max-packet size -- 最大包大小
1 character set -- 字符集
string[23] reserved (all [0]) -- 保留,总是0
string[NUL] username -- 用户名
当具备某种能力时,在满足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内部的命令,不是客户端给服务端发的,忽略即可
Java代码
1 [00] COM_SLEEP
2.COM_QUIT 请求
当客户端要关闭连接时,发送该命令到Server
Java代码
1 [01] COM_QUIT
返回:要么是OK Packet,要么就连接关闭
3.COM_INIT_DB 请求
修改连接默认的schema
Java代码
1 [02] COM_INIT_DB
string[EOF] schema name -- 数据库名
返回:OK Packet 或者ERR Packet
4.COM_QUERY 请求
提交普通的查询请求,则该查询属于即时处理类型,则无需进行预编译操作。
Java代码
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 响应
普通查询结果,也是一个逻辑上的报文,其由以下部分组成:
- 该数据包仅包含一个整数(标记为N变量)代表结果字段的数量以及一个8-byte大小的附加信息块
第2部分包含N个ColumnDefinitionPacket(其中N为具体数值),每个ColumnDefinitionPacket代表一个字段的相关信息。
Java代码
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
}
- EOF包标识所有ColumnDefinition包已全部传输完毕;接下来将对数据进行处理。
多个RowDataPacket分别对应一行数据信息,在其中的Null字段采用0xfb(251)进行编码表示
Java代码
lenenc_str field1's value -- 第一个字段的值
lenenc_str field2's value -- 第二个字段的值
...
lenenc_str fieldN's value -- 第N个字段的值
第5个EOF包表明该ResultSetPacket已结束。如果此包中包含 SERVER_MORE_RESULT_EXISTS字段,则说明接下来还会有一个新的 ResultSetPacket出现,并将从头开始接收数据。
整个查询过程如下图:

7.COM_FIELD_LIST 请求
获取字段定义,5.7.11开始已弃用
Java代码
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 请求
创建数据库请求
Java代码
1 [05] COM_CREATE_DB
string[EOF] schema name -- 数据库名
返回:OK Packet或者ERR Packet
10.COM_DROP_DB 请求
删除数据库请求
Java代码
1 [06] COM_DROP_DB
string[EOF] schema name -- 数据库名
返回:OK Packet 或者 ERR Packet
11.COM_REFRESH 请求
flush和reset语句的实现指令自5.7.11版本起被废弃使用。现在应采用com_query来进行flush操作,并执行相应的flush指令。
Java代码
1 [07] COM_REFRESH
1 sub_command -- 命令,取值见下
sub_command取值:

返回:OK Packet或者ERR Packet
12.COM_SHUTDOWN 请求
提交用于关闭MySQL服务器的请求,在版本5.7.9及以后将不再支持该功能,在8.0版本及以后将完全移除该命令,在运行mysqladmin shutdown指令时需注意此事项
Java代码
1 [08] COM_SHUTDOWN
if more data {
1 shutdown type
}
shutdown type取值:

返回:OK Packet或者ERR packet
13.COM_STATISTICS 请求
输出MySQL服务器运行状态信息,在执行MySQL管理员status命令时显示该统计结果。
Java代码
1 [09] COM_STATISTICS
返回:string
14.COM_PROCESS_INFO 请求
Obtain a list of active threads. When executing the commands 'mysql -k show processlist' or 'mysqladmin -k show processlist', send this command.
Java代码
1 [0a] COM_PROCCESS_INFO
返回:ResultSetPacket 或者ERR Packet
15.COM_CONNECT 请求
mysql server 内部命令,5.7.11版本弃用,使用COM_QUERY替代
16.COM_PROCESS_KILL 请求
关闭连接
Java代码
1 [0c] COM_PROCCESS_KILL
4 connection id -- 连接ID,这个是在HandShake Packet传过来的
返回:OK Packet或者ERR Packet
16.COM_DEBUG 请求
When the COM_DEBUG trigger is activated, it dumps internal debug information to the stdout of the mysql-server. The SUPER user privilege is necessary for this function. Issue the MySQL Administration tool's debug command when needed.
Java代码
1 [0d] COM_DEBUG
返回:EOF Packet或者ERR Packet
17.COM_PING 请求
心跳检测
Java代码
1 [0e] COM_PING
返回:OK Packet
18.COM_TIME
mysql server内部命令
19.COM_DELAYED_INSERT
mysql server内部命令
20.COM_CHANGE_USER 请求
修改当前连接的用户并重新设置其连接状态, 包括与该用户的关联信息, 同时也涉及user variables、临时表和预编译语句等技术细节.
Java代码
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 请求
Reinitialize the session state. Compared to COMCHANGEUSER, this approach is lighter in terms of connection management as it avoids closing and reopening the connection and does not require authentication.
Java代码
1 [1f] COM_RESET_CONNECTION
返回:OK Packet或者ERR Packet
22.COM_DAEMON 请求
mysql server内部命令
23.COM_STMT_PREPARE 请求
预编译语句,执行conn.preparedStatement(sql)时发送该命令
Java代码
1 the COM_STMT_PREPARE command
string query statement -- sql语句
返回:COM_STMT_PREPARED OK Packet或者Err Packet
24.COM_STMT_PREPARE Response 响应
预编译成功时的响应报文,时逻辑报文,其由多个报文组成:
1.第一个报文包含预编译成功信息,其格式如下:
Java代码
1 status, [00]=OK --成功标志
4 statementId -- 预编译的语句ID
2 number of columns -- 字段个数
2 number of params -- 参数个数
1 reserved, always 00 -- 保留
2 number of warnings -- 警告个数
当参数个数(记为N)为非零值时,将包含N个ColumnDefinitionPacket以及一个EOF Packet。
当某个字段集合(标记为M)的数量超过零时,将被发送包括M个ColumnDefinitionPacket和一个EOF包。
25.COM_STMT_SEND_LONG_DATA请求
将参数值传递至预先编译的语句。预先编译中有多少个,则需要相应地发送该请求,并且所有请求必须在COM_STMT.Execute之前完成。
Java代码
1 [18] COM_STMT_SEND_LONG_DATA
4 statement-id -- 预编译语句ID
2 param-id -- 参数ID
n data -- 参数值
返回:无返回
26.COM_STMT_EXECUTE请求
执行预编译语句
Java代码
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
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请求
关闭预编译语句,该请求无响应
Java代码
1 [19] COM_STMT_CLOSE
4 statement-id -- 预编译语句ID
29.COM_STMT_RESET请求
清除由COM_STMT_SEND_LONG_DATA传输的所有各项数值,并同时关闭由COM_STMT.Execute所打开的游标
Java代码
1 [1a] COM_STMT_RESET
4 statement-id
返回:OK Packet或者ERR Packet
