数据库与缓存10 分钟阅读更新于 2026-07-30

一条 SQL 在 MySQL 中怎么执行:从连接到 InnoDB

MySQL 架构章节,按连接器、SQL 解析、预处理、优化器、执行器和 InnoDB 存储引擎的顺序,讲清查询和更新语句在 MySQL 内部的主流程。

相关工具

先别急着背组件名

学习 MySQL 架构时,最容易出现的情况是:连接器、分析器、优化器、执行器、存储引擎都能背出来,但真遇到一条慢 SQL,还是不知道该往哪里看。原因不在名词少,而在名词没有串成流程。

相关基础内容 里把 MySQL 分成两层:Server 层和存储引擎层。Server 层负责建立连接、分析和执行 SQL;存储引擎层负责数据的存储和提取,支持 InnoDB、MyISAM、Memory 等引擎。现在最常用的是 InnoDB,它的索引模型主要是 B+ 树。

这一篇就沿着一条 SQL 的生命周期讲。你可以把 MySQL 想成一个接待窗口:客户端先连进来,系统确认你是谁、有没有权限;然后拆解你写的 SQL,看它语法是否成立;接着判断表和字段是否存在;再选择一条成本更低的执行路线;最后真的去存储引擎里读写数据。顺序理清了,后面学索引、事务、锁和日志会轻很多。

一条 SQL 在 MySQL 中执行的流程图,包含客户端、连接器、解析 SQL、预处理、优化器、执行器、InnoDB、数据页索引,以及 update 流程中的 redo log 和 binlog
一条 SQL 在 MySQL 中的执行主线

查询语句主要走连接、解析、预处理、优化、执行和存储引擎;更新语句还会牵出 redo log 和 binlog。

连接器先处理连接和权限

客户端把 SQL 发给 MySQL 之前,要先建立连接。文中说,连接器负责跟客户端建立连接、获取权限,后面的权限判断都基于此时读到的权限。也就是说,一个用户连上 MySQL 后,系统会先确认账号、密码、主机来源和权限范围。

这里有一个容易被忽略的细节:权限不是只在执行语句那一刻才突然出现。连接建立后,MySQL 已经拿到了当前用户的权限信息。后续执行某条 SQL 时,执行器还会判断当前用户是否有执行权限。如果权限不够,你写的 SQL 再正确,也不会继续往下走。

连接本身也有成本。文中提到,MySQL 会定期清理空闲连接,`wait_timeout` 默认值是 8 小时。建立连接比较复杂,所以业务里通常会用连接池和长连接,避免每查几次就断开重连。但长连接太多也会占内存,极端情况下可能导致 MySQL 被系统杀掉。实际应用中,连接池大小、空闲连接回收、连接重置,都不是装饰项。

查询缓存这一步已经不推荐依赖

旧版本 MySQL 在执行查询语句前,可能先看查询缓存里有没有现成结果。命中缓存就直接返回;没命中才继续执行查询,并把结果放入缓存。这个思路听起来不错,但 资料 明确提醒:不建议使用查询缓存,因为数据表频繁更新时,查询缓存中的结果很容易和最新数据不一致。

更关键的是,MySQL 8.0 开始,执行一条 SQL 查询语句不会再走查询缓存阶段。这里要和 Redis 缓存区分开。MySQL 的查询缓存是数据库内部机制,粒度和失效方式受限;Redis 是应用外部缓存,key、过期时间、数据结构、失效策略都由业务自己设计。

所以排查现代 MySQL 查询问题时,不要把希望寄托在查询缓存上。更应该关注 SQL 写法、索引设计、执行计划、返回行数和磁盘读取次数。缓存可以帮助系统降压,但数据库自身的查询路径仍然要走得顺。

解析 SQL:先看你写的是什么

一条 SQL 在人眼里可能很简单,比如 `select * from user where id = 1`。但 MySQL 拿到的是一串字符,它要先识别每个词是什么意思。文中把这一步拆成词法分析和语法分析:词法分析负责识别关键字、表名、字段名、操作符;语法分析负责判断它是否符合 SQL 语法,并构建语法树。

这一步解决的是“能不能读懂”。如果你把 `select` 拼错,少写括号,关键字顺序不对,错误通常会在这里暴露。它还没有真正去表里拿数据,只是在确认这句话有没有资格进入后面的执行流程。

很多初学者调 SQL 时会直接盯着数据结果,其实第一类错误是语句本身不成立。语法错误、字段拼写错误、表名写错、引号不匹配,都不属于性能问题。先让 SQL 成为一条合法语句,再谈索引和执行计划。

预处理:确认表和字段存在

SQL 语法没问题,不代表它一定能执行。预处理阶段会进一步判断表和字段是否存在。比如你写了 `where user_id = 10`,但表里实际字段叫 `uid`,语法上没毛病,业务上却找不到对应列,这类问题就会在这里被拦住。

预处理的意义很朴素:先确认 SQL 引用的对象真实存在,再把它交给优化器。否则优化器没法判断怎么走索引、怎么连接表、怎么读取数据。

这一步也提醒我们,数据库问题不全是“高级问题”。有些错误就是表名、字段名、库名、别名、作用域写错。尤其在多表 join、子查询、临时字段别名比较多时,先检查对象是否存在,常常比直接怀疑数据库性能更有效。

优化器:决定怎么做更划算

优化器负责选择执行方案。文中提到,当一张表里有多个索引时,优化器会基于查询成本决定使用哪个索引;当语句涉及多表关联时,还会决定各个表的连接顺序。它关心的不是 SQL 表面怎么写,而是哪种执行路径代价更低。

举个很常见的例子。用户表里既有 `age` 索引,也有 `city` 索引,你写了 `where age = 30 and city = '杭州'`。MySQL 可能选择先走年龄索引,也可能选择城市索引,具体取决于统计信息、过滤效果、回表成本等因素。索引存在,不等于一定会被选中;被选中,也不等于一定是你想象中的方式。

这就是为什么排查慢 SQL 时要看 `EXPLAIN`。相关的索引章节也提到,可以在 SQL 前加 explain 观察是否使用索引。虽然不同版本和输出字段会有差异,但核心目的不变:别猜 MySQL 怎么执行,直接看执行计划。

执行器和 InnoDB:真正把数据读出来

优化器选好方案后,执行器开始干活。文中说,MySQL 通过分析器知道要做什么,通过优化器知道该怎么做,于是进入执行器阶段。执行器会先判断当前用户是否有权限,然后根据执行计划调用存储引擎接口。

这里要注意 Server 层和存储引擎层的边界。Server 层负责 SQL 语义、执行计划和调用流程;InnoDB 负责真正的数据存储和提取。你可以理解为:执行器知道要按什么条件查,InnoDB 知道数据页和索引页在哪里,以及怎样沿着 B+ 树找到对应记录。

InnoDB 使用 B+ 树索引模型,是因为数据库查询要尽量少读磁盘。资料 在索引部分讲过,想让查询过程访问尽量少的数据块,就不应该用普通二叉树,而要用 N 叉树来降低树高。树高降低,磁盘 I/O 次数也会减少。这个理由比“B+ 树很快”更接近工程本质。

update 语句会多牵出日志

查询语句走连接、解析、预处理、优化、执行。更新语句也要走这套主流程,只是多了日志和事务相关的工作。文中说,执行一次 update 语句,前面同样要先连接数据库,表有更新时跟表有关的查询缓存会失效,然后分析器解析、优化器优化、执行器执行,最后完成更新。

更新流程里有两个很重要的日志:redo log 和 binlog。资料 对它们的区别说得很直接:redo log 是 InnoDB 引擎特有的,binlog 是 MySQL Server 层实现的,所有引擎都可以使用。redo log 是物理日志,记录某个数据页上做了什么修改;binlog 是逻辑日志,记录语句的原始逻辑,比如给某一行的某个字段加 1。

它们的写入方式也不同。redo log 是循环写,空间固定,会写满后覆盖旧内容;binlog 是追加写,文件达到一定大小后切到下一个文件,不会覆盖以前的日志。理解这点,后面学崩溃恢复、主从复制、数据恢复时就不会觉得日志只是“多写一份记录”。

把这条链路用于排查问题

掌握执行流程的好处,是排查问题时不会乱。连接失败,先看账号、密码、网络、连接数、权限和连接池;语法报错,先看 SQL 本身;字段不存在,先看表结构和别名;查询慢,先看执行计划、索引命中和扫描行数;更新异常,再继续看事务、锁、redo log、binlog 和主从延迟。

这条链路也能解释很多面试追问。问 MySQL 架构,可以先答 Server 层和存储引擎层;问一条 SQL 怎么执行,可以按连接器、解析、预处理、优化器、执行器、存储引擎讲;问 update 发生什么,可以在查询流程基础上补充 redo log 和 binlog。回答不需要堆术语,顺序清楚更重要。

后续学 MySQL 索引、事务、锁、MVCC、日志时,都可以把它们挂回这条主线。索引主要影响优化器和存储引擎怎么找数据;事务和锁主要影响并发读写的正确性;日志主要影响更新、恢复和复制。这样学起来会更像一张地图,而不是一堆散开的卡片。

常见问题

MySQL 的 Server 层和存储引擎层分别负责什么?

Server 层负责连接、权限、SQL 解析、优化和执行调度;存储引擎层负责数据的存储和提取。常用的 InnoDB 就属于存储引擎层。

MySQL 8.0 还有查询缓存吗?

MySQL 8.0 开始,查询语句不再走查询缓存阶段。实际应用中更常见的是用 Redis 这类外部缓存来承接热点读取。

优化器一定会使用我创建的索引吗?

不一定。优化器会根据成本估算选择执行方案。索引存在只是候选路径,最终是否使用,要结合执行计划、统计信息和查询条件判断。

redo log 和 binlog 最大的区别是什么?

redo log 是 InnoDB 的物理日志,记录数据页修改,用于崩溃恢复;binlog 是 Server 层的逻辑日志,记录语句或行变更逻辑,常用于归档、复制和恢复。

MySQL 与 Redis

继续阅读

返回专题