MySQL 压缩数据包详解
1. 整体结构
MySQL 压缩数据包由两部分组成:
- 压缩包头 (Compressed Packet Header):固定 7 字节
- 有效载荷 (Payload):压缩或未压缩的实际数据
diff
plaintext
复制
+---------------------+---------------------+
| 压缩包头 (7 字节) | 有效载荷 |
+---------------------+---------------------+
2. 压缩包头详解
字段 | 大小 | 说明 |
---|---|---|
压缩后长度 | 3 字节 | 大端序存储,表示有效载荷的实际长度(即整个包长度减 7 字节包头) |
序列号 | 1 字节 | 独立于普通数据包的序列号,按压缩包递增 |
未压缩长度 | 3 字节 | 关键标志位: - >0 :表示压缩数据 - =0 :表示未压缩数据 |
3. 有效载荷的两种形式
(1) 压缩数据 (未压缩长度 > 0
)
- 使用 zlib 的 DEFLATE 算法(RFC 1951)压缩
- 可包含多个 MySQL 数据包(协议允许合并压缩多个包)
- 解压后数据 = 原始 MySQL 包(含 4 字节包头)
(2) 未压缩数据 (未压缩长度 = 0
)
- 直接存储原始 MySQL 数据包
- 触发条件:数据长度 < MIN_COMPRESS_LENGTH(默认 50 字节)
4. 关键机制解析
(1) 多包合并压缩
scss
plaintext
复制
原始包 1 (18 字节) 原始包 2 (25 字节)
↓ ↓
+---------------------------+
| 合并压缩 (单个压缩包) |
+---------------------------+
优势:减少小包场景下的网络开销
(2) 长度限制规避
当原始包长度 ≥ 16MB(2²⁴ - 1)时:
- 压缩包头无法表示未压缩长度(仅 3 字节)
- 强制拆分为多个压缩包发送
(3) 序列号独立
- 压缩包序列号与普通包序列号各自独立计数
- 每个连接维护两套序列号
5. 实战案例解析
(1) 压缩示例:`SELECT "0123..." (46 字节)
makefile
plaintext
复制
原始包:
2e 00 00 00 03 ... 35 22 (46 字节)
压缩包:
22 00 00 00 → 压缩后长度 = 34 字节 (0x22)
32 00 00 → 未压缩长度 = 50 字节 (含包头膨胀)
78 9c ... 6c → DEFLATE 压缩数据
✓ 压缩率:46 → 34 字节(节省 26%)
(2) 未压缩示例:SELECT 1
(13 字节)
makefile
plaintext
复制
压缩包:
0d 00 00 00 → 有效载荷长度 = 13 字节
00 00 00 → 未压缩长度 = 0 (标志位)
09 00 00 00 → 原始包头 (长度 9 + 序列号 0)
03 53 45 ... → "SELECT 1" ASCII
✓ 因长度 < 50 字节,直接透传
(3) 大包拆分场景
原始包长度 16MB - 2 字节时:
- 原始长度:0xFFFFFE (16,777,214 字节)
- 添加 4 字节包头 → 总长 0x1000002 (16,777,218 字节)
- 超过 3 字节表示范围 (0xFFFFFF) → 必须拆包
6. 核心实现逻辑
ini
c
复制
// 伪代码:压缩包构建
void build_compressed_packet() {
if (original_size < MIN_COMPRESS_LENGTH) {
header.uncompressed_len = 0; // 未压缩标志
payload = original_data; // 原始数据透传
} else {
header.uncompressed_len = original_size;
payload = zlib_compress(original_data); // DEFLATE 压缩
}
header.compressed_len = payload.size();
header.seq_id = compressed_seq++; // 独立序列号递增
}
7. 设计要点总结
特性 | 说明 |
---|---|
按需压缩 | MIN_COMPRESS_LENGTH 避免小包负压缩 |
协议兼容 | 未压缩包含完整 MySQL 包头 |
并行序列 | 压缩/普通包序列号独立计数 |
大包拆分 | 处理 16MB+ 数据边界 |
高效合并 | 单压缩包承载多协议包 |
注:实际实现参考 MySQL 源码
sql-common/compression.cc
的compress_packet()
函数