mysql server层不使用多路复用io,采用每个连接一个线程的阻塞式模型;innodb存储引擎层支持原生异步磁盘io(aio),但与网络io无关。

MySQL 用的是多路复用 IO 吗?
不用。MySQL Server 层(即你连上的 mysqld 进程)本身不直接使用 epoll、kqueue 或 select 做网络连接的多路复用。它默认采用「每个连接一个线程」(thread-per-connection)模型,由操作系统线程调度处理 socket 读写——底层 socket 是阻塞式(blocking)的,不是靠单线程 + 多路复用驱动的。
这意味着:你不会在 MySQL 源码里看到主循环调用 epoll_wait();也不会因为连接数暴涨就自动切到事件驱动模型。它的并发扩展性瓶颈,恰恰就卡在这个线程模型上。
那为什么有人说 MySQL 支持异步 IO?
这是指 InnoDB 存储引擎层的 AIO(Asynchronous I/O),仅用于磁盘文件读写(比如 ibdata1、ib_logfile0、表空间文件),和网络 IO 完全无关。
关键区别:
- 网络 IO:mysqld 主线程/工作线程用
recv()/send()阻塞调用,无多路复用 - 磁盘 IO:InnoDB 后台线程(如
io_thread)可配置为native AIO(Linux 上走io_submit()/io_getevents()),但需满足:innodb_use_native_aio = ON,且文件系统支持(ext4/xfs OK,某些挂载选项或容器环境可能禁用) - 若
innodb_use_native_aio = OFF,InnoDB 会退化为同步 IO + 线程池模拟异步,性能明显下降
想真用多路复用?得绕开 mysqld 自己干
如果你的应用需要高并发低延迟的 MySQL 访问,又不想被线程数压垮,常见做法是:把连接管理从 MySQL Server 层抽出来,在应用侧或中间件层做多路复用。
典型场景:
- Go 应用用
database/sql+ 连接池(SetMaxOpenConns),底层net.Conn虽是阻塞的,但 goroutine 调度成本远低于 OS 线程,效果接近事件驱动 - Node.js 用
mysql2配合连接池,所有 IO 通过 libuv 的epoll统一调度 - Proxy 层如
MySQL Router、ProxySQL、MaxScale会自己实现非阻塞网络栈,对后端 MySQL 仍发普通连接,但对外暴露复用连接能力
注意:mysql -h 命令行、PHP mysqli 扩展、Python mysqlclient 默认都是同步阻塞调用,不自带多路复用。
查证 MySQL 实际 IO 行为的几个命令
别只看文档,用这些直接看运行时行为:
- 查当前连接模型:
SHOW VARIABLES LIKE 'thread_handling';—— 返回one-thread-per-connection就是默认模型 - 查 InnoDB 是否启用了原生 AIO:
SHOW VARIABLES LIKE 'innodb_use_native_aio';和SHOW ENGINE INNODB STATUS\G里找AIO相关统计 - 看系统级线程数:
ps -T -p $(pgrep mysqld) | wc -l,通常 ≈ 当前连接数 + 后台线程数(约 10–20 个) - 抓包验证网络模型:
strace -p $(pgrep mysqld) -e trace=epoll_wait,select,poll—— 几乎不会看到输出,说明没用多路复用
真正影响性能的,往往是磁盘 AIO 配置是否生效、连接池大小是否合理、以及网络层是否被代理或负载均衡器悄悄做了复用——这些比纠结“MySQL 有没有 epoll”更值得花时间确认。










