mysql连接生命周期-命令阶段(Prepared Statements)

132 阅读15分钟

COM_STMT_PREPARE​

 MySQL 协议中用于创建预处理语句 (Prepared Statement)​​ 的命令 COM_STMT_PREPARE。以下是关键点的解释:

  1. ​目的与功能 (COM_STMT_PREPARE)​​:

    • 客户端向服务器发送此命令,请求将一个 SQL 查询字符串(包含占位符 ?)​预编译
    • 服务器解析 SQL,确定参数个数、返回结果的列数等信息,分配一个唯一的 statement_id 并缓存编译后的语句结构。
    • 后续可以使用 COM_STMT_EXECUTE 命令(配合 statement_id 和具体的参数值)高效地多次执行该语句。
  2. ​客户端请求包格式​:

    • 命令标识符 (command)​: 1字节,固定值 0x16 (即 COM_STMT_PREPARE)。

    • 查询字符串 (query)​: 一个以 EOF (End-Of-File) 方式编码的字符串。这意味着字符串长度由数据包剩余部分决定,没有显式的长度前缀。内容就是 SQL 语句本身,其中参数用 ? 标记。

    • 示例请求 (SELECT CONCAT(?, ?) AS col1)​:

      1c 00 00 00  // 包长度 (小端序: 0x00 00 00 1c = 28字节),包序列号 (0x00 00 00)
      16           // COM_STMT_PREPARE 命令码 (0x16)
      53 45 4c 45 43 54 20 43 4f 4e 43 41 54 28 3f 2c 20 3f 29 20 41 53 20 63 6f 6c 31 // "SELECT CONCAT(?, ?) AS col1"
      
  3. ​服务器响应包 (COM_STMT_PREPARE_OK)​​:

    • 状态 (status)​: 1字节,成功时为 0x00
    • 语句 ID (statement_id)​: 4字节整数。服务器分配的标识符,后续 COM_STMT_EXECUTE 等命令使用它来引用这个预编译语句。
    • 列数 (num_columns)​: 2字节整数。表示查询预期返回的结果集的列数。对于 INSERT/UPDATE/DELETE 等非查询语句,通常为 0。
    • 参数个数 (num_params)​: 2字节整数。表示查询中占位符 ? 的个数。
    • 保留位 / 填充 (reserved_1)​: 1字节,文档中标记为 [00] filler,常为 0x00
    • 警告计数 (warning_count)​: (可选) 如果包长大于 12 字节,包含此 2 字节的警告数量。
    • 元数据标志 (metadata_follows)​: (可选) 仅当客户端具备 CLIENT_OPTIONAL_RESULTSET_METADATA 能力时存在。1字节标志,指示是否跳过元数据发送。见 enum_resultset_metadata (文档未详述,通常 RESULTSET_METADATA_FULL=0 表示发送完整元数据)。
  4. ​参数元数据块​:

    • 条件: 如果 num_params > 0并且​ (CLIENT_OPTIONAL_RESULTSET_METADATA 未设置 ​metadata_follows == RESULTSET_METADATA_FULL),服务器会紧接着发送 num_params 个 ​Column Definition​ 数据包。
    • 内容: 每个 ​Column Definition​ 包详细描述一个参数的类型、字符集等信息。格式与 COM_QUERY 结果集的列描述相同。
    • 结束标记 (EOF)​: 在 num_params 个描述包之后,如果客户端不具备CLIENT_DEPRECATE_EOF 能力,服务器会发送一个 ​EOF_Packet​ (0xFE) 标记参数元数据块的结束。
  5. ​列元数据块​:

    • 条件: 如果 num_columns > 0并且​ (CLIENT_OPTIONAL_RESULTSET_METADATA 未设置 ​metadata_follows == RESULTSET_METADATA_FULL),服务器会在参数元数据块(如果存在)之后发送 num_columns 个 ​Column Definition​ 数据包。
    • 内容: 每个包描述结果集中一列的类型、名称、字符集等信息。
    • 结束标记 (EOF)​: 在 num_columns 个描述包之后,如果客户端不具备CLIENT_DEPRECATE_EOF 能力,服务器会发送一个 ​EOF_Packet​ (0xFE) 标记列元数据块的结束。
  6. ​示例响应分析​:

    • 示例 1 (SELECT CONCAT(?, ?) AS col1)​:
      • 0c 00 00 01 00 01 00 00 00 01 00 02 00 00 00 00:
        • 0c 00 00: 包长12字节,序列号1 (01 00 00 小端序为 0x000001)
        • 00: status (OK)
        • 01 00 00 00: statement_id=1
        • 01 00: num_columns=1 (结果集有1列 col1)
        • 02 00: num_params=2 (2个参数 ?)
        • 00: reserved_1 (填充)
      • 后续包: 发送了 2 个参数描述包 (17 00 00 02 ...17 00 00 03 ...),一个 EOF_Packet (05 00 00 04 fe 00 00 02 00), 1 个列描述包 (1a 00 00 05 ...), 再一个 EOF_Packet (05 00 00 06 fe 00 00 02 00)。因为 num_params=2>0num_columns=1>0,所以发送了完整的参数和列元数据。
    • 示例 2 (DO 1)​:
      • 0c 00 00 01 00 01 00 00 00 00 00 00 00 00 00 00:
        • 0c 00 00: 包长12字节,序列号1
        • 00: status (OK)
        • 01 00 00 00: statement_id=1
        • 00 00: num_columns=0 (DO 1 无结果集)
        • 00 00: num_params=0 (无参数)
        • 00: reserved_1
        • 末尾 00 00 00 00: 可能是对齐的填充字节。因为没有参数和列,服务器不再发送任何元数据包或 EOF_Packet

总结: COM_STMT_PREPARE 是 MySQL 客户端/服务器协议中用于创建预处理语句的核心命令。客户端发送 SQL 字符串(带 ?),服务器响应一个状态包 (COM_STMT_PREPARE_OK),包含分配的 statement_id、参数个数和结果集列数。根据条件(参数/列数、客户端能力标志),服务器可能紧接着发送详细的参数元数据描述包和列元数据描述包,并用 EOF_Packet(除非客户端支持 CLIENT_DEPRECATE_EOF)分隔这些元数据块或标记其结束。这个机制是高效执行参数化查询的基础。

MySQL COM_STMT_EXECUTE 协议详解

​作用​

COM_STMT_EXECUTE 是 MySQL 客户端/服务器协议中的命令,用于执行一个已预编译的 SQL 语句(Prepared Statement)​。它通过 statement_id 指定目标语句,并传递占位符(如 ?)的实际参数值(二进制格式),支持匿名参数和命名查询属性。

数据包结构(按传输顺序)

1. ​基础头部​

字段

类型

描述

status

1字节

固定值 0x17(标识 COM_STMT_EXECUTE

statement_id

4字节

预编译语句的唯一 ID(由 COM_STMT_PREPARE 返回)

flags

1字节

标志位(如游标类型,参见 enum_cursor_type

iteration_count

4字节

执行次数(当前版本固定为 1

2. ​参数处理部分​

当满足以下条件时出现:

  • 预编译语句有参数(num_params > 0
  • ​ 客户端支持查询属性(CLIENT_QUERY_ATTRIBUTES)且标志包含 PARAMETER_COUNT_AVAILABLE

子字段​:

  • ​**parameter_count**​(变长整数)

    • 仅当 CLIENT_QUERY_ATTRIBUTES 启用时存在。
    • 作用:覆盖预编译时的参数数量(num_params),允许额外传递查询属性。
    • 计算:总参数数 = 原始参数数 + 额外属性数
  • ​**null_bitmap**​(变长二进制)

    • NULL 值位图,长度为 (parameter_count + 7) / 8 字节。
    • 每位表示对应参数是否为 NULL(1 = NULL)。
  • ​**new_params_bind_flag**​(1字节)

    • 是否重新绑定参数类型(1 = 需要重新绑定)。
  • 参数元数据​(当 new_params_bind_flag = 1 时)
    对每个参数循环发送:

    字段

    类型

    描述

    parameter_type

    2字节

    参数类型(如 intvarchar,参见 enum_field_type

    parameter_name

    变长字符串

    仅当 CLIENT_QUERY_ATTRIBUTES 启用时存在;参数名(无名则为空)

  • ​**parameter_values**​(变长二进制)

    • 所有参数的实际值(按顺序以二进制编码存储)。

关键行为说明

  1. 参数匹配

    • num_params(预编译时定义的数量)个参数用于填充 SQL 中的匿名占位符(?)。
    • 多余参数(num_remaining_attrs)作为查询属性存储到线程描述符(THD)中。
  2. 命名参数处理

    • 若参数有名称(通过 parameter_name 传递),可后续作为查询属性访问(如 SELECT @@attribute_name)。
    • 注意​:即使匿名占位符的参数有名称,也优先按位置匹配,名称仅作为额外属性。
  3. 覆盖参数数量

    • parameter_count 可覆盖原始 num_params,使 总参数数 = num_params + num_remaining_attrs
    • 示例:预编译时 num_params=2,但执行时发送 parameter_count=5,则:
      • 前 2 个参数填充 SQL 占位符。
      • 后 3 个参数转为查询属性。

示例数据解析

原始十六进制:0f 00 03 66 6f 6f ...

  • 0fCOM_STMT_EXECUTE 命令头。
  • 00 03:小端存储的 statement_id=3
  • 66 6f 6f:ASCII 字符 "foo",可能为参数名或字符串值。

关联函数

  • 客户端​:mysql_stmt_execute()(C API 函数)。
  • 服务器​:mysqld_stmt_execute()(处理执行逻辑)。
  • 预编译过程:COM_STMT_PREPARE(创建预编译语句)。

总结

COM_STMT_EXECUTE 是高效执行参数化查询的核心协议,通过二进制传输减少解析开销,并扩展支持命名查询属性。其设计兼顾了性能(二进制编码)与灵活性(覆盖参数、命名属性),适用于高频查询场景(如事务处理)。

​COM_STMT_FETCH 

这是 MySQL 客户端/服务器协议中的一个 ​命令类型(Command Type)​,用于从预编译语句(Prepared Statement)的结果集中 ​分批获取数据行

​主要用途​

当预编译语句(如 SELECT)返回大量数据时,客户端通过此命令分批获取数据,避免一次性传输全部结果导致内存耗尽或网络阻塞。

​命令格式(客户端 -> 服务器)​​

字段

类型

说明

​**status**​

int<1>

固定值 0x1C(十进制 28),标识这是 COM_STMT_FETCH 命令。

​**statement_id**​

int<4>

预编译语句的唯一 ID(由 COM_STMT_PREPARE 返回)。

​**num_rows**​

int<4>

本次请求获取的最大行数(例如 100 行)。

​服务器响应​

服务器可能返回以下三种响应之一:

  1. 结果集数据包 (Multi-Resultset)​
    • 包含实际请求的行数据(二进制格式)。
    • 如果还有更多数据,客户端需再次发送 COM_STMT_FETCH 获取下一批。
  2. 空结果集
    • 表示所有数据已获取完毕(无更多行)。
  3. 错误包 (ERR_Packet)​
    • 包含错误信息(如无效语句 ID 或结果集已关闭)。

​相关开发接口​

  • C API: mysql_stmt_fetch() 函数内部使用此协议命令。
  • 服务器实现: mysqld_stmt_fetch 函数处理此命令。

​关键注意事项​

  1. 必须先执行预编译语句
    需先通过 COM_STMT_EXECUTE 执行语句生成结果集,才能用 COM_STMT_FETCH 获取数据。
  2. 结果集游标
    每次调用会隐式移动结果集的游标位置。
  3. 资源释放
    获取完成后需用 COM_STMT_CLOSE 释放服务器资源。

​典型流程​

sequenceDiagram
    Client->>Server: COM_STMT_PREPARE (创建预编译语句)
    Server-->>Client: 返回 statement_id
    Client->>Server: COM_STMT_EXECUTE (执行语句)
    Server-->>Client: 返回结果集元数据
    loop 分批获取数据
        Client->>Server: COM_STMT_FETCH(statement_id, num_rows)
        Server-->>Client: 返回实际数据行
    end
    Client->>Server: COM_STMT_CLOSE (释放资源)

​总结​

COM_STMT_FETCH 是 MySQL 高效处理大结果集的核心协议命令,通过分批传输避免资源瓶颈。理解其工作流程对开发高性能数据库应用至关重要,尤其在需要流式处理查询结果的场景中(如数据导出或实时分析)。

COM_STMT_CLOSE

​核心功能:​​

COM_STMT_CLOSE 命令用于 ​关闭(释放)一个服务端已准备好的预处理语句 (Prepared Statement)​

关键信息解释:​

  1. ​目的:​​

    • 当客户端不再需要某个之前准备好的预处理语句时(例如,执行完毕或不再使用),它会发送这个命令。
    • 这个命令告诉 MySQL 服务器:“请释放与这个语句 ID 关联的所有资源(如内存、内部结构等)”。
  2. ​无响应包:​​

    • 最重要的特点之一:​​ 服务器在处理 COM_STMT_CLOSE 命令后,​不会​ 向客户端发送任何确认或响应包。
    • 原因:​​ 这是一个纯粹的清理操作。客户端发出命令后,就假设服务器会执行释放操作。即使命令失败(例如,指定的 statement_id 无效),服务器也不会通知客户端。这种设计是为了最小化通信开销,因为客户端通常在确定不再需要该语句时才发送此命令。
    • 对客户端的影响:​​ 客户端实现必须设计为在发送 COM_STMT_CLOSE 后,​不再​ 尝试使用这个 statement_id(例如执行 COM_STMT_EXECUTE 或再次关闭它),因为它可能已经被服务器释放了。客户端应该立即将该 statement_id 标记为无效。
  3. ​Payload (命令负载) 结构:​​

    这个命令发送给服务器的数据包包含两个部分:

    • ​**status (状态) [1字节, int<1>]:​**​
      • 值固定为 ​**0x19**​ (十进制 25)。
      • 这是 COM_STMT_CLOSE 命令的标识符。服务器通过读取这个字节就知道客户端要执行关闭预处理语句的操作。
    • ​**statement_id (语句ID) [4字节, int<4>]:​**​
      • 一个 4 字节的整数。
      • 唯一标识​ 客户端想要关闭的那个预处理语句。
      • 这个 ID 是之前客户端发送 COM_STMT_PREPARE 命令时,服务器在响应包中返回给客户端的。客户端在后续执行 (COM_STMT_EXECUTE) 或关闭 (COM_STMT_CLOSE) 该语句时都需要提供这个 ID。
  4. ​关联函数 (See also):​​

    • ​**mysql_stmt_close (C API):​**​ 这是 MySQL C 客户端库中用于关闭预处理语句的标准函数。应用程序调用此函数,它会负责构造并发送 COM_STMT_CLOSE 协议命令给服务器。
    • ​**mysql_stmt_prepare (C API):​**​ 用于创建预处理语句的函数,它会发送 COM_STMT_PREPARE 命令并接收包含 statement_id 的响应。
    • ​**mysqld_stmt_close (Server Internals):​**​ 这很可能是 MySQL 服务器内部处理 COM_STMT_CLOSE 命令的函数。
    • ​**mysql_stmt_precheck (Server Internals):​**​ 可能是服务器在执行 mysql_stmt_prepare 或相关操作前进行的一些内部检查函数。
  5. ​示例 (Example):​​

    • 文档指出“See also”,意味着没有在此处提供具体的网络包字节流示例。理解 COM_STMT_CLOSE 的用法通常是通过理解客户端 API 函数 (mysql_stmt_close) 的行为来进行的。当你调用 mysql_stmt_close(MYSQL_STMT *stmt) 时,底层会发生:
      1. 客户端库从 stmt 对象中取出关联的 statement_id
      2. 客户端库构造一个包含 0x19statement_id 的小数据包。
      3. 客户端库将这个包发送给服务器。
      4. 客户端库将 stmt 对象标记为无效(不再使用)。
      5. 客户端库 ​不会​ 等待或期望服务器的任何回复。
  6. ​文档来源 (Generated by):​​

    • Generated by 1.9.2 表示这个文档片段是由某个版本号为 1.9.2 的工具自动生成的(可能是 Doxygen 或类似工具)。

总结:​

COM_STMT_CLOSE 是 MySQL 客户端/服务器协议中一个简单的、用于资源清理的命令。它的唯一作用就是让服务器释放指定的预处理语句资源。客户端通过发送包含固定标识符 0x19 和目标语句 statement_id 的包来发起请求。服务器执行释放操作后不发送任何响应。客户端在发送此命令后,必须立即放弃使用该 statement_id。这个命令通常由客户端库的 mysql_stmt_close() 函数触发。

COM_STMT_RESET

它用于重置服务器端预处理语句的状态。

核心解释:​

  1. 作用:​COM_STMT_RESET 命令主要有两个核心作用:

    • 清除累积的数据:​​ 重置(清除)之前通过 COM_STMT_SEND_LONG_DATA 命令为预处理语句的参数(通常是 BLOBTEXT 等长数据类型)发送到服务器的所有数据块。
    • 关闭游标:​​ 如果该预处理语句之前通过 COM_STMT_EXECUTE 命令执行并打开了结果集的游标(例如,执行 SELECT 语句时),那么 COM_STMT_RESET 会关闭这个游标。
  2. 目的:​​ 此命令用于将预处理语句恢复到执行 COM_STMT_PREPARE 之后、第一次绑定参数和/或执行之前的状态。它允许你在重用同一个 statement_id 执行新查询之前,清理掉旧的、可能很大的参数数据和释放游标资源。

  3. 协议包结构:​

    • Payload Type:​​ 固定部分。
    • ​**status (1字节):​​ 标识命令类型的字节。对于 COM_STMT_RESET,这个值始终是 ​0x1A**​ (26 的十六进制)。
    • ​**statement_id (4字节):​**​ 要重置的预处理语句的唯一标识符 (ID)。这个 ID 是在服务器响应客户端的 COM_STMT_PREPARE 请求时分配的。
  4. 示例:​

    • 字节序列:05 00 00 00 1a 01 00 00 00
    • 分解:​
      • 05 00 00 00:包长度 (5 字节)。(小端字节序:05 00 00 00 = 5)
      • 1a:命令字节 (COM_STMT_RESET)。
      • 01 00 00 00statement_id (1)。(小端字节序:01 00 00 00 = 1)
    • 含义:​​ 这个包告诉 MySQL 服务器:请重置 statement_id 为 1 的预处理语句(清除其累积的长数据参数并关闭其游标)。
  5. 相关函数/概念:​

    • ​**mysql_stmt_reset() (C API):​**​ 这是 MySQL C API 中客户端调用的函数,它最终会触发服务器端的 COM_STMT_RESET 命令。应用程序开发者主要使用这个 API 函数。
    • ​**mysqld_stmt_reset():​**​ 这很可能是服务器内部处理 COM_STMT_RESET 命令的函数(文档提到它)。
    • ​**mysql_stmt_precheck():​**​ 可能是一个内部检查函数。
    • ​**COM_STMT_PREPARE:​**​ 创建预处理语句的命令。
    • ​**COM_STMT_EXECUTE:​**​ 执行预处理语句(并可能打开游标)的命令。
    • ​**COM_STMT_SEND_LONG_DATA:​**​ 为预处理语句参数发送长数据的命令。
    • ​**COM_STMT_FETCH:​**​ 从未关闭的游标中获取更多结果行的命令(在 COM_STMT_RESET 关闭游标后就不能再用了)。

总结:​

COM_STMT_RESET 是一个底层网络协议命令,用于:

  1. 清除​ 之前通过 COM_STMT_SEND_LONG_DATA 发送到特定预处理语句 (statement_id) 的所有参数数据(主要是长数据)。
  2. 关闭​ 该预处理语句通过 COM_STMT_EXECUTE 可能打开的游标(释放服务器资源)。
  3. 将其状态重置回准备完成但尚未绑定参数/执行的状态,为语句的重用做准备。

应用程序开发者通常通过 C API 的 mysql_stmt_reset() 函数来间接使用这个功能,而不是直接操作协议包。

COM_STMT_SEND_LONG_DATA

以下是核心信息的清晰解释:

  1. ​目的:

    • 用于在Prepared Statement(预处理语句)​​ 执行过程中,向服务器发送非常长参数数据​(例如 BLOBTEXT 类型)。
    • 它允许客户端将大块参数数据分片发送给服务器。
    • 对同一个参数重复调用此命令,会将新数据追加到该参数已接收数据的末尾
  2. ​何时使用:

    • 必须在执行预处理语句的命令 ​**COM_STMT_EXECUTE​ ​之前**发送。
    • 通常用于处理单个参数值特别大的情况,无法一次性通过 COM_STMT_EXECUTE 的常规参数包发送。
  3. ​数据包结构​ (Payload):

    • status [0x18] (1字节): 固定值 0x18,标识这是 COM_STMT_SEND_LONG_DATA 命令。
    • statement_id (4字节): 目标预处理语句的唯一标识符(ID)。
    • param_id (2字节): 要发送数据的参数序号​(从 0 开始)。
    • data (变长二进制): 要发送的实际数据块。
  4. ​服务器响应:

    • 没有响应​ (No response is sent back to the client)。
    • 客户端发送此命令后,服务器只是接收并存储(或追加)数据,不会发回任何确认包。客户端接着发送 COM_STMT_EXECUTE 来真正执行语句。
  5. ​总结:

    COM_STMT_SEND_LONG_DATA 是 MySQL 协议中用于高效传输预处理语句中超大参数数据的专用命令。它通过分片传输追加机制处理大块数据,并且必须在语句执行命令之前调用,服务器不会对此命令本身进行回复。

简单流程示例​:

  1. 客户端:COM_STMT_PREPARE -> (服务器返回 statement_id)
  2. 客户端 (对于大参数 X):
    • COM_STMT_SEND_LONG_DATA (statement_id, param_id=X, data_chunk1)
    • COM_STMT_SEND_LONG_DATA (statement_id, param_id=X, data_chunk2)
    • ... (可选,多次发送)
  3. 客户端:COM_STMT_EXECUTE (statement_id, 普通参数包) -> (服务器执行并返回结果)
  4. (可选) 客户端:COM_STMT_CLOSE (statement_id)