OK_Packet
1. OK_Packet 的核心作用
- 成功响应标识 :当客户端发送命令(如查询、设置变量等)成功执行后,服务器通过 OK_Packet 通知客户端操作已成功完成。
- 取代 EOF_Packet :从 MySQL 5.7.5 开始,OK_Packet 也用于标识结果集结束(End of File, EOF),原有的 EOF_Packet 被弃用。
2. OK_Packet 的协议结构
Packet 的 Payload(有效载荷) 结构根据客户端的能力标志(Capability Flags) 动态变化:
字段 | 数据类型 | 出现条件 | 描述 |
---|---|---|---|
header |
int<1> | 必选 | 固定值 0x00 (OK)或 0xFE (EOF) |
affected_rows |
int | 必选 | 受影响的行数(如 UPDATE/DELETE) |
last_insert_id |
int | 必选 | 最后插入的自增 ID(如 INSERT) |
status_flags |
int<2> | 若 CLIENT_PROTOCOL_41 能力启用 |
服务器状态标志(如事务状态) |
warnings |
int<2> | 若 CLIENT_PROTOCOL_41 能力启用 |
警告数量 |
info |
string | 若 CLIENT_SESSION_TRACK 能力启用 |
人类可读的状态信息(如 SQL 提示) |
session_state_info |
string | 若 status_flags 包含 SERVER_SESSION_STATE_CHANGED |
会话状态变更信息(详细结构见下文) |
3. 区分 OK 与 EOF
- OK Packet :
header = 0x00
且 包长度 > 7 - EOF Packet :
header = 0xFE
且 包长度 < 9
(MySQL 5.7.5+ 通过此方式兼容旧版协议)
4. 向后兼容性处理
客户端/服务器组合 | 行为 |
---|---|
旧客户端 + 新服务器 | 客户端不声明 CLIENT_DEPRECATE_EOF → 服务器发送传统 EOF_Packet |
新客户端 + 旧服务器 | 服务器不支持新标志 → 发送传统 EOF_Packet |
新客户端 + 新服务器 | 客户端声明 CLIENT_DEPRECATE_EOF → 服务器用 OK_Packet (0xFE ) 代替 EOF |
5. 会话状态跟踪(Session State Information)
当会话状态变更时(如修改系统变量、切换数据库),session_state_info
包含多个状态块,结构如下:
lua
+------+-------------------+--------------------------------+
| Type | Data | 说明 |
+------+-------------------+--------------------------------+
| 0x00 | 0x0F | 跟踪类型:系统变量变更 |
| | "autocommit" | 变量名 |
| | "OFF" | 新值 |
+------+-------------------+--------------------------------+
| 0x01 | "test" | 跟踪类型:切换 Schema |
| | | 数据库名(如 `USE test`) |
+------+-------------------+--------------------------------+
| 0x02 | "1" | 跟踪类型:启用状态跟踪 |
| | | (ASCII '1' 表示启用) |
+------+-------------------+--------------------------------+
-
常见跟踪类型:
SESSION_TRACK_SYSTEM_VARIABLES
(0x00):系统变量变更(如SET autocommit=OFF
)SESSION_TRACK_SCHEMA
(0x01):Schema 切换(如USE test
)SESSION_TRACK_STATE_CHANGE
(0x02):会话跟踪功能启用状态变更
6. 示例说明
场景 :执行成功且启用 CLIENT_PROTOCOL_41
能力
响应内容:
affected_rows = 0
last_insert_id = 0
status_flags = SERVER_STATUS_AUTOCOMMIT
(表示开启自动提交)warnings = 0
- 无额外信息(
info
和session_state_info
为空)
关键总结
- 功能演进 :OK_Packet 在 MySQL 5.7.5+ 中一包两用(成功响应 + EOF 标识)。
- 动态结构 :包体结构由客户端能力标志 动态决定,需检查
CLIENT_PROTOCOL_41
等标志。 - 会话跟踪 :通过
session_state_info
实现精细化状态同步(如变量变更、数据库切换)。 - 兼容性 :新老协议通过
CLIENT_DEPRECATE_EOF
标志智能切换,确保平滑过渡。
注 :实际实现参考 MySQL 源码中的
net_send_ok()
函数。
MySQL ERR_Packet 详解
ERR_Packet 是 MySQL 客户端/服务器协议中用于传递错误信息的数据包。当客户端请求发生错误时(如 SQL 语法错误、权限问题等),服务器会返回此包。
包结构(Payload)
字段 | 类型/长度 | 描述 |
---|---|---|
header |
int<1> |
**固定值 0xFF **(标识这是一个 ERR_Packet) |
error_code |
int<2> |
错误代码 (如 1146 表示表不存在) |
sql_state_marker (可选) |
string[1] |
固定字符 # (仅在启用 CLIENT_PROTOCOL_41 时存在) |
sql_state (可选) |
string[5] |
5字符的 SQL 状态码 (如 42S02 表示表不存在,需启用协议 4.1) |
error_message |
string<EOF> |
人类可读的错误描述 (长度不超过 MYSQL_ERRMSG_SIZE=512 字节) |
示例解析
示例数据(十六进制):
17 00 00 01 ff 48 04 23 48 59 30 30 30 4e 6f 20 74 61 62 6c 65 73 20 75 73 65 64
-
包头部
17 00 00
:包长度(23 字节,小端序)01
:序列号(Sequence ID)ff
:ERR_Packet 标识(header=0xFF
)
-
错误代码
48 04
:小端序解析为0x0448
→ 十进制错误码1096
。
-
SQL 状态(协议 4.1+)
23
:sql_state_marker
(ASCII#
)48 59 30 30 30
:ASCII 解码 → **SQL 状态HY000
**(通用错误)。
-
错误信息
4e 6f 20 74 61 62 6c 65 73 20 75 73 65 64
→ ASCII 解码为 **No tables used
**。
最终错误信息 :
错误码 1096 (HY000): No tables used
关键说明
-
协议版本依赖
sql_state_marker
和sql_state
仅在客户端支持 **CLIENT_PROTOCOL_41
** 时发送(MySQL 4.1+ 默认启用)。
-
常见错误码
错误码 SQL 状态 典型场景 1064
42000
SQL 语法错误 1146
42S02
表不存在 1045
28000
用户权限认证失败 -
生成函数
- 服务器内部通过
net_send_error_packet()
函数生成此包。
- 服务器内部通过
实际应用场景
-
当客户端执行
SELECT * FROM non_existent_table;
时,服务器返回:- 错误码:
1146
- SQL 状态:
42S02
- 错误信息:
Table 'test.non_existent_table' doesn't exist
- 错误码:
-
客户端库(如 Connector/Python、JDBC)会解析 ERR_Packet,抛出类似
mysql.connector.errors.ProgrammingError
的异常。
📌 总结:ERR_Packet 是 MySQL 协议中高效传递错误的标准格式,包含机器可读的错误码和人类可读的描述,是客户端错误处理的核心机制。
MySQL EOF_Packet 详解
核心作用
EOF_Packet 是 MySQL 协议中用于标记查询结果结束的包(如结果集传输完毕或命令执行结束)。在 MySQL 5.7.5+ 中已被 OK_Packet 取代,但为兼容旧版协议仍可能发送。
包结构(Payload)
字段 | 类型/长度 | 出现条件 | 描述 |
---|---|---|---|
header |
int<1> |
必选 | 固定值 0xFE (标识 EOF) |
warnings |
int<2> |
启用 CLIENT_PROTOCOL_41 时 |
警告数量 |
status_flags |
int<2> |
启用 CLIENT_PROTOCOL_41 时 |
服务器状态标志(如事务状态) |
关键限制 :包总长度必须 < 9 字节(否则可能被误判为其他数据类型)。
协议演进与兼容性
场景 | 行为 |
---|---|
MySQL < 5.7.5 | 使用 EOF_Packet 标记结果结束 |
MySQL ≥ 5.7.5 | 默认用 OK_Packet 取代 EOF_Packet(支持更多功能如会话状态跟踪) |
旧客户端 + 新服务器 | 服务器降级发送 EOF_Packet(若客户端不支持 CLIENT_DEPRECATE_EOF 标志) |
新客户端 + 旧服务器 | 服务器始终发送 EOF_Packet |
示例解析
示例数据(十六进制):
05 00 00 05 fe 00 00 02 00
-
包头解析:
05 00 00
:包长度(5 字节,小端序)05
:序列号(Sequence ID)fe
:EOF_Packet 标识(header=0xFE
)
-
协议 4.1 扩展字段:
00 00
:警告数 = 002 00
:状态标志 =SERVER_STATUS_AUTOCOMMIT
(小端序0x0002
)
含义 :
"结果集已结束,无警告,自动提交已启用"
关键注意点
-
与 OK_Packet 的区分:
- 检查包头:
0xFE
+ 包长 < 9 字节 → EOF_Packet - 若包长 ≥ 9 字节且包头为
0x00
→ OK_Packet(含更多数据)
- 检查包头:
-
弃用警告:
- MySQL 5.7.5+ 优先使用 OK_Packet(因支持会话跟踪等新特性)
- 仅在兼容旧客户端时发送 EOF_Packet
-
风险场景:
- 解析时若未检查包长,可能将 **其他数据中的
0xFE
** 误判为 EOF(文档特别警告需验证包长 < 9)
- 解析时若未检查包长,可能将 **其他数据中的
函数关联
- 服务器生成此包调用:**
net_send_eof()
**
总结:EOF_Packet 是 MySQL 协议的历史遗留标记,现代版本(5.7.5+)应优先处理 OK_Packet。理解其结构及兼容逻辑对开发数据库中间件或协议分析工具至关重要。