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

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 denied 或 ERROR 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_string 从 thd->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() 之前的环节。










