游乐游手机版
首页/数据库/文章详情

MySQL update语句执行流程深度解读与原理分析

时间:2026-06-13 06:58
update语句执行时,先查询BufferPool,若无则从磁盘加载并加独占锁;将旧数据写入undolog,在BufferPool中更新数据变为脏页;修改记录写入redologbuffer,提交时先刷redolog到磁盘,再生成binlog并刷盘,最后在redolog标记commit,脏页后续刷回磁盘。

图1 update语句执行流程

MySQL之update语句执行流程解读

首先检查Buffer Pool中是否存在该条记录,如果不存在,则需要从磁盘加载到缓冲池,随后为该行记录添加独占锁。

接下来,将更新前的旧数据写入undo log,该日志主要用于事务回滚时的数据恢复。

然后直接在Buffer Pool内执行数据更新操作,此时该数据块会变为脏页。

执行器顺手将修改记录写入redo log,需要注意的是,此时数据仍位于内存中的redo log buffer中。

在准备提交事务的阶段(即prepare阶段),根据配置策略将redo log从内存刷盘到磁盘上的redo log文件。

接着,执行器生成该次更新对应的binlog,并同样按照策略将binlog刷写到磁盘上的binlog文件。

执行器调用存储引擎的提交事务接口,完成最终的事务提交。这一步会将当前binlog文件名以及本次更新在binlog文件中的位置一并写入redo log文件,同时在redo log中添加一个commit标记。

如果后续触发了脏页刷新操作,则会将内存中更新后的脏数据刷回至磁盘。

图2 update语句执行流程框架图

MySQL之update语句执行流程解读

首先通过网络传输:客户端通过TCP/IP协议将SQL语句发送到Server层的SQL接口。

SQL接口接收请求后,先进行解析,同时验证用户权限是否匹配。

权限验证通过后,分析器介入,检查语句是否存在语法错误等问题。

随后优化器登场,生成相应的执行计划,并从中选择最优方案。

接着执行器按照执行计划真正执行该语句。

  • 过程中需要打开表(open table),如果表上存在MDL锁,则需等待。
  • 如果没有锁,则在表上添加一个短暂的MDL(S)锁。
  • 如果opend_table数值过大,说明open_table_cache设置偏小,会导致频繁打开frm文件。

进入引擎层后,首先从innodb_buffer_pool的data dictionary(元数据信息)中获取表结构信息。

根据元数据信息,到lock info中检查是否存在相关锁,同时将该update语句所需的锁信息写入lock info(锁机制本身还包含许多细节)。

接着,涉及变更的老数据通过快照方式存储到innodb_buffer_pool的undo page中,并记录undo log修改对应的redo日志。如果data page中已经有数据,则直接载入undo page;否则需要从磁盘读取相应page的数据再载入。

然后在innodb_buffer_pool的data page上执行update操作。物理数据页的修改记录会被写入redo log buffer中。由于一次update事务可能涉及多个页面的修改,因此redo log buffer中会存在多条页面修改信息。同时基于group commit机制,本次事务所产生的redo log buffer可能与其他事务一起被flush并sync到磁盘。

同时,修改信息会按照event格式记录到binlog_cache中。需要注意的是,binlog_cache_size是事务级别的参数,而非会话级别。一旦提交,dump线程就会主动将binlog_cache中的event发送给从库的I/O线程。

之后,这条SQL需要在二级索引上做的修改,会被写入change buffer page,等到下次有其他SQL需要读取该二级索引时,再与二级索引进行merge操作。

(从设计角度看,这种机制将随机I/O转换为顺序I/O,不过当前主流磁盘已普遍采用SSD,因此随机I/O与顺序I/O在寻址方面的差距并不显著。)

此时update语句已经完成,需要决定是提交还是回滚。这里仅讨论提交的情况。

  • 提交操作:由于存储引擎层与Server层之间采用内部XA协议(用于保证两个事务的一致性,主要是为了确保redo log和binlog的原子性),因此提交过程分为prepare阶段和commit阶段。
  • prepare阶段:将事务的xid写入,并对binlog_cache执行flush和sync操作(对于大事务而言,这一步极为耗时)。
  • commit阶段:由于之前该事务产生的redo log已经sync到磁盘,因此这一步只需在redo log中标记commit。

当binlog和redo log都成功落盘后,如果触发了脏页刷新操作,会先将脏页复制到doublewrite buffer中,再将doublewrite buffer的内容刷新到共享表空间,最后由page cleaner线程将脏页写入磁盘。

实际上在具体实现中,第5步调用了第6步的过程,因此本质上是同一回事。MySQL Server层和InnoDB层都各自保存了表结构,部分书籍在描述时会分开讲解。

总结

从整体来看,无论是Buffer Pool、undo log、redo log还是binlog,每一个环节紧密配合,再加上XA两阶段提交的可靠性保障,最终形成了一条完整且可靠的update执行链路。深入理解这一流程,很多MySQL故障排查和性能调优问题便有了清晰的入手方向。

来源:https://www.jb51.net/database/3631977sh.htm
上一篇MySQL数据库非空约束主键外键作用详解图文教程 下一篇如何使用SSMS还原.bak数据库备份文件的详细步骤
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
Redis 7.0增量AOF重写RDB前导码配置详解
数据库 · 2026-07-02

Redis 7.0增量AOF重写RDB前导码配置详解

先说一个几乎所有人都踩过的典型误区:很多人把 aof-use-rdb-preamble yes 当作开启“增量重写”的开关。实际上,这个配置只干了一件事——让重写后的 AOF 文件头部带上 RDB 快照。它解决的是加载速度问题,跟“增量重写”本身的概念压根不是一回事。真正的增量重写,依赖的是 Red

在Python Tornado异步框架中安全执行SQL命令的方法与最佳实践
数据库 · 2026-07-02

在Python Tornado异步框架中安全执行SQL命令的方法与最佳实践

直接在Tornado里用SQLAlchemy同步执行SQL,结果就是阻塞IOLoop,所谓“异步框架里写同步数据库代码”,等于白搭。安全执行的关键不是“怎么写SQL”,而是“怎么不卡住事件循环”。 为什么不能在RequestHandler里直接调用session execute() 因为sessio

利用SQL触发器实现在INSERT数据时自动同步到审计表
数据库 · 2026-07-02

利用SQL触发器实现在INSERT数据时自动同步到审计表

先说结论:可以用触发器把 INSERT 数据同步到审计表,但必须用 AFTER INSERT,并且审计表的字段顺序、类型、字符集得和源表严格一致。否则,轻则写入错位、数据截断,重则直接报错、丢数据。下面把这些坑一个一个掰开说。 能,但必须用 AFTER INSERT,且审计表字段顺序、类型、字符集要

如何用SQL编写按不同工作日统计员工出勤率
数据库 · 2026-07-02

如何用SQL编写按不同工作日统计员工出勤率

在实际业务中,统计不同工作日的出勤率是HR系统里的高频需求。如果直接按日期函数分组,很容易掉进语言环境、索引失效或分母口径的坑里。下面就来拆解具体的实现要点。 必须用 CASE WHEN 将日期映射为固定 weekday 标签(如 Mon )再分组,避免语言环境导致的分组断裂;需过滤 DOW IN

Spring Boot 3动态拼接SQL为何引发严重安全漏洞
数据库 · 2026-07-02

Spring Boot 3动态拼接SQL为何引发严重安全漏洞

SQL注入漏洞的核心成因,本质上是因为用户输入直接参与了SQL语句的字符串拼接,而未采用参数化绑定机制。在MyBatis中使用${}、QueryWrapper中调用apply()与last()、JPA的@Query注解进行拼接等操作,都会绕过PreparedStatement的安全防护。动态字段必须