监听连接是 MySQL 服务的入口,其核心是 Connection_acceptor 模板类与 Mysqld_socket_listener 类协同工作的 connection_event_loop 事件循环。整个过程可分为初始化、循环监听与接受、分发处理三个阶段。
🚀 阶段一:网络初始化 (Network Initialization)
在 mysqld_main 函数完成一系列初始化后,会调用 network_init 来建立服务端的监听套接字(Socket)。
- 创建套接字 :
- 根据配置,为 TCP/IP 协议(默认3306端口)创建套接字,会优先尝试 IPv4,若失败则尝试 IPv6。
- 同时,也会为本地连接的 Unix Socket(类Unix系统)或 Windows Named Pipe 创建监听套接字。
- 绑定与监听 :
- 将套接字绑定(
bind)到指定的 IP 地址和端口。 - 调用
listen使套接字进入监听状态,等待客户端的连接请求。
- 将套接字绑定(
如果 MySQL 异常退出后快速重启,可能出现端口被旧进程占用导致绑定失败的情况。此时 MySQL 会进入循环等待,直到端口被释放或超过 --port-open-timeout 设定的超时时间。
🔁 阶段二:事件循环与连接接受
完成初始化后,程序会进入由 Connection_acceptor 管理的核心事件循环 connection_event_loop,其核心任务是循环监听并接受新连接。
-
事件循环机制:
- 在循环中,会不断调用
Mysqld_socket_listener对象的listen_for_connection_event方法。 - 该方法底层使用
poll或select系统调用来监听套接字上的 I/O 事件。MySQL 选择poll/select而非epoll,主要是为了跨平台的可移植性。
- 在循环中,会不断调用
-
接受新连接:
- 当
poll/select检测到监听套接字上有新连接到达时,listen_for_connection_event会调用mysql_socket_accept接受连接。 - 成功接受后,会创建一个
Channel_info对象,它像一个"数据包",封装了本次连接的所有信息,如客户端的 socket 文件描述符、IP 和端口等。
- 当
⚙️ 阶段三:连接处理与分发
获取到封装了连接信息的 Channel_info 对象后,核心处理流程转向 Connection_handler_manager 类。
-
连接数检查 :
Connection_handler_manager会调用check_and_incr_conn_count检查当前连接数。若已达到max_connections限制,新连接会被拒绝(但会为root用户预留一个额外的连接通道用于紧急管理)。 -
分发与创建线程 :连接数检查通过后,管理器会调用
add_connection方法。这一步会分发到具体的Connection_handler实现。- 默认的
Per_thread_connection_handler会为每个新连接创建一个线程。为了优化性能,会优先尝试从thread_cache中复用空闲线程。 - 新线程(无论是新建还是复用)的入口函数是
handle_one_connection(或handle_connection)。这个线程随后会负责该连接整个生命周期的所有操作。
值得一提的是,MySQL 5.7 对此流程进行了优化,将
THD(线程描述符)、NET、VIO等会话对象的初始化工作交给了新创建的用户线程自身去完成,使得主线程能更专注于高效接受新连接。 - 默认的
🤝 连接线程的后续任务
新线程启动后,会执行一系列初始化,并与客户端完成握手认证:
- 初始化会话与网络对象 :线程入口函数会初始化
THD(线程描述符)、VIO(虚拟I/O)和NET等关键对象。其中VIO是对底层 Socket 的抽象,提供了vio_read和vio_write等回调函数。同时,连接 socket 会被设置为非阻塞模式 (O_NONBLOCK)。 - 握手与认证 :
- 发送握手包 :服务端向客户端发送一个包含挑战码(scramble) 的握手初始化包。
- 客户端响应:客户端使用挑战码加密密码,将加密后的密码连同用户名等信息打包发回服务端。
- 服务端验证:服务端解密并验证密码,检查用户账户和权限。
- 返回结果 :验证通过则返回
OK包,连接成功建立;否则返回ERROR包。
- 进入命令循环 :认证通过后,线程便进入
do_command循环,持续读取、解析并执行客户端发来的 SQL 命令。
💎 总结
MySQL 的监听连接机制,是一套经过精心设计的、清晰优雅的事件驱动模型。其核心流程可以概括为:
- 准备 :
mysqld_main启动后,通过network_init创建并配置好监听套接字。 - 循环 :进入
connection_event_loop,主线程通过poll/select高效地等待新连接。 - 接受 :当新连接到达,主线程将其接受,并封装为
Channel_info对象。 - 分发 :
Connection_handler_manager检查连接数限制后,将连接分发出去。 - 处理:通常由独立的线程接管连接,完成握手认证,最后进入 SQL 命令的处理循环。
这一系列流程,共同构成了 MySQL 能够处理成千上万并发连接的基础。
java
/**
连接接受器循环以接受来⾃客户端的连接
Connection acceptor loop to accept connections from clients.
*/
void connection_event_loop() {
Connection_handler_manager *mgr =
Connection_handler_manager::get_instance();
while (!connection_events_loop_aborted()) {
// 监听连接事件
Channel_info *channel_info = m_listener-
>listen_for_connection_event();
// 处理⼀个新的连接
if (channel_info != nullptr) mgr-
>process_new_connection(channel_info);
}