首页 >> 基础教程

一条更新语句是如何执行的?

  1. 连接与验证

    • 客户端应用程序(如你的代码、数据库管理工具)通过连接器与数据库服务器建立连接。

    • 连接器验证用户名、密码、主机权限等。验证通过后,建立连接并管理连接状态。

  2. 解析与优化

    1. 解析器: 接收 SQL 语句 UPDATE table_name SET column1 = value1, ... WHERE condition;

      1. 词法分析: 识别 SQL 中的关键字(UPDATESETWHERE)、表名、列名、值、操作符等。

      2. 语法分析: 检查 SQL 结构是否符合语法规则(例如,SET 后面是否跟了列赋值,WHERE 条件是否合法)。如果语法错误,返回错误

    2. 预处理器:

      1. 检查表名和列名是否存在。

      2. 解析 *(如果使用)。

      3. 检查用户是否有执行 UPDATE 操作的必要权限。

      4. 展开视图(如果更新的是视图)。

    3. 优化器:

      1. 生成该 UPDATE 语句的多种可能的执行计划。计划的核心在于如何高效地定位到需要更新的行(WHERE 条件)。

      2. 考虑因素包括:表大小、索引情况(是否有索引能快速定位行)、列统计信息等。

      3. 估算每种执行计划的成本(主要是 I/O 和 CPU 开销)。

      4. 选择它认为成本最低的执行计划。

      5. 关键决策点: 是使用主键索引、唯一索引、普通索引还是进行全表扫描来找到目标行?是否需要临时表?选择哪个索引效率最高?

  3. 执行器

    1. 根据优化器选定的执行计划进行操作。

    2. 调用存储引擎提供的接口,开始执行真正的数据更新。

  4. 存储引擎处理 (InnoDB 核心流程)

    1. 开启事务: InnoDB 默认在自动提交模式下,每条语句都是一个独立事务。如果显式开启了事务,则属于该事务的一部分。执行器会通知存储引擎开始一个新事务(或加入当前事务)。

    2. 定位数据:

      1. 根据执行计划,利用 B+树索引查找满足 WHERE 条件的行。

      2. 如果 WHERE 条件涉及索引列,通常能快速定位到目标行所在的数据页

      3. 如果无合适索引,则可能需要进行全表扫描(效率较低)。

    3. 读取旧值与 Undo Log:

      1. 目的 1 (回滚): 如果事务需要回滚(ROLLBACK),可以利用 Undo Log 将数据恢复到修改前的状态。

      2. 目的 2 (MVCC): 为其他正在进行的事务提供行数据的旧版本快照,保证它们能看到一致性视图。

      3. 读操作: 为了支持事务的隔离性(特别是 READ COMMITTED 及以上级别)和 MVCC,InnoDB 需要读取目标行当前的快照数据(可能是当前最新提交版本,也可能是符合当前事务隔离级别的可见版本)。

      4. 写 Undo Log: 在修改数据之前,InnoDB 会先将目标行的旧版本数据(修改前的数据)写入 Undo Log

    4. 修改数据页 (Buffer Pool):

      1. 将需要更新的行数据从磁盘加载到内存中的 Buffer Pool(如果还未在内存中)。

      2. 在内存中的 Buffer Pool 里,按照 SET 子句更新这些行的数据。此时磁盘上的数据并未改变

      3. 这些被修改的内存页称为 Dirty Page(脏页)。

    5. 写 Redo Log Buffer:

      1. 在修改 Buffer Pool 中的页之后,将描述这次物理修改操作的 Redo Log 记录写入内存中的 Redo Log Buffer

      2. Redo Log 记录的是物理操作(如“在某个数据页的某个偏移量修改了几个字节的数据”),而不是逻辑操作(UPDATE ...)。

      3. 目的 (持久性): 保证即使数据库发生崩溃,重启后也能利用 Redo Log 将未刷新到磁盘的脏页数据恢复出来(Crash Recovery)。遵循 Write-Ahead Logging 原则:日志先于数据页落盘。

    6. 处理二级索引:

      1. 如果更新的列包含在二级索引中,需要同步更新这些索引。

      2. InnoDB 使用 Change Buffer 来优化非唯一二级索引的更新操作(特别是当索引页不在 Buffer Pool 中时)。修改先记录在 Change Buffer,之后在合适的时机(如索引页被读取时、后台线程刷脏时)再合并到索引页。这减少了随机 I/O。

      3. 唯一索引的更新需要立即检查唯一性约束,因此无法使用 Change Buffer

  5. 提交事务 (关键的两阶段提交)

    1. 当执行 COMMIT 语句时(或自动提交模式下语句执行完成时),事务进入提交阶段。

    2. Prepare Phase:

      1. 将 Redo Log Buffer 中与该事务相关的所有 Redo Log 记录刷盘(持久化到磁盘上的 Redo Log 文件)。此时 Redo Log 记录了所有修改,足以保证崩溃恢复。

      2. 在 Redo Log 中写入一个特殊的 PREPARE 记录,标记事务进入准备状态。

    3. Write Binlog:

      1. Binlog: 是 MySQL Server 层的逻辑日志(Statement/Row/Mixed 格式),记录数据修改的逻辑操作(如 SQL 语句本身或行的前后镜像)。用于主从复制、数据恢复等。

      2. 将本次事务产生的 Binlog 写入磁盘上的 Binlog 文件。

    4. Commit Phase:

      1. 在 Redo Log 中写入一个 COMMIT 记录。

      2. 至此,事务才算正式提交成功。客户端收到确认。

      3. 关键点: 这个两阶段提交(先 Redo Prepare + Binlog 写盘,然后 Redo Commit)保证了 Binlog 和存储引擎数据之间的一致性。即使系统崩溃,重启恢复时也能根据 Redo Log 和 Binlog 的状态决定是提交还是回滚事务。

  6. 后台异步刷脏

    1. 事务提交后,被修改的脏页(Dirty Page)仍然在 Buffer Pool 内存中。

    2. InnoDB 的后台线程(如 Page Cleaner Thread)会在合适的时机(根据检查点机制、Buffer Pool 空间压力等)将脏页异步地刷新(Flush)到磁盘上的数据文件(.ibd 文件)中。这通常发生在系统相对空闲时。

    3. 注意: 即使脏页还没刷盘,只要 Redo Log 已经持久化,数据库就能保证数据不丢失(崩溃恢复时 Redo Log 可以重做修改)。


最新文章
InnoDB 和 MyISAM 主要有什么区别?2025-07-06
mysql存储引擎应该怎么选择?2025-07-06
mysql的几种存储引擎2025-07-06
MySQL 的段区页行2025-07-06
一条更新语句是如何执行的?2025-07-06
mysql中一条查询语句是如何执行的?2025-07-02
MySQL基础架构及执行流程解析2025-07-02
MySQL SQL语法树解析过程详解2025-07-02
mysql中SQL 的隐式数据类型转换?2025-07-01
MySQL 第 3-10 条记录怎么查?2025-06-30
备案号:蜀ICP备2023042032号-1