mysql接收到SQL后第一步做什么_mysql执行入口分析

4次阅读

mysql接收到SQL后的入口函数是do_command(),它位于sql/sql_parse.cc,负责初始化语句上下文、读取完整packet、识别命令类型并派发处理。

mysql接收到SQL后第一步做什么_mysql执行入口分析

MySQL 接收到 SQL 后先走哪个入口函数?

MySQL 服务端接收到客户端发来的 SQL 字符串后,第一步不是解析,也不是优化,而是进入网络层的事件分发循环,最终调用 do_command() —— 这是 SQL 执行链路的真正入口函数(在经典线程模型下)。它位于 sql/sql_parse.cc,负责初始化语句上下文、读取完整 packet、识别命令类型(如 COM_QUERY),并派发到对应处理函数(比如 dispatch_command())。

注意:如果你用的是线程池(Thread pool)或 X Protocol(mysqlx),入口会不同:handle_connection()mysqlx::Connection::execute_statement(),但传统连接下 do_command() 是绕不开的第一站。

为什么不是直接 parse_sql()?

因为 MySQL 在真正解析前必须完成几件关键前置事:

  • do_command() 要先检查连接状态、用户权限、字符集设置、事务状态(是否在 autocommit 模式)
  • 要从 socket 缓冲区中完整读取一个 COM_QUERY 包(含长度头 + SQL 字符串),避免粘包或截断
  • 要为这条语句分配临时 THD(Thread Handler)对象,并绑定当前线程与会话上下文
  • 只有这些就绪后,才会调用 dispatch_command()mysql_parse()parse_sql() 进入语法解析阶段

所以看到报错 Error 1045 (28000): access deniedERROR 1118 (42000): Row size too large,它们都发生在 mysql_parse() 之前 —— 说明连入口都没进完。

如何验证这个入口流程?

最轻量的方式是加 GDB 断点:

gdb -p $(pidof mysqld) (gdb) b do_command (gdb) c

然后用 mysql -e "select 1" 触发,你会停在 do_command() 开头。继续 s 单步,就能看到它怎么读 packet、怎么调 dispatch_command()、怎么把 query_stringthd->packet 拷贝到 thd->query_string

注意:调试时确保已关闭 query cache(query_cache_type=0),否则部分路径会被绕过;另外 8.0+ 中部分逻辑被拆到 sql/sql_cmd.cpp,但入口仍统一收口在 do_command()

容易忽略的细节:SQL 还没到 parser 就可能被拦截

很多 dba 认为“SQL 一来就 parse”,其实中间有多个拦截点:

  • 如果启用了 init_connect,会在 do_command() 后立即执行该语句(在用户 SQL 前)
  • 如果设置了 max_connect_errors 且客户端 IP 已超限,连接会在 do_command() 前被拒绝(见 check_connection()
  • 如果 SQL 长度超过 max_allowed_packet,错误发生在 my_net_read() 阶段,根本进不到 do_command()
  • ssl/TLS 握手失败、认证插件(如 caching_sha2_password)校验不通过,也都卡在更早的网络/协议层

所以当遇到“SQL 没反应”或“连接直接断开”,别急着查慢日志或 explain,先确认是不是卡在了 do_command() 之前的环节。

text=ZqhQzanResources