mysql中一条查询语句是如何执行的?
MySQL 中一条查询语句(如 SELECT
)的执行是一个分层协作的过程,涉及多个核心组件。以下是详细执行流程(以 SELECT * FROM users WHERE id = 10;
为例):
1. 连接建立与认证
客户端发起请求:通过 MySQL 协议(如 TCP/IP)连接服务器。
连接层处理:
验证用户名、密码、主机权限。
分配线程(或从线程池复用)处理该连接。
若启用
wait_timeout
,闲置连接会被自动关闭。
2. 查询缓存(仅 MySQL 5.7 及之前)
检查缓存:计算查询语句的哈希值,查询缓存(Key-Value 结构)。
若命中:直接返回结果(权限验证后)。
若未命中/MySQL 8.0+:跳过此步骤(8.0 已移除该功能)。
3. 解析与预处理
解析器(Parser):
词法分析:拆分 SQL 为 tokens(如
SELECT
、*
、FROM
、users
)。语法分析:构建语法树,检查是否符合 SQL 语法规则(如关键词顺序)。
预处理器:
检查表和列是否存在。
解析
*
为所有列名。验证权限(用户是否有权访问
users
表)。
4. 查询优化器
核心任务:生成成本最低的执行计划。
优化过程:
全表扫描:读取所有行(成本高)。
使用索引:通过 B+ 树定位
id=10
(成本低)。分析可用索引(如
PRIMARY KEY(id)
)。估算不同执行方式的成本:
选择索引并确定
JOIN
顺序(若涉及多表)。输出:生成执行计划树(可通过
EXPLAIN
查看)。
5. 执行器
调度执行:
根据优化器的计划调用存储引擎接口。
打开表,检查执行权限。
执行流程:
执行器 -> 存储引擎: “读取 users 表 id=10 的行” 存储引擎 -> 执行器: 返回符合条件的行
6. 存储引擎处理
InnoDB 引擎为例:
检查
id=10
是否在 Buffer Pool(内存缓存)中:若存在:直接返回数据。
若不存在:从磁盘
.ibd
文件读取页到 Buffer Pool。通过索引查找:
使用主键索引(B+ 树)定位
id=10
的数据页位置。若涉及二级索引,需回表查询主键索引。
加锁机制:
默认 RR 隔离级别:对读取行加临键锁(Next-Key Lock)防止幻读。
若为读提交(RC):加行锁。
7. 结果返回
执行器处理结果:
将存储引擎返回的行数据组合成结果集。
若启用慢查询日志且超阈值,记录该查询。
返回客户端:
通过连接线程的输出缓冲区逐步发送结果。
客户端接收并显示数据(如命令行或应用程序)。
性能关键点
连接管理:线程创建/销毁开销大,建议用连接池(如 HikariCP)。
优化器决策:索引失效或统计信息不准会导致全表扫描(需定期
ANALYZE TABLE
)。Buffer Pool:命中率低时磁盘 I/O 压力大(可调整
innodb_buffer_pool_size
)。网络传输:避免
SELECT *
,只取必要列减少数据传输量。锁竞争:长时间未提交的事务会阻塞查询(监控
Innodb_row_lock_waits
)。
调试工具
EXPLAIN [SQL]
:查看执行计划。SHOW PROFILES;
:分析各阶段耗时。慢查询日志:定位性能瓶颈。