MySQL - 通讯
前言
学习 MySQL 通信编码.
数据类型
MySQL 协议中有两种数据类型:
- int
- 固定长度
- 长度编码
- string
- 固定长度
\0
结尾
数据包
MySQL 通信 Payload
类型 | 名称 | 描述 |
---|---|---|
int<3> | Payload 长度 | 声明该数据包有效数据为多少字节 |
int<1> | 序列ID | 数据包ID |
string<var> | payload | 数据包内容 |
例如:
关闭连接 COM_QUIT 的 Payload 为:
01 00 00 00 01 | 长度: 1
| 序列ID: 0x00
| payload: 0x01
也就是说, 发送一次数据包, Payload 最长可以为
2
24
−
1
2^{24} - 1
224−1, int<3>
表示三个字节用来表示长度.
发送 16MB 数据
那如果说, 我需要发送 2 24 2^{24} 224 bit(16MB) 的数据, 那该如何呢?
答案是:
1: ff ff ff 00 ...
2: 00 00 00 01
上面表示有两次连续请求,
第一次发送 Payload 长度为
2
24
2^{24}
224 bit, 序列 ID 为 00, ...
表示 Payload 数据;
第二次发送 Payload 长度为 0, 表示已没有更多 Payload, 序列 ID 为 01.
序列 ID
每个数据包的序列 ID 都会递增,并且可能会回绕。 它从 0 开始,并在命令阶段开始新命令时重置为 0。
实操
查询数据库, 查看 JDBC 请求和 MySQL server 响应数据.
表结构:
DROP TABLE IF EXISTS users;
CREATE TABLE users
(
id BIGINT(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增Id',
username VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT '用户名',
age TINYINT(3) NOT NULL DEFAULT 1 COMMENT '年龄',
PRIMARY KEY (id)
) ENGINE = InnoDB
AUTO_INCREMENT = 1
DEFAULT CHARSET = utf8mb4 COMMENT ='用户表';
INSERT INTO users (username, age) VALUES ('小明', 11);
依赖:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
代码:
InputStream resourceAsStream = Thread.currentThread()
.getContextClassLoader()
.getResourceAsStream("jdbc.properties");
Properties p = new Properties();
p.load(resourceAsStream);
Class.forName("com.mysql.cj.jdbc.Driver");
Connection connection = DriverManager.getConnection(URL, p);
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("select * from users");
while (resultSet.next()) {
int id = resultSet.getInt("id");
System.out.printf("id: %d%n", id);
String username = resultSet.getString("username");
System.out.printf("username: %s%n", username);
int age = resultSet.getInt("age");
System.out.printf("age: %d%n", age);
}
resultSet.close();
statement.close();
connection.close();
// id: 1
// username: 小明
// age: 11
交互流程
DriverManager::getConnection
IP | 功能 |
---|---|
192.168.3.1 | client |
192.168.3.28 | server |
红色圈起来的是 client 发起的 TCP 三次握手, 这个我们需要去了解 TCP 协议, 这里就不做过多解释.
黄色就是初始握手, 从服务器发送初始握手数据包开始. 此后, 客户端可以选择使用 SSL 连接请求数据包请求建立 SSL 连接,然后客户端发送握手响应数据包。
服务端初始握手:
蓝色是验证帐号信息, 假设客户端要以用户 U 身份登录,并且该用户帐户使用身份验证方法 M。如果客户端和服务器都使用方法 M 在初始握手中生成身份验证数据,则使用快速身份验证路径。 在这种情况下,握手期间已经开始了第一轮认证。 现在,取决于身份验证方法,可以交换其他身份验证数据,直到服务器拒绝或接受连接为止。
客户端用户登录:
绿色是相关配置, 例如设置自动提交:
自此, DriverManager::getConnection
的事就做完了, 接下来就是 Statement::executeQuery
做的事了
Statement::executeQuery
我们先看看请求, 请求类型为 COM_QUERY
响应数据 COM_QUERY Response
Connection::close
在这我们看看开头关闭连接的数据是不是和实际一样的
MySQL 的操作流程到此就完成了.
资源
Chapter 14 MySQL Client/Server Protocol
MySQL Client/Server Protocol