MySQL执行SELECT经历解析、查缓存(旧版)、权限校验、生成执行计划、存储引擎取数五阶段;type=ALL不等于未用索引,可能因索引失效或优化器放弃;查询缓存8.0已移除,5.7需禁用;InnoDB与MyISAM在锁机制、COUNT(*)、排序实现上差异显著。

MySQL 执行 SELECT 语句时,会经过哪些核心阶段
MySQL 并不是拿到 SELECT 就直接去磁盘扫数据。它先走一套逻辑处理流程:先解析语法、再查缓存(如果启用)、接着做权限校验、然后生成执行计划,最后才真正访问存储引擎读取数据。整个过程里,SELECT 的写法、索引是否存在、统计信息是否准确,都会直接影响“生成执行计划”这一步的结果——而这一环出问题,就容易导致慢查询。
为什么 EXPLAIN 显示 type=ALL 却还在用索引
这是常见误解。type=ALL 表示全表扫描,但不代表完全没用索引。比如你写了 SELECT * FROM t WHERE a > 1 AND b = 2,而只有 (a) 单列索引,优化器可能评估后发现仍需回表+过滤大量行,最终放弃使用该索引,退化为全表扫描。也可能是因为 WHERE 条件中用了函数或隐式类型转换,导致索引失效:
-
WHERE DATE(create_time) = '2024-01-01'→ 无法走create_time索引 -
WHERE user_id = '123'(user_id 是 INT)→ 触发隐式转换,可能丢索引 -
WHERE status != 1→ 范围不明确,通常不走索引
查询缓存(query cache)在 MySQL 8.0 中已移除,但旧版本要注意什么
MySQL 5.7 及以前,如果启用了 query_cache_type = ON,相同 SQL 文本(注意:是**完全一致的字符串**,包括空格、大小写、注释)且结果未过期,会直接返回缓存结果。但它有严重副作用:
- 任何对表的
INSERT/UPDATE/DELETE都会让该表所有相关缓存失效 - 缓存键区分大小写和空格,
select * from t和SELECT * FROM t是两个缓存项 - 一旦表更新频繁,缓存命中率极低,反而增加锁竞争开销
所以即使在 5.7,也建议设为 query_cache_type = OFF,而不是依赖它加速查询。
存储引擎层实际读取数据时,InnoDB 和 MyISAM 差异在哪
MySQL Server 层只负责生成执行计划,真正取数据由存储引擎完成。这对 SELECT 行为影响很大:
- InnoDB 每次读都加
READ COMMITTED或更低隔离级别的行级锁(即使是只读),并检查 MVCC 版本链;MyISAM 完全无事务,读操作不加锁,但并发写时会锁整张表 - InnoDB 必须通过主键或二级索引定位,即使
SELECT COUNT(*)也要遍历索引树;MyISAM 把行数存在表头,COUNT(*)极快 - InnoDB 的
ORDER BY如果无法利用索引排序,会在内存或磁盘做 filesort;MyISAM 同样要 filesort,但临时文件位置和缓冲区行为不同
这些差异意味着:同样的 SQL,在两种引擎下执行时间、锁行为、内存占用可能完全不同。别只看 Server 层的执行计划,得结合引擎特性判断真实开销。










