MySQL执行SELECT语句的典型流程是:连接→查询缓存(8.0已移除)→解析→预处理→优化器→执行器→存储引擎层→返回结果,关键性能环节在优化器和存储引擎层。
MySQL 执行一条 SELECT 语句,不是直接去磁盘查数据,而是走一套有明确顺序的内部流程。理解这个流程,才能知道优化该从哪下手。
典型流程(以 InnoDB 为例)是:连接 → 查询缓存(8.0 已移除)→ 解析 → 预处理 → 优化器 → 执行器 → 存储引擎层 → 返回结果。其中真正影响性能的关键环节集中在「优化器」和「存储引擎层」。
query_cache_type,不用再纠结是否开启EXPLAIN 输出的 type 字段(如 ALL、range、ref)反映的是优化器选择的访问类型,不是 SQL 写得“对不对”,而是“它认为怎么最快”ha_innobase::index_read()),真正的 I/O、加锁、MVCC 判断都在 InnoDB 层完成常见误解是“建了索引就一定走”,其实优化器会基于统计信息、索引选择性、WHERE 条件写法、隐式类型转换等综合判断。哪怕索引存在,也可能被跳过。
WHERE YEAR(create_time) = 2025 → 无法使用 create_time 上的普通 B+ 树索引WHERE user_id = '123'(user_id 是 INT)→ MySQL 转成数字比较,但可能导致索引失效(尤其在字符集不同或 collation 不一致时)INDEX (a, b, c),但查询是 WHERE b = 2 AND c = 3 → 完全用不上该索引ALL,这不是 bug,是成本计算结果当 EXPLAIN 的 Extra 列出现 Using filesort,说明 MySQL 需要额外排序缓冲区,不是靠索引顺序直接返回结果。这在大数据量下极易拖慢响应。
ORDER BY 字段在索引中且顺序匹配,就能复用索引有序性。例如 INDEX (status, created_at),则 WHERE status = 1 ORDER BY created_at DESC 可免排序ORDER BY a ASC, b DESC 在 MySQL 8.0+ 才支持混合方向索引(INDEX (a ASC, b DESC)),低版本只认统一方向LIMIT 本身不触发 filesort,但和 ORDER BY 组合时,若没索引支撑,MySQL 仍需先排完所有匹配行再截断——所以 LIMIT 10 并不能让 ORDER BY 变快SELECT id, name FROM users WHERE status=1 ORDER BY created_at,可建 INDEX (status, created_at, id, name)
MySQL 的 JOIN 是嵌套循环(Nested Loop),没有真正意义上的哈希连接(8.0.18+ 才在特定条件下用 Hash Join,但默认仍倾向 NLJ)。谁当驱动表(outer table),决定了外层循环次数和整体代价。

ANALYZE TABLE 没及时运行)、或条件写法干扰判断(如子查询、OR 条件),会导致选错STRAIGHT_JOIN,但属高危操作——必须确认被驱动表有合适索引,否则性能雪崩WHERE is_vip = 1 只返回 10 行,则它是理想驱动表utf8mb4_general_ci vs utf8mb4_0900_as_cs)会导致无法使用索引,JOIN 变*表交叉SELECT STRAIGHT_JOIN u.name, o.total FROM users u JOIN orders o ON u.id = o.user_id WHERE u.is_vip = 1 AND o.status = 'paid';
真正难的不是写出快 SQL,而是看懂 EXPLAIN 里每个字段在说什么,以及明白 InnoDB 怎么把你的 SQL 翻译成页读取、行锁、间隙锁这些底层动作。很多“优化”失败,是因为在执行器层调优,而瓶颈其实在存储引擎的 Buffer Pool 命中率或 MVCC 版本链遍历上。