Mysql学习(十二) -- SQL执行到底做了什么事?

MySQL 执行一条 SQL 的内部完整流程

以一条查询 SQL 为例:

sql 复制代码
SELECT * FROM orders WHERE user_id = 100 AND status = 'PAID' ORDER BY create_time DESC LIMIT 10;

阶段一:连接器(Connector)
复制代码
客户端 → 连接器
  • 验证用户名、密码、主机权限
  • 建立 TCP 连接,分配线程
  • 维护连接状态(SHOW PROCESSLIST 看到的就是这里)
  • 连接建立后,权限判断结果会缓存在连接上下文中,后续该连接内的查询不再重复校验权限

阶段二:查询缓存(MySQL 8.0 已移除)
复制代码
查询 → 查询缓存
  • 以 SQL 文本为 key,查缓存中是否有结果
  • 命中 → 直接返回,跳过后续所有阶段
  • 未命中 → 继续往下走

⚠️ MySQL 8.0 已经彻底移除了查询缓存,因为失效太频繁(表有任何更新就清空该表所有缓存),命中率极低。


阶段三:分析器(Analyzer)
复制代码
SQL文本 → 词法分析 → 语法分析 → 解析树

词法分析:把 SQL 字符串拆成 token

复制代码
"SELECT * FROM orders WHERE user_id = 100"
→ [SELECT] [*] [FROM] [orders] [WHERE] [user_id] [=] [100]

语法分析:根据语法规则,构建解析树

复制代码
         SELECT
        /   |   \
   columns  FROM  WHERE
     *     orders  user_id = 100
             |     status = 'PAID'
          ORDER BY  LIMIT
         create_time  10
  • 这一步会检查 SQL 语法是否正确
  • 表名、列名是否存在,在这一步还不会检查(那是优化器的事)

阶段四:预处理器(Preprocessor)
复制代码
解析树 → 预处理
  • 展开 SELECT * → 查表的元数据,替换成具体列名
  • 检查表名、列名是否存在
  • 检查权限
  • 处理通配符、默认值等

阶段五:优化器(Optimizer)--- 最核心的阶段
复制代码
解析树 → 查询优化 → 执行计划

优化器要做的决策:

决策 举例
选择哪个索引 idx_user_ididx_status,用哪个?
多表 JOIN 顺序 A JOIN B JOIN C,先 JOIN 哪个?
是否使用索引 走索引快还是全表扫描快?
子查询优化 子查询能否转成 JOIN?
排序优化 能否利用索引的有序性,避免 filesort?

优化器基于**代价模型(Cost-Based)**做决策:

复制代码
IO代价 = 需要读取的页数 × 单页读取代价
CPU代价 = 需要检查的行数 × 单行比较代价

总代价 = IO代价 + CPU代价

哪个执行计划的总代价最低,就选哪个

输出一个执行计划,比如:

复制代码
→ 使用 idx_user_id 索引查找 user_id = 100 的行
→ 在索引结果中过滤 status = 'PAID'
→ 按 create_time 排序
→ 取前 10 条

阶段六:执行器(Executor)
复制代码
执行计划 → 调用存储引擎 → 返回结果
  • 先检查对目标表是否有 SELECT 权限

  • 调用存储引擎接口,按执行计划逐步执行

    执行器 存储引擎(InnoDB)
    | |
    |-- 调用 ha_init() 打开表 --------→ | 打开表,加载表结构
    | |
    |-- 调用 ha_index_init(idx) ------→ | 初始化 idx_user_id 索引
    | |
    |-- 调用 ha_index_read(100) ------> | 在B+树中找到 user_id=100 的记录
    | |
    |-- 过滤 status='PAID' <---------- | 返回匹配行
    | |
    |-- 调用 ha_index_read() 继续 ----→ | 继续扫描下一条
    | |
    |-- 收集够10条,排序,返回结果 |


阶段七:存储引擎层(InnoDB)

存储引擎内部做的事:

复制代码
1. 从 Buffer Pool 中查找数据页
   ├─ 命中 → 直接返回(内存读取,微秒级)
   └─ 未命中 → 从磁盘读取数据页到 Buffer Pool(毫秒级)

2. 行锁处理
   ├─ SELECT ... FOR UPDATE → 加排他锁(X锁)
   ├─ SELECT ... LOCK IN SHARE MODE → 加共享锁(S锁)
   └─ 普通 SELECT → 不加锁(MVCC 读)

3. MVCC(多版本并发控制)
   ├─ 读取 undo log 中的历史版本
   └─ 根据 Read View 判断哪一版对当前事务可见

4. 返回数据给执行器

完整流程图
复制代码
客户端
  │
  ▼
┌─────────────────────┐
│     连接器           │  验证身份、建立连接
└─────────┬───────────┘
          ▼
┌─────────────────────┐
│   查询缓存(8.0移除)  │  命中则直接返回
└─────────┬───────────┘
          ▼
┌─────────────────────┐
│     分析器           │  词法分析 → 语法分析 → 解析树
└─────────┬───────────┘
          ▼
┌─────────────────────┐
│     预处理器         │  展开*、检查表名列名、权限
└─────────┬───────────┘
          ▼
┌─────────────────────┐
│     优化器           │  选索引、定JOIN顺序、生成执行计划
└─────────┬───────────┘
          ▼
┌─────────────────────┐
│     执行器           │  按执行计划调用存储引擎
└─────────┬───────────┘
          ▼
┌─────────────────────┐
│   存储引擎(InnoDB)   │  Buffer Pool → 行锁 → MVCC → 返回数据
└─────────┬───────────┘
          ▼
       返回结果给客户端

📌 一句话总结

一条 SQL 从进入 MySQL 到返回结果,经过 连接器 → 查询缓存 → 分析器 → 预处理器 → 优化器 → 执行器 → 存储引擎 共 7 个阶段。其中优化器是最核心的,它决定走哪个索引、怎么 JOIN、要不要排序,直接决定了 SQL 的快慢。


你是在排查某条具体 SQL 的性能问题吗?可以把 SQL 和执行计划(EXPLAIN 结果)贴给我,我帮你分析优化器选了什么样的执行计划、有没有优化空间。

相关推荐
敲代码的嘎仔1 小时前
从零实现视频续播 + 学习进度统计:前端心跳、条件更新、GROUP BY 统计全链路拆解
java·前端·数据库·学习·面试·职场和发展·音视频
xcLeigh1 小时前
AI 编程学习路线图:一份覆盖前端、后端、全栈的系统学习计划
前端·人工智能·学习
babe小鑫2 小时前
精算学专业秋招:Excel、SQL、R和Python应该先练什么
sql·r语言·excel
fanfan_hongyun3 小时前
UE5.1 学习记录-玩家控制器
学习·ue5
2501_9336707910 小时前
2026秋招量化分析岗技能栈:Python、SQL、统计建模、回测项目怎么准备
开发语言·python·sql
传奇开心果编程10 小时前
【xilem0.4基础语法学与练】第13课:Xilem 0.4 最简短代码体现“一切皆设计图“
学习·rust·前端框架
小雪崩11 小时前
嵌入式学习 day45:51单片机基础
学习·51单片机
天衍四九-11 小时前
MySQL中如何定位慢查询?如果SQL语句执行很慢,如何分析和优化?
数据库·sql·mysql
尚久龙11 小时前
mysql-5.7.26-winx64的安装步骤
数据库·mysql