【第8天】每天一个MySQL知识点,百日打怪升级

本文涉及的产品
PolarDB Agent Flow,2核4GB
RDS AI 助手,专业版
RDS DuckDB + QuickBI 企业套餐,8核32GB + QuickBI 专业版
简介: 本文系统解析MySQL中SELECT语句的完整执行流程:从连接器验权、(已移除的)查询缓存、解析器语法检查、预处理器语义校验,到优化器索引选择与执行器调用存储引擎取数。涵盖面试高频考点与实战避坑指南,助DBA深入理解SQL底层机制。(239字)

SQL基础:SELECT执行流程


大家好,我是一名拥有10年以上经验的DBA老兵。

做这个系列,源于一个朴素的愿望:把踩过的坑、总结的经验系统化输出,希望能帮到刚入行或想进阶的兄弟们。

让我们开始今天的第8天内容。


🎯 面试考点

  • SELECT 语句从输入到输出经历了哪些步骤?
  • 查询缓存为什么在 MySQL 8.0 被移除了?
  • 优化器是怎么选择索引的?
  • 执行器是怎么获取数据的?

背景引入

💡 说白了:你以为 SELECT 只是查一下?其实它跑了半个数据库

你有没有想过:

  • 你敲下 SELECT * FROM user WHERE id = 1,MySQL 到底做了什么?
  • 为什么有时候明明加了索引,MySQL 却不用?
  • 为什么同样的 SQL,有时候快有时候慢?

说实话,不理解 SELECT 执行流程的 DBA,就像不知道汽车怎么跑的司机——能开,但出了问题只能干瞪眼。

今天的目标:搞懂 SELECT 语句的完整执行流程,面试必问


核心概念

执行流程全景图

image.png


第一步:连接器

💡 说白了:连接器就是门卫,验明正身才让进

职责

  • 建立 TCP 连接
  • 验证用户名密码
  • 获取用户权限(后续操作都依赖这个权限)

面试必问

  • 为什么有时候连接很慢?
  • 长连接和短连接有什么区别?
  • 为什么长连接会导致内存泄漏?

面试解答

Q: 为什么有时候连接很慢?

因为建立 TCP 连接需要三次握手,如果数据库服务器距离远或者网络差,连接就会慢。建议使用连接池,复用连接。

Q: 长连接和短连接有什么区别?

短连接:每次执行完 SQL 就断开连接。长连接:执行完 SQL 后保持连接,下次复用。长连接减少了建立连接的开销,但会导致内存增长。


第二步:查询缓存(MySQL 8.0 已移除)

💡 说白了:查询缓存就是"抄作业",SQL 完全一样就直接返回结果

工作机制

  1. 检查 SQL 是否命中缓存(精确匹配,包括空格)
  2. 命中 → 直接返回结果
  3. 未命中 → 继续执行后续步骤,结果放入缓存

为什么 MySQL 8.0 移除了?

  • 缓存失效太频繁:表有任何更新,该表所有缓存失效
  • 命中率低:业务 SQL 通常有参数差异
  • 维护成本高:需要额外的锁机制
-- MySQL 5.7 可以手动关闭查询缓存
SET GLOBAL query_cache_type = 0;

-- 查看缓存状态
SHOW STATUS LIKE 'Qcache%';

第三步:解析器

💡 说白了:解析器就是"语文老师",检查 SQL 语法对不对

职责

  • 词法分析:识别关键字、表名、列名
  • 语法分析:检查 SQL 语法是否正确
-- 语法错误示例
SELEC * FROM user;  -- 报错:SELEC 不是有效关键字

-- 语义错误示例(解析器检查不出,在预处理器报错)
SELECT * FROM user WHERE non_exist_column = 1;  -- 如果列不存在,在预处理器报错

第四步:预处理器

💡 说白了:预处理器就是"班主任",检查表和列是否存在

职责

  • 检查表名、列名是否存在
  • 检查用户是否有权限访问这些表和列
  • 展开 SELECT * 为具体列名
-- 预处理器会检查
SELECT * FROM user WHERE age > 18;

-- 等价于
SELECT id, name, age, ... FROM user WHERE age > 18;

第五步:优化器

💡 说白了:优化器就是"军师",决定怎么执行最高效

职责

  • 选择使用哪个索引
  • 决定表的连接顺序
  • 选择最优的执行计划

优化器的工作原理

-- 优化器会考虑以下因素
EXPLAIN SELECT * FROM user WHERE age > 18 AND city = '北京';

-- 优化器可能选择:
-- 方案1:使用 idx_age 索引,然后回表过滤 city
-- 方案2:使用 idx_city 索引,然后回表过滤 age
-- 方案3:使用联合索引 idx_age_city(如果存在)

面试必问

  • 优化器是基于什么选择索引的?
  • 优化器选择错了怎么办?

面试解答

Q: 优化器是基于什么选择索引的?

基于统计信息(cardinality、数据分布等)估算成本,选择成本最低的执行计划。但统计信息可能不准确,导致优化器选错索引。

Q: 优化器选择错了怎么办?

可以使用 FORCE INDEX 强制指定索引,或者 ANALYZE TABLE 更新统计信息。


第六步:执行器

💡 说白了:执行器就是"干活的人",真正去存储引擎取数据

职责

  • 根据执行计划,调用存储引擎的接口获取数据
  • 对结果进行过滤、排序、聚合等处理
-- 执行器的工作流程
SELECT * FROM user WHERE age > 18;

-- 1. 调用存储引擎的索引接口,获取满足条件的记录ID
-- 2. 调用存储引擎的行读取接口,获取完整行数据
-- 3. 返回结果集给客户端

实战案例

场景一:查看 SELECT 执行流程

-- 开启性能监控
SET profiling = 1;

-- 执行查询
SELECT * FROM user WHERE id = 1;

-- 查看执行过程
SHOW PROFILES;
SHOW PROFILE FOR QUERY 1;

输出示例

+----------------------+----------+
| Status               | Duration |
+----------------------+----------+
| starting             | 0.000045 |
| checking permissions | 0.000006 |
| Opening tables       | 0.000015 |
| init                 | 0.000008 |
| System lock          | 0.000006 |
| optimizing           | 0.000004 |
| statistics           | 0.000012 |
| preparing            | 0.000008 |
| executing            | 0.000003 |
| Sending data         | 0.000035 |
| end                  | 0.000003 |
| query end            | 0.000002 |
| closing tables       | 0.000004 |
| freeing items        | 0.000015 |
| cleaning up          | 0.000003 |
+----------------------+----------+

场景二:优化器选错索引

-- 创建测试表
CREATE TABLE orders (
    id INT PRIMARY KEY AUTO_INCREMENT,
    user_id INT,
    status VARCHAR(20),
    amount DECIMAL(10,2),
    create_time DATETIME,
    INDEX idx_user_id (user_id),
    INDEX idx_status (status),
    INDEX idx_create_time (create_time)
);

-- 插入数据(status 分布不均匀)
INSERT INTO orders (user_id, status, amount, create_time)
SELECT 
    FLOOR(RAND() * 10000),
    CASE WHEN RAND() < 0.9 THEN 'completed' ELSE 'pending' END,
    ROUND(RAND() * 1000, 2),
    DATE_ADD('2026-01-01', INTERVAL FLOOR(RAND() * 365) DAY)
FROM (
    WITH RECURSIVE cte AS (SELECT 1 AS n UNION ALL SELECT n+1 FROM cte WHERE n<100000)
    SELECT * FROM cte
) tmp;

-- 查看执行计划
EXPLAIN SELECT * FROM orders WHERE status = 'pending' AND create_time > '2026-06-01';

-- 优化器可能选错索引,强制指定
SELECT * FROM orders FORCE INDEX(idx_create_time) WHERE status = 'pending' AND create_time > '2026-06-01';

💡 AI 辅助实战

🤖 AI 能不能帮我对比不同 SQL 写法的执行流程?

能!让 AI 分析同一需求的不同写法,对比执行流程和性能差异:

提示词示例:

"以下两种写法都能查询有订单的用户,请分析执行流程有什么区别:

写法1(子查询):
SELECT * FROM users WHERE id IN (SELECT user_id FROM orders)

写法2(JOIN):
SELECT DISTINCT u.* FROM users u JOIN orders o ON u.id = o.user_id

请分析:
1. 两种写法的执行流程
2. 哪种写法性能更好
3. 什么场景下选择哪种写法"

AI 分析结果

对比项 写法1(子查询) 写法2(JOIN)
执行流程 先执行子查询,再用 IN 匹配 先 JOIN,再去重
索引使用 可能无法使用索引 可以使用索引
临时表 可能需要临时表 需要临时表去重
性能 数据量大时较慢 通常更快

AI 优化建议

  • 写法1 改成 EXISTS 或 JOIN
  • 写法2 可以用 GROUP BY 代替 DISTINCT
  • 都可以加索引优化

避坑指南

⚠️ 真实踩过的坑:

  1. 不要迷信 EXPLAIN

    • EXPLAIN 只是优化器的预估,一般不会有太大偏差,但它不是实际执行情况
    • 建议:用 EXPLAIN ANALYZE(MySQL 8.0+)看真实执行
  2. 长连接要定期重置

    • 长连接会导致内存增长(临时表、排序缓冲区等)
    • 建议:定期调用 mysql_reset_connection() 重置会话状态

    C API 示例

    MYSQL *conn = mysql_init(NULL);
    mysql_real_connect(conn, "localhost", "root", "", "test", 0, NULL, 0);
    
    mysql_query(conn, "SELECT * FROM user");
    
    // 重置连接(不断开,只清理会话状态)
    mysql_reset_connection(conn);
    
  3. 优化器统计信息要定期更新

    • 统计信息过期会导致优化器选错索引
    • 建议:定期执行 ANALYZE TABLE

思考题

🤔 互动时间:

  1. 如果 SELECT 语句执行很慢,你会按什么顺序排查?
  2. 为什么 MySQL 8.0 移除了查询缓存?
  3. 优化器选错了索引,除了 FORCE INDEX,还有什么办法?

总结

🎯 面试考点

  • 执行流程:连接器 → 查询缓存 → 解析器 → 预处理器 → 优化器 → 执行器
  • 连接器:权限验证、长连接/短连接
  • 查询缓存:MySQL 8.0 已移除,因为失效频繁、命中率低
  • 解析器:词法分析、语法分析
  • 优化器:基于统计信息选择最优执行计划
  • 执行器:调用存储引擎获取数据

下期预告:WHERE子句优化技巧 —— 面试必问!

全本合集《每天一个MySQL知识点,百日打怪升级》

您的关注是我每日更新的动力:没有那多


有问题欢迎评论区交流,明天见!

相关文章
|
3月前
|
SQL 缓存 Java
【MyBatis】MyBatis框架知识(全体系总结)
MyBatis 是一款轻量级持久层框架,支持SQL与代码分离、动态SQL、灵活映射及一二级缓存。本文系统讲解其核心组件、配置文件、Mapper映射、CRUD、缓存机制、插件扩展(如PageHelper、MyBatis-Plus)及最佳实践,助你高效掌握ORM开发。
1214 3
|
人工智能 运维 关系型数据库
智能运维+多模型服务能力,阿里云 RDS AI 助手旗舰版正式上线!
RDS AI 助手旗舰版在 RDS AI 助手专业版智能运维能力的基础上,提供灵活模型选择、智能模型路由、多模型灾备、API Key 集成等更自主可控、灵活便捷的模型服务,并支持纳管运维各类环境部署的数据库。
智能运维+多模型服务能力,阿里云 RDS AI 助手旗舰版正式上线!
|
1月前
|
SQL 关系型数据库 MySQL
【MySQL百日打怪升级第14天】 LIMIT 分页的性能优化:深分页到底慢在哪?
本文深入剖析MySQL深分页(如`LIMIT 100000,20`)性能瓶颈:本质是OFFSET导致全量扫描与丢弃,页码越深,扫描行数线性增长。详解三种实战优化方案——游标分页(高效稳定,需有序唯一字段)、延迟关联(兼容OFFSET,索引覆盖减回表)、范围分页(极简但场景受限),并附EXPLAIN对比与避坑指南。(239字)
199 6
|
1月前
|
SQL 算法 关系型数据库
【MySQL百日打怪升级第10天】JOIN的底层原理与优化:NLJ、Hash Join 与 Merge Join
本文系统解析MySQL三大JOIN算法:NLJ(含Simple/Index/Block变体)、8.0.18引入的Hash Join(O(N+M)复杂度,专治无索引大表连接),以及面试常考但MySQL原生不支持的Sort-Merge Join,附实战EXPLAIN识别与优化指南。(239字)
196 5
|
2月前
|
SQL 关系型数据库 MySQL
EXPLAIN 执行计划:一眼看穿你的SQL慢在哪
数据库小学妹带你轻松掌握SQL性能诊断!通过EXPLAIN查看执行计划,精准识别索引失效、全表扫描(ALL)、key为NULL等瓶颈。聚焦type、key、rows等6个关键字段,结合实战案例与避坑指南(如函数滥用、最左前缀破坏),让优化有的放矢。学完即用,告别盲目调优!
|
1月前
|
人工智能 关系型数据库 MySQL
【第6天】每天一个MySQL知识点,百日打怪升级
本文为DBA老兵总结的索引优化实战指南:聚焦“何时建、何时不建”核心问题。详解索引选择性(唯一值/总行数)、失效场景(低区分度、函数运算、隐式转换)及建索引黄金法则——WHERE/JOIN/ORDER BY/GROUP BY高频字段优先,状态类、低选择性列坚决不建。附EXPLAIN实战分析与AI辅助诊断技巧。(239字)
210 1
|
1月前
|
人工智能 关系型数据库 MySQL
【第3天】每天一个MySQL知识点,百日打怪升级
本系列由10年经验DBA主理,系统讲解MySQL安装(RPM/二进制/源码)与核心配置,涵盖`my.cnf`优先级、`innodb_buffer_pool_size`调优、连接与日志参数设置、四种生效方式对比,并附生产避坑指南与面试高频考点,助力快速入门与实战进阶。(239字)
207 2
|
8天前
|
人工智能 监控 前端开发
一篇文章讲清楚 AI Agent:从 Token、RAG、Skill 到 MCP、SDD 和 Harness 工程
本文直击测试开发落地AI Agent的痛点:Demo炫酷却难进真实工程。从Token成本、RAG知识接入、Memory记忆管理到Skill能力封装、ReAct执行闭环、MCP工具连接、SDD规格驱动及Harness可控环境,系统拆解Agent工程化关键链路,助测试开发者跨越“能回答”迈向“可交付”的可靠任务闭环。
|
6月前
|
SQL XML Java
对于 MyBatis Plus 和 MyBatis我们究竟该怎么选择?
MyBatis是轻量级半自动ORM框架,SQL自定义灵活,适合复杂查询;MyBatis Plus在其基础上封装通用CRUD,提升开发效率,支持Lambda与分页插件。两者可混合使用,兼顾效率与灵活性,适用于不同场景。
|
2月前
|
SQL 存储 关系型数据库
【第1天】每天一个MySQL知识点,百日打怪升级
本系列以“每天一个知识点”形式,系统讲解MySQL核心原理。首日聚焦Client/Server通信流程,详解三层架构(连接层→SQL层→存储引擎层)、连接管理、查询解析优化及执行计划,直击面试高频考点,助你从CRUD进阶到懂原理、能排障。(239字)
236 0