MySQL执行SELECT语句的典型流程是:连接→查询缓存(8.0已移除)→解析→预处理→优化器→执行器→存储引擎层→返回结果,关键性能环节在优化器和存储引擎层。

SELECT 语句到底经过了哪些阶段?
MySQL 执行一条 SELECT 语句,不是直接去磁盘查数据,而是走一套有明确顺序的内部流程。理解这个流程,才能知道优化该从哪下手。
典型流程(以 InnoDB 为例)是:连接 → 查询缓存(8.0 已移除)→ 解析 → 预处理 → 优化器 → 执行器 → 存储引擎层 → 返回结果。其中真正影响性能的关键环节集中在「优化器」和「存储引擎层」。
- MySQL 5.7 及以前:查询缓存可能成为性能陷阱——只要表有任意更新,整个表相关缓存全失效;8.0 直接删了
query_cache_type,不用再纠结是否开启 -
EXPLAIN输出的type字段(如ALL、range、ref)反映的是优化器选择的访问类型,不是 SQL 写得“对不对”,而是“它认为怎么最快” - 执行器不负责数据读取,只调用存储引擎接口(如
ha_innobase::index_read()),真正的 I/O、加锁、MVCC 判断都在 InnoDB 层完成
为什么加了索引,EXPLAIN 还显示 type=ALL?
常见误解是“建了索引就一定走”,其实优化器会基于统计信息、索引选择性、WHERE 条件写法、隐式类型转换等综合判断。哪怕索引存在,也可能被跳过。
- 对索引字段做函数操作:
WHERE YEAR(create_time) = 2023→ 无法使用create_time上的普通 B+ 树索引 - 隐式类型转换:
WHERE user_id = '123'(user_id是INT)→ MySQL 转成数字比较,但可能导致索引失效(尤其在字符集不同或 collation 不一致时) - 联合索引未满足最左前缀:
INDEX (a, b, c),但查询是WHERE b = 2 AND c = 3→ 完全用不上该索引 - 优化器估算全表扫描比走索引更快(比如查 80% 的行)→ 它真会选
ALL,这不是 bug,是成本计算结果
ORDER BY 和 LIMIT 怎么避免 filesort?
当 EXPLAIN 的 Extra 列出现 Using filesort,说明 MySQL 需要额外排序缓冲区,不是靠索引顺序直接返回结果。这在大数据量下极易拖慢响应。
鸿思特商城系统HstShop是一款B2C独立网店系统,由拥有十年互联网开发经验的牛头带队开发完成,完全免费开源,适合大中型网站平台快速构建立强大的网上商城平台网店系统。HstShop悉心听取每一位商家的需求与建议,根据中国人的购物习惯改进了购物流程,实现更好的用户购物体验。HstShop网店系统无论在产品功能、稳定性、执行效率、负载能力、安全性和搜索引擎优化等方面都居国内同类产品领先地位,成为国内
- 如果
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)
JOIN 为什么慢?驱动表选错是主因
MySQL 的 JOIN 是嵌套循环(Nested Loop),没有真正意义上的哈希连接(8.0.18+ 才在特定条件下用 Hash Join,但默认仍倾向 NLJ)。谁当驱动表(outer table),决定了外层循环次数和整体代价。
- 优化器通常选「预估结果集更小」的表为驱动表。但统计信息不准(
ANALYZE TABLE没及时运行)、或条件写法干扰判断(如子查询、OR 条件),会导致选错 - 强制指定驱动表可用
STRAIGHT_JOIN,但属高危操作——必须确认被驱动表有合适索引,否则性能雪崩 - 小表驱动大表 ≠ 数据量小的表,而是「WHERE 条件过滤后结果集小」的表。比如用户表 100 万行,但
WHERE is_vip = 1只返回 10 行,则它是理想驱动表 - 关联字段类型/字符集不一致(如
utf8mb4_general_civsutf8mb4_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 版本链遍历上。









