专栏文章
专栏文章
MySQL 系列
1. MySQL 系列 #01:MySQL 简介 2. MySQL 系列 #02:MySQL 数据类型与 Java 类型映射 3. MySQL 系列 #03:MySQL 基础架构与执行流程 4. MySQL 系列 #04:MySQL InnoDB 日志系统 5. MySQL 系列 #05:MySQL 事务与 MVCC 6. MySQL 系列 #06:MySQL 索引原理与优化 7. MySQL 系列 #07:MySQL 锁机制 8. MySQL 系列 #08:MySQL 性能问题排查 9. MySQL 系列 #09:MySQL 主备复制与高可用 10. MySQL 系列 #10:MySQL 实战技巧与常见陷阱 11. MySQL 系列 #11:MySQL 数据库设计规范 12. MySQL 系列 #12:MySQL SQL 函数与查询技巧 13. MySQL 系列 #13:MySQL InnoDB Buffer Pool 原理 14. MySQL 系列 #14:MySQL 排序与聚合原理

MySQL 系列 #03:MySQL 基础架构与执行流程

发布于 2026-05-26 10:33 👁 17 次阅读
#mysql#架构#query-execution

一条 SQL 从发出到返回结果,经历了哪些组件、做了什么?本文以 Server 层 + 存储引擎层为主线,拆解 MySQL 逻辑架构及查询/更新语句的完整执行路径。


目录

章节 说明
整体架构 Server 层与存储引擎层的分工
查询语句执行流程 连接器→分析器→优化器→执行器
更新语句执行流程 多了 redo log 和 binlog 的写入
连接管理 长连接的内存问题与应对方案
查询缓存 为什么不建议使用

整体架构

mysql arch

关键设计:不同存储引擎共用同一个 Server 层,存储引擎以插件形式接入。

层级 职责
Server 层 SQL 解析、权限校验、查询优化、内置函数
存储引擎层 数据存储和读取,不同引擎能力不同

查询语句执行流程

SELECT * FROM T WHERE ID=10 为例:

flowchart TD
    A["客户端发送查询"] --> B["连接器<br/>建立连接、认证身份、获取权限"]
    B --> C{"查询缓存<br/>是否命中?"}
    C -->|命中| D["直接返回结果(8.0 已删除)"]
    C -->|未命中| E["分析器<br/>词法分析 + 语法分析"]
    E --> F["优化器<br/>选择索引、确定 join 顺序"]
    F --> G["执行器<br/>调用存储引擎接口逐行读取"]
    G --> H["返回结果集"]
    style D fill:#fcc,stroke:#c00

各组件职责

组件 职责 典型错误
连接器 TCP 握手、用户名/密码认证、读取权限 Access denied for user
分析器 词法分析(识别关键字/表名/列名)+ 语法分析 You have an error in your SQL syntax
优化器 选择使用哪个索引、决定多表 join 顺序 选错索引(可用 force index 干预)
执行器 验证表级权限,调用引擎接口逐行读取 SELECT command denied to user

权限校验的三层机制

为什么不在分析器阶段做权限校验?

SQL 确实先经过连接器,但连接器是透传层——只管理网络连接,收到 SQL 后原样转发给分析器,不解读 SQL 内容(职责分离)。即使连接器"看"了 SQL,仍然无法做细粒度权限校验,因为:

三层权限校验完整链路:

① 连接建立时(连接器)
   ├─ 身份认证:用户名 + 密码
   └─ 读取并缓存全局权限(mysql.user)→ 后续所有请求复用此缓存

② SQL 到来,执行器阶段
   ├─ 库级权限:有没有权限访问此 database(mysql.db)
   ├─ 表级权限:有没有权限操作此 table(mysql.tables_priv)
   ├─ 列级权限:有没有权限访问这些列(mysql.columns_priv)
   └─ 视图/存储过程展开后的底层对象权限

⚠️ 重要:执行器用的是连接建立时缓存的权限快照,而非实时查权限表。因此 GRANT 改权限后旧连接不会立即生效,需断开重连(或 FLUSH PRIVILEGES)。

语法错误(分析器)和权限错误(执行器)是不同阶段的不同报错来源:You have an error in your SQL syntax vs SELECT command denied to user

rows_examined 与实际扫描行数

慢查询日志中 rows_examined 表示执行器调用引擎接口的次数,不等于引擎内部实际扫描的行数(引擎一次调用可能内部扫了多行)。


更新语句执行流程

更新语句(UPDATE/INSERT/DELETE)走同样的 Server 层流程,额外多了两个日志模块

执行器读取 ID=2 的行(通过 InnoDB 接口)
    ↓
执行器计算新值,调用 InnoDB 写入接口
    ↓
InnoDB 将操作写入 redo log(prepare 状态)
    ↓
执行器写入 binlog 到磁盘
    ↓
InnoDB 提交 redo log(commit 状态)

两阶段提交(2PC) 是为了保证 redo log 和 binlog 的一致性,详见日志系统笔记。


连接管理

长连接 vs 短连接

对比 长连接 短连接
定义 请求持续复用同一连接 每次查询后断开
开销 建连少,但内存累积 频繁建连,开销大
推荐 ✅ 推荐(减少建连开销) ❌ 不推荐

长连接内存累积问题

MySQL 在执行过程中的临时内存挂在连接对象上,连接断开时才释放。长连接积累下来,可能导致 OOM,现象为 MySQL 异常重启。

两种解决方案

-- 方案1:定期断开长连接(执行大查询后主动断开)
-- 代码层主动关闭连接

-- 方案2(推荐):MySQL 5.7+ 定期重置连接资源
-- 不需要重连,不需要重新认证
mysql_reset_connection()

连接超时参数

-- 查看空闲连接超时时间(默认 8 小时)
SHOW VARIABLES LIKE 'wait_timeout';

-- 查看当前所有连接(Sleep 状态为空闲连接)
SHOW PROCESSLIST;

查询缓存

为什么不建议使用

适合使用查询缓存的唯一场景

几乎不更新的静态配置表(如系统参数表),可用 SQL_CACHE 显式指定:

-- 仅在 8.0 以下版本有效
SELECT SQL_CACHE * FROM system_config WHERE id = 1;

参考资料

← 返回列表

评论 (0)

暂无评论,来留下第一条吧。
登录注册 后才能发表评论