SQL 语句在 MySQL 执行过程

SQL SELECT 语句执行过程

SQL Select 语句整个查询执行过程,总的来说分为 6 个步骤 :

  1. 连接:客户端向 MySQL 发送一条查询请求
    • 客户端与服务器建立连接:通过TCP/IP、Unix套接字或命名管道等方式
    • 身份验证:验证用户名、密码和主机权限
    • 连接管理:服务器为每个连接分配线程,通过SHOW PROCESSLIST可查看
    • 会话变量设置:根据配置文件或用户请求初始化会话环境
  • 与 MySQL 建立的连接,是由连接器 Connectors 来完成的

  • 客户端如果长时间没有和 MySQL 产生交互,连接器就会自动将此次连接断开;这个时间是由参数 wait_timeout 控制的,默认值是8小时。如果连接被断开之后,客户端若再次发送请求会收到一个错误 的提醒:Lost connection to MySQL server during query

  • 客户端与 MySQL 建立连接的过程是比较复杂繁琐的,建议在使用中要尽量减少建立连接的动作,尽量 使用长连接。但是若全部使用长连接,有时候会导致 MySQL 的占用内存飞速飙升,这是因为 MySQL 在 执行过程中临时使用的内存是管理在连接对象里面的。这些资源只有在连接断开的时候才释放。所以如果 长连接累积下来,可能导致内存占用太大,被系统强行杀掉(OOM),从现象看就是 MySQL 异常重启了

长连接和短链接:

  • 长连接是指连接成功后,如果客户端持续有请求,则一直使用同一个连接
  • 短连接是指每次执行完很少的几次查询就断开连接,下次查询再重新建立一个
  1. 缓存:服务器会先检查查询缓存,如果命中缓存,则直接返回缓存结果,否则进入下一阶段
    • 缓存查找:MySQL 将 SELECT 语句作为 key 在查询缓存中查找
    • 缓存条件:要求 SQL 语句完全一致(包括空格、大小写)
    • 缓存失效:表数据修改会导致相关缓存全部失效
    • 缓存配置:通过query_cache_typequery_cache_size参数控制
    • 注意:MySQL 8.0已移除查询缓存功能,因维护缓存开销常大于收益
  2. 解析:服务器进行 SQL 解析(词法语法)、预处理
    • 词法分析:将 SQL 语句分解为令牌(tokens),识别关键字、表名、列名等
    • 语法分析:检查 SQL 语法是否正确,构建解析树
    • 语义检查:验证表、列是否存在,用户是否有权限
    • 视图展开:如果查询涉及视图,将视图替换为定义
    • 预处理:处理表达式,进行常量折叠等优化
  3. 优化:再由优化器生成对应的执行计划
    • 查询重写:优化器可能重写查询以提高效率
    • 执行计划生成:
      • 选择使用哪些索引
      • 决定表的连接顺序(对于多表查询)
      • 评估全表扫描与索引扫描的成本
      • 考虑使用临时表或文件排序
    • 统计信息使用:基于表的统计信息(行数、索引基数等)做出决策
    • 优化器提示:用户可通过FORCE INDEX等提示影响优化器决策
  4. 执行:MySQL 根据执行计划,调用存储引擎的 API 来执行查询
    • 执行引擎:调用存储引擎 API 获取数据
    • 索引使用:根据执行计划使用相应的索引
    • 表扫描:如果需要,执行全表扫描
    • 连接操作:执行嵌套循环连接、哈希连接或排序合并连接
    • 排序分组:执行 ORDER BY、GROUP BY 等操作
    • 临时表:必要时创建临时表处理中间结果
  5. 结果:将结果返回给客户端,同时缓存查询结果
    • 结果集生成:将数据组装成客户端需要的格式
    • 网络传输:通过连接将结果发送给客户端
    • 增量返回:对于大结果集,可能分批返回
    • 缓存写入:如果查询缓存开启且查询可缓存,将结果存入缓存
    • 资源清理:释放执行过程中使用的临时资源

SQL Update 语句执行过程

更新语句在执行时也会先走一遍同样的查询语句的流程

MySQL 整个更新的执行过程,总的来说分为 5 个步骤 :

执行的更新 SQL:update t_student set age=age+1 where id=2

  1. 客户端向 MySQL 服务器发送一条更新请求
    • 客户端与MySQL服务器建立连接后发送 UPDATE 语句
    • 服务器接收 SQL 文本并进行初步校验(如连接权限检查)
    • 如果是事务中操作,会检查当前事务状态(活跃/已提交/已回滚)
  2. 清除表查询缓存 ,跟这个有关的查询缓存会失效。这就是一般不建议使用查询缓存的原因
    • 立即失效化:标记与该表相关的所有查询缓存条目为无效
    • 缓存维护开销:这是查询缓存的主要缺点之一,任何修改操作都会导致相关缓存失效
    • 性能影响:对于写频繁的应用,维护缓存的开销可能超过收益
    • 注意:MySQL 8.0+已完全移除查询缓存功能
  3. 分析器进行 SQL 解析(词法和语法分析),分析这是一条更新语句
    • 词法分析:将UPDATE语句拆解为token(识别关键字、表名、列名等)
    • 语法分析:验证SQL语法结构是否正确(如SET子句格式、WHERE条件等)
    • 语义检查:
      • 验证目标表和列是否存在
      • 检查用户是否有UPDATE权限
      • 验证WHERE条件中引用的列是否存在
    • 预处理:展开视图、处理表达式等
  4. 优化器生成对应的执行计划,优化器决定使用 id 索引
    • 索引选择:基于WHERE条件选择最优索引(如例中的id索引)
    • 访问路径决策:决定使用索引扫描还是全表扫描
    • 成本评估:估算不同执行计划的I/O和CPU成本
    • 连接顺序:如果是多表UPDATE,确定表连接顺序
    • 生成计划:最终确定执行方案,包括使用的索引和访问方法
  5. 执行器负责更新,找到这一行,然后进行更新:
    • 行定位:通过选定索引定位到要修改的行(如果 id=2 这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回【MySQL 8.0后已取消查询缓存】)
      • 如果使用索引:
        • 通过执行器会逐行扫描,检查是否符合 WHERE 条件,效率较低索引快速定位到目标行(减少 I/O 开销)
        • 如果索引是二级索引(非主键),还需要回表查询聚簇索引(主键索引)获取完整数据
      • 如果没有索引(全表扫描):
        • 执行器会逐行扫描,检查是否符合 WHERE 条件,效率较低
    • 获取行锁(并发控制):MySQL 采用锁机制和 MVCC 来保证事务的隔离性
      • InnoDB 行锁:
        • 对符合条件的行加 排他锁(X锁) ,防止其他事务同时修改
        • 如果 WHERE 条件无法走索引,可能会 锁表 (如 UPDATE table WHERE name LIKE '%abc%'
      • MVCC(读已提交/可重复读隔离级别):
        • 读取数据时,基于 ReadView 判断可见性(避免脏读、不可重复读)。
        • 更新时,会检查该行是否被其他事务修改(冲突检测)
    • 写入 undo log(回滚日志):在修改数据前,InnoDB 会先记录 undo log (撤销日志)
      • 用于事务回滚(ROLLBACK
      • 支持 MVCC,让其他事务能读取修改前的数据(一致性读)
    • 修改内存中的数据页:InnoDB 不会直接修改磁盘数据,而是先更新 Buffer Pool(缓冲池) 中的缓存页
      • 从磁盘加载数据页到 Buffer Pool(如果不在内存)
      • 在内存中修改数据(此时磁盘数据仍是旧的)
      • 标记该页为 脏页(Dirty Page) ,后续由后台线程刷盘
    • 写入 redo log:为了保证数据持久性(Durability),InnoDB 会记录 redo log
      • 用于崩溃恢复(即使 MySQL 宕机,也能恢复已提交的事务)
      • 采用 WAL(Write-Ahead Logging) 机制,先写日志再写数据
    • 提交事务:事务提交(显式或隐式),InnoDB 会进行以下操作
      • 写入 binlog(二进制日志):
        • 用于主从复制(Replication)和点时间恢复(PITR)
        • 采用 两阶段提交(2PC) 保证 redo log 和 binlog 的一致性
      • 释放锁:释放行锁,其他事务可以访问该行
      • 刷盘(可选):
        • 根据 sync_binloginnodb_flush_log_at_trx_commit 决定是否立即刷盘
相关推荐
雨落倾城夏未凉20 分钟前
数据库编程应用--创建专题1
数据库
Leo.yuan1 小时前
数据安全中心是什么?如何做好数据安全管理?
大数据·运维·网络·数据库·数据仓库
文牧之1 小时前
PostgreSQL的扩展 auth_delay
运维·数据库·postgresql
微风轻吟挽歌2 小时前
TIDB创建索引失败 mkdir /tmp/tidb/tmp_ddl-4000/1370: no such file or directory.
数据库·oracle·tidb
betazhou2 小时前
Oracle expdp过滤部分表数据
数据库·oracle
燃犀知不可乎骤得3 小时前
qt控制台程序与qt窗口程序在读取数据库中文字段的差异!!巨坑
开发语言·数据库·qt
2301_768350234 小时前
Redis缓存-数据淘汰策略
数据库·redis·缓存
GUIQU.5 小时前
【Oracle】DCL语言
数据库·oracle
不剪发的Tony老师8 小时前
sqlite-vec:谁说SQLite不是向量数据库?
数据库·人工智能·sqlite
敲键盘的小夜猫9 小时前
Milvus向量Search查询综合案例实战(下)
数据库·python·milvus