MySQL 压缩数据包详解

91 阅读3分钟

MySQL 压缩数据包详解

1. ​整体结构

MySQL 压缩数据包由两部分组成:

  • 压缩包头 (Compressed Packet Header)​​:固定 7 字节
  • 有效载荷 (Payload)​​:压缩或未压缩的实际数据
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) 多包合并压缩

plaintext
复制
原始包 1 (18 字节)       原始包 2 (25 字节)
       ↓                       ↓
      +---------------------------+
      |      合并压缩 (单个压缩包)     |
      +---------------------------+

优势:减少小包场景下的网络开销

​(2) 长度限制规避
当原始包长度 ≥ 16MB(2²⁴ - 1)时:

  • 压缩包头无法表示未压缩长度(仅 3 字节)
  • 强制拆分为多个压缩包发送

​(3) 序列号独立

  • 压缩包序列号与普通包序列号各自独立计数
  • 每个连接维护两套序列号

5. ​实战案例解析

​(1) 压缩示例:`SELECT "0123..." (46 字节)​

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 字节)​

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 字节时:

  1. 原始长度:0xFFFFFE (16,777,214 字节)
  2. 添加 4 字节包头 → 总长 0x1000002 (16,777,218 字节)
  3. 超过 3 字节表示范围 (0xFFFFFF)​​ → 必须拆包

6. ​核心实现逻辑

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() 函数