文档教程知识库【免费下载链接】CS-Base图解计算机网络、操作系统、计算机组成、数据库共 1000 张图 50 万字破除晦涩难懂的计算机基础知识让天下没有难懂的八股文 在线阅读https://xiaolincoding.com项目地址https://gitcode.com/GitHub_Trending/cs/CS-Base点击查看免费下载output_article_start_tag一条 select 语句在 MySQL 中到底发生了什么——CS-Base 图解 MySQL 执行流程全拆解本文基于 CS-Base 仓库的 《执行一条 select 语句期间发生了什么》 一文逐层拆解 MySQL 执行一条SELECT查询语句的完整旅程从客户端建立连接、身份鉴权到查询缓存命中、SQL 解析再到预处理、优化与执行三个阶段。读完本文你将能清晰回答MySQL 执行一条 select 语句期间发生了什么并掌握连接器、解析器、预处理器、优化器、执行器各自的职责边界以及索引下推、回表、覆盖索引等关键概念在真实执行链路中的落点。一、MySQL 的整体架构Server 层与存储引擎层在剖析执行流程之前先建立全局视角。MySQL 的架构分为两层Server 层负责建立连接、分析和执行 SQL。MySQL 大多数核心功能模块都在这里实现主要包括连接器、查询缓存、解析器、预处理器、优化器、执行器。此外所有内置函数日期、时间、数学、加密函数等以及所有跨存储引擎的功能存储过程、触发器、视图等都在 Server 层实现。存储引擎层负责数据的存储和提取。支持 InnoDB、MyISAM、Memory 等多个存储引擎不同存储引擎共用一个 Server 层。从 MySQL 5.5 版本开始InnoDB 成为默认存储引擎我们常说的索引数据结构就由存储引擎层实现。InnoDB 支持且默认使用 B 树索引数据表中创建的主键索引和二级索引默认都使用 B 树索引。关于 B 树索引的细节可进一步阅读仓库中的 索引常见面试题、从数据页的角度看 B 树 与 为什么 MySQL 采用 B 树作为索引。接下来就按一条 SQL 查询语句的执行顺序依次看每个功能模块的作用。二、第一步连接器建立连接、校验身份、管理会话在 Linux 上使用 MySQL首先要连接 MySQL 服务然后才能执行 SQL# -h 指定 MySQL 服务的 IP 地址连接本机 MySQL 服务时可省略该参数 # -u 指定用户名管理员角色名为 root # -p 指定密码为安全起见建议不要在命令行直接写密码而是通过交互对话输入 mysql -h$ip -u$user -p连接过程需要先经过TCP 三次握手MySQL 基于 TCP 协议传输。如果 MySQL 服务未启动会收到连接失败报错如果服务正常运行完成 TCP 连接建立后连接器开始校验用户名和密码用户名或密码不对收到Access denied for user错误客户端程序结束执行用户名和密码都正确连接器会获取该用户的权限并保存起来此后该连接内的任何操作都基于连接建立时读到的权限做权限判断。因此一个用户建立连接后即使管理员中途修改了该用户的权限也不会影响已存在连接的权限只有新建连接才会使用新的权限设置。2.1 查看连接数show processlist想知道当前 MySQL 服务被多少个客户端连接执行show processlist;结果中每个用户对应一行Command列为Sleep表示该连接空闲连上后没有再执行任何命令Time列显示空闲时长如 736 秒。2.2 空闲连接会一直占用吗wait_timeoutMySQL 通过wait_timeout参数控制空闲连接的最大空闲时长默认 8 小时28800 秒mysql show variables like wait_timeout; ---------------------- | Variable_name | Value | ---------------------- | wait_timeout | 28800 | ---------------------- 1 row in set (0.00 sec)超过该时长连接器会自动断开空闲连接。也可以手动断开mysql kill connection 6; Query OK, 0 rows affected (0.00 sec)注意空闲连接被服务端主动断开后客户端并不会立刻感知直到客户端发起下一个请求才会收到ERROR 2013 (HY000): Lost connection to MySQL server during query。2.3 连接数限制max_connectionsMySQL 支持的最大连接数由max_connections参数控制默认值通常是 151mysql show variables like max_connections; ------------------------ | Variable_name | Value | ------------------------ | max_connections | 151 | ------------------------ 1 row in set (0.00 sec)超过该值后系统会拒绝新的连接请求并报错Too many connections。2.4 短连接 vs 长连接MySQL 连接与 HTTP 类似也有短连接和长连接之分// 短连接 连接 mysql 服务TCP 三次握手 执行sql 断开 mysql 服务TCP 四次挥手 // 长连接 连接 mysql 服务TCP 三次握手 执行sql 执行sql 执行sql .... 断开 mysql 服务TCP 四次挥手长连接可以减少反复建立、断开连接的开销一般推荐使用长连接。但长连接会带来内存占用增多的隐患MySQL 在执行查询过程中临时使用内存管理连接对象这些连接对象资源只有在连接断开时才释放。长连接累积很多时MySQL 服务占用内存过大有可能被系统强制杀掉导致服务异常重启。解决长连接占用内存有两种方式定期断开长连接断开连接即释放连接占用的内存资源。客户端主动重置连接MySQL 5.7 实现了mysql_reset_connection()函数接口注意是接口函数而非命令。客户端执行完大操作后在代码中调用该函数重置连接达到释放内存的效果。这个过程不需要重连、不需要重新做权限验证但会把连接恢复到刚刚创建完成时的状态。2.5 连接器小结与客户端进行 TCP 三次握手建立连接校验客户端的用户名和密码不对则报错校验通过后读取该用户的权限后续权限逻辑判断都基于此时读取到的权限。三、第二步查询缓存Query CacheMySQL 8.0 已移除连接器工作完成后客户端即可向 MySQL 发送 SQL 语句。MySQL 收到 SQL 后会解析出 SQL 语句的第一个字段判断语句类型如果是查询语句select 语句先去**查询缓存Query Cache**中查找看看之前是否执行过这条命令查询缓存以 key-value 形式保存在内存中key 为 SQL 查询语句value 为 SQL 查询结果命中缓存则直接返回 value 给客户端未命中则继续往下执行执行完后将查询结果存入查询缓存。但查询缓存其实挺鸡肋对更新频繁的表查询缓存命中率很低——只要表有更新操作该表的查询缓存就会被清空。刚缓存了查询结果很大的数据、还没被使用时表一更新缓存就被清空等于缓存了个寂寞。因此MySQL 8.0 直接删除了查询缓存8.0 开始执行 select 语句不再经过查询缓存阶段MySQL 8.0 之前的版本可通过将参数query_cache_type设置为DEMAND关闭查询缓存。注意这里说的查询缓存是Server 层的查询缓存MySQL 8.0 移除的也是它并不是 InnoDB 存储引擎中的 Buffer Pool。Buffer Pool 的相关原理可参考仓库的 揭开 Buffer_Pool 的面纱。四、第三步解析 SQL解析器在正式执行 SQL 前MySQL 会先对 SQL 语句做解析交由解析器完成。解析器做两件事词法分析根据输入的字符串识别出关键字构建出 SQL 语法树方便后续模块获取 SQL 类型、表名、字段名、where 条件等语法分析根据词法分析结果按照语法规则判断输入 SQL 是否满足 MySQL 语法。如果 SQL 语法不对会在解析器阶段报错。例如把from写成formMySQL 解析器就会报语法错误。但注意表不存在或字段不存在并不是在解析器里判断的。《MySQL 45 讲》曾说是在解析器里做的但结合 MySQL 源码5.7 与 8.0分析解析器只负责构建语法树和检查语法不会去查表或字段是否存在。那这个工作由谁做——预处理阶段prepare。五、第四步执行 SQLprepare → optimize → execute经过解析器后进入执行 SQL 查询语句的流程。每条SELECT查询语句流程主要分为三个阶段prepare 阶段预处理阶段optimize 阶段优化阶段execute 阶段执行阶段。5.1 预处理器prepare预处理阶段做两件事检查 SQL 查询语句中的表或字段是否存在将select *中的*符号扩展为表上的所有列。例如下面这条语句test表不存在就会在 prepare 阶段报错mysql select * from test; ERROR 1146 (42S02): Table mysql.test doesnt exist关于表/字段是否存在不是在解析器判断的结论可用 MySQL 8.0 源码佐证报错表不存在时的函数调用栈显示错误是在get_table_share()函数里报的而该函数在 prepare 阶段被调用。对 MySQL 8.0表或字段存在性判断放在 prepare 阶段对 MySQL 5.7判断工作在词法分析语法分析之后、prepare 阶段之前做。两者结论一致都不在解析器里做。之所以 5.7 与 8.0 位置不同是因为 MySQL 5.7 代码结构不佳8.0 代码结构变化很大将这项工作放入了 prepare 阶段。5.2 优化器optimize预处理之后还需要为 SQL 查询语句制定一个执行计划这个工作交由优化器完成。优化器负责将 SQL 查询语句的执行方案确定下来。比如表里有多个索引时优化器会基于查询成本考虑决定选择使用哪个索引。以开头的查询语句select * from product where id 1为例很简单就是选择使用主键索引。想查看优化器选择了哪个索引可在查询语句最前面加explain命令输出执行计划。执行计划中的key表示执行过程中使用了哪个索引比如key为PRIMARY表示使用了主键索引。如果执行计划中key为 null说明没有使用索引会进行全表扫描type ALL这是效率最低档次的扫描方式。优化器选择索引的经典例子——覆盖索引假设 product 表原本只有主键索引现在将name设置为普通索引二级索引于是 product 表同时拥有主键索引id和普通索引name。执行查询select id from product where id 1 and name like i%;这条语句既可使用主键索引也可使用普通索引但执行效率不同。这是覆盖索引场景查询结果直接在二级索引就能找到二级索引 B 树叶子节点存储的是主键值没必要再走主键索引因为查询主键索引 B 树的成本比二级索引 B 树大。优化器基于查询成本会选择代价小的普通索引。执行计划中可以看到使用了普通索引nameExtra为Using index表明使用了覆盖索引优化。关于覆盖索引、回表、二级索引 B 树结构可参考 索引常见面试题 和 从数据页的角度看 B 树 的详细图解。5.3 执行器execute经历优化器后执行方案确定MySQL 真正开始执行语句由执行器完成。执行过程中执行器与存储引擎交互交互以数据行为单位。下面用三种执行方式说明执行器与存储引擎的交互过程主键索引查询、全表扫描、索引下推。5.3.1 主键索引查询以select * from product where id 1;为例。查询条件用到主键索引且是等值查询主键 id 唯一不会有 id 相同的记录优化器决定选用访问类型为const即使用主键索引查询一条记录。执行流程执行器第一次查询调用read_first_record函数指针指向的函数。因访问类型为 const该指针指向 InnoDB 引擎索引查询接口把条件id 1交给存储引擎让存储引擎定位符合条件的第一条记录存储引擎通过主键索引的 B 树结构定位到 id 1 的记录记录不存在则向执行器上报找不到错误查询结束记录存在则将记录返回给执行器执行器从存储引擎读到记录后判断记录是否符合查询条件符合则发送给客户端不符合则跳过执行器查询过程是 while 循环会再查一次。这次因不是第一次查询调用read_record函数指针指向的函数因访问类型为 const该指针被指向一个永远返回 -1 的函数调用后执行器退出循环查询结束。5.3.2 全表扫描以select * from product where name iphone;为例。查询条件没有用到索引优化器选用访问类型为ALL全表扫描。执行流程执行器第一次查询调用read_first_record函数指针指向的函数。因访问类型为 all该指针指向 InnoDB 引擎全扫描接口让存储引擎读取表中第一条记录执行器判断读到的记录 name 是否为 iphone不是则跳过是则将记录发给客户端。Server 层每从存储引擎读到一条记录就会发送给客户端客户端之所以最终直接显示所有记录是因为客户端等查询语句查询完成后才显示所有记录执行器 while 循环继续查询调用read_record函数指针指向的函数访问类型为 all仍指向 InnoDB 引擎全扫描接口接着让存储引擎读取下一条记录执行器继续判断不符合条件即跳过符合则发送到客户端重复上述过程直到存储引擎读完表中所有记录向执行器返回读取完毕信息执行器收到查询完毕信息退出循环停止查询。5.3.3 索引下推Index Condition Pushdown这部分非常适合讲索引下推MySQL 5.6 推出的查询优化策略这样能清楚知道下推这个动作下推到了哪里。索引下推能够减少二级索引查询时的回表操作提高查询效率——它把 Server 层部分负责的事情交给存储引擎层处理。举例假设用户表对age和reward字段建立了联合索引(age, reward)执行查询select * from t_user where age 20 and reward 100000;联合索引遇到范围查询、就会停止匹配age 字段能用到联合索引但 reward 字段无法利用到索引。具体原因可参考 索引常见面试题 中联合索引范围查询小节的分析。不使用索引下推MySQL 5.6 之前时执行器与存储引擎的流程Server 层首先调用存储引擎接口定位到满足查询条件的第一条二级索引记录即 age 20 的第一条记录存储引擎根据二级索引 B 树快速定位到该记录获取主键值然后进行回表操作将完整记录返回给 Server 层Server 层判断该记录 reward 是否等于 100000成立则发送给客户端否则跳过继续向存储引擎索要下一条记录存储引擎定位后获取主键值再次回表返回完整记录给 Server 层如此往复直到存储引擎读完所有记录。可见没有索引下推时每查询到一条二级索引记录都要回表然后 Server 再判断 reward 是否等于 100000。使用索引下推后判断 reward 是否等于 100000 的工作交给存储引擎层Server 层首先调用存储引擎接口定位到满足查询条件的第一条二级索引记录age 20 的第一条记录存储引擎定位到二级索引后先不执行回表而是先判断该索引中包含的列reward 列的条件是否成立条件不成立直接跳过该二级索引条件成立则执行回表将完整记录返回给 Server 层Server 层判断其他查询条件本例无其他条件是否成立成立则发送给客户端否则跳过然后向存储引擎索要下一条记录如此往复直到存储引擎读完所有记录。使用了索引下推后虽然 reward 列无法使用到联合索引但因为它包含在联合索引(age, reward)里所以直接在存储引擎过滤出满足 reward 100000 的记录后才去回表获取完整记录相比不使用索引下推节省了大量回表操作。如何判断是否使用了索引下推当执行计划里的Extra部分显示Using index condition说明使用了索引下推。关于联合索引范围查询与索引下推的更完整分析可继续阅读 索引常见面试题 中的对应小节Q1~Q4 示例、key_len 判断等。六、总结一条 select 语句的完整旅程执行一条 SQL 查询语句期间发生了什么连接器建立连接、管理连接、校验用户身份查询缓存查询语句命中缓存则直接返回否则继续往下执行MySQL 8.0 已删除该模块解析 SQL通过解析器进行词法分析、语法分析构建语法树方便后续模块读取表名、字段、语句类型执行 SQL共三个阶段预处理阶段检查表或字段是否存在将select *中的*扩展为表上的所有列优化阶段基于查询成本选择查询成本最小的执行计划执行阶段根据执行计划执行 SQL 查询语句从存储引擎读取记录返回给客户端。MySQL 执行一条 select 语句核心脉络可以浓缩为连接器建立连接并鉴权 → 查询缓存8.0 起已移除→ 解析器构建语法树 → 预处理器校验表/字段并展开*→ 优化器制定执行计划 → 执行器按计划与存储引擎逐行交互并返回结果。七、延伸阅读本文来自 CS-Base图解计算机网络、操作系统、计算机组成、数据库仓库的 MySQL 基础篇仓库中还有大量与本文主题强相关的文章可继续深入索引常见面试题覆盖索引、回表、联合索引最左匹配、范围查询、索引下推、索引区分度等完整讲解从数据页的角度看 B 树从数据页、页目录、B 树结构看 InnoDB 数据组织与查询过程为什么 MySQL 采用 B 树作为索引B 树与 B 树、二叉树、Hash 的对比索引失效有哪些索引失效的典型场景与原因MySQL 一行记录是怎么存储的行格式、表空间、数据页的存储细节揭开 Buffer_Pool 的面纱区分 Server 层查询缓存与 InnoDB Buffer Pool。/output_article_end_tag赞分享文档教程知识库【免费下载链接】CS-Base图解计算机网络、操作系统、计算机组成、数据库共 1000 张图 50 万字破除晦涩难懂的计算机基础知识让天下没有难懂的八股文 在线阅读https://xiaolincoding.com项目地址https://gitcode.com/GitHub_Trending/cs/CS-Base点击查看免费下载相关推荐Vue.js 源码分析new Vue 实例化时到底发生了什么初始化全流程拆解Vue.js 源码分析new Vue 实例化时到底发生了什么初始化全流程拆解 导读 本文聚焦 Vue.js 源码中 new Vue options 这一入文档教程前端JavaGuide项目解析深入理解MySQL中SQL语句的执行过程JavaGuide项目解析深入理解MySQL中SQL语句的执行过程 本文基于JavaGuide开源项目深度解析MySQL中SQL语句的完整执行流程涵盖查询文档教程后端数据库学习不再难CS-Base 项目中 MySQL 图解的底层逻辑解析数据库学习不再难CS Base 项目中 MySQL 图解的底层逻辑解析 你是否还在为 MySQL 底层原理晦涩难懂而烦恼是否面对 B树、MVCC 等概念感文档教程知识库上一篇OpCore Simplify 使用指南自动生成 OpenCore EFI新手也能完成黑苹果配置下一篇extuner未来展望性能调优工具的技术路线图与发展方向创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考