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

本文涉及的产品
RDS Agent(兼容OpenClaw),2核4GB
RDS MySQL DuckDB 分析主实例,基础系列 4核8GB
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知识点,百日打怪升级》

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


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

相关文章
|
10天前
|
SQL 关系型数据库 MySQL
EXPLAIN 执行计划:一眼看穿你的SQL慢在哪
数据库小学妹带你轻松掌握SQL性能诊断!通过EXPLAIN查看执行计划,精准识别索引失效、全表扫描(ALL)、key为NULL等瓶颈。聚焦type、key、rows等6个关键字段,结合实战案例与避坑指南(如函数滥用、最左前缀破坏),让优化有的放矢。学完即用,告别盲目调优!
|
3月前
|
人工智能 API 机器人
OpenClaw 用户部署和使用指南汇总
本文档为OpenClaw(原MoltBot)官方使用指南,涵盖一键部署(阿里云轻量服务器年仅68元)、钉钉/飞书/企微等多平台AI员工搭建、典型场景实践及高频问题FAQ。同步更新产品化修复进展,助力用户高效落地7×24小时主动执行AI助手。
29176 253
|
人工智能 运维 关系型数据库
智能运维+多模型服务能力,阿里云 RDS AI 助手旗舰版正式上线!
RDS AI 助手旗舰版在 RDS AI 助手专业版智能运维能力的基础上,提供灵活模型选择、智能模型路由、多模型灾备、API Key 集成等更自主可控、灵活便捷的模型服务,并支持纳管运维各类环境部署的数据库。
智能运维+多模型服务能力,阿里云 RDS AI 助手旗舰版正式上线!
|
4天前
|
人工智能 关系型数据库 MySQL
【第6天】每天一个MySQL知识点,百日打怪升级
本文为DBA老兵总结的索引优化实战指南:聚焦“何时建、何时不建”核心问题。详解索引选择性(唯一值/总行数)、失效场景(低区分度、函数运算、隐式转换)及建索引黄金法则——WHERE/JOIN/ORDER BY/GROUP BY高频字段优先,状态类、低选择性列坚决不建。附EXPLAIN实战分析与AI辅助诊断技巧。(239字)
70 1
|
5天前
|
存储 人工智能 关系型数据库
【第5天】每天一个MySQL知识点,百日打怪升级
本文是MySQL索引核心篇,由10年经验DBA精讲B+树原理。深入剖析为何选B+树而非B树/二叉树/哈希表,详解其非叶节点仅存key、叶子双向链表、聚簇与二级索引结构,并结合回表、覆盖索引、页分裂等实战场景,直击面试高频考点。(239字)
73 2
|
6天前
|
SQL 人工智能 关系型数据库
【第4天】每天一个MySQL知识点,百日打怪升级
本系列由10年经验DBA精心打造,系统梳理MySQL客户端常用命令:从连接参数(-u/-p/-h/-P/字符集)、快捷指令(\s/\q/\G)、数据库/表操作(SHOW/CREATE/DROP/DESC),到状态监控(PROCESSLIST/STATUS/VARIABLES)与实战排障技巧,兼顾面试考点与生产避坑,助你快速上手、底气十足。
76 2
|
8天前
|
人工智能 关系型数据库 MySQL
【第3天】每天一个MySQL知识点,百日打怪升级
本系列由10年经验DBA主理,系统讲解MySQL安装(RPM/二进制/源码)与核心配置,涵盖`my.cnf`优先级、`innodb_buffer_pool_size`调优、连接与日志参数设置、四种生效方式对比,并附生产避坑指南与面试高频考点,助力快速入门与实战进阶。(239字)
63 2
|
8天前
|
存储 人工智能 关系型数据库
【第2天】每天一个MySQL知识点,百日打怪升级
【MySQL第2天】深入解析InnoDB与MyISAM核心差异:事务支持、行锁vs表锁、崩溃恢复、外键及适用场景。10年DBA实战总结,助你避开选型陷阱,面试稳拿分!无脑选InnoDB,除非只读/日志等特殊需求。(239字)
100 3
|
1天前
|
SQL 存储 关系型数据库
【第9天】每天一个MySQL知识点,百日打怪升级
本文详解WHERE子句索引优化四大核心:最左前缀原则(联合索引须从最左列开始)、范围查询截断(>、<后列失效)、索引列禁用函数/运算(避免隐式转换)、LIKE右模糊有效而左模糊失效。附实战案例与避坑指南,助你告别全表扫描,面试工作双通关。(239字)
17 0
|
3天前
|
SQL 关系型数据库 MySQL
【第7天】每天一个MySQL知识点,百日打怪升级
本文系统推荐4款核心SQL优化工具:MySQL内置EXPLAIN(执行计划分析)、开源SQLE(SQL审核平台)、小米SOAR(自动优化器)及Percona pt-query-digest(慢日志分析利器),覆盖事前审查、实时诊断与事后复盘全链路,助DBA高效定位索引失效、全表扫描等性能瓶颈。(239字)
54 0