MySQL中一条更新语句是怎么执行的?

假设,创建一个InnoDB表t并插入数据如下:

CREATE TABLE `t` (
  `id` int(11) NOT NULL,
  `a` int(11) DEFAULT NULL,
  `b` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `a` (`a`)
) ENGINE=InnoDB;
insert into t values(0,0,0),(5,5,5),
(10,10,10),(15,15,15),(20,20,20),(25,25,25);

现在执行一条修改语句:

update t set a=6 WHERE id =5;

假设当前数据库的隔离级别是可重复读,这条修改语句在mysql中是怎么执行流程是怎样的呢?

下面是详细的执行步骤:

1、查询解析和优化

  • MySQL首先解析更新语句,检查语法。
  • 然后优化器评估执行计划,选择使用哪个索引来定位记录。

2、打开表和索引

  • 打开表t,为其设置意向锁,准备修改数据。

3、InnoDB Buffer Pool

  • MySQL会检查要更新的行是否已经在InnoDB缓冲池(buffer pool)中。
  • 如果不在,则从磁盘读取到缓冲池。
  • 由于需要修改数据,因此这个行被标记为脏页(dirty page)。

4、锁定记录

  • 加锁阶段,因为使用的是可重复读隔离级别,InnoDB会对找到的索引记录加上next-key锁(结合行锁和gap锁),防止幻读。

5、Redo Log Buffer

  • 在数据被修改前,修改操作被写入到重做日志缓冲区(redo log buffer),确保数据库的持久性。

6、索引更新

  • 实际修改内存中缓冲池的记录,包括聚集索引的行记录以及任何受影响的二级索引(secondary index)。

7、Change Buffer

  • 如果涉及到非唯一二级索引的修改,并且目标页面不在缓冲池中,修改可能会首先写入到change buffer(之前称为插入缓冲insert buffer)中,以减少磁盘I/O操作。

8、Redo Log

  • redo log buffer中的内容刷(flush)到磁盘上的重做日志文件(redo log)中,通常是在事务提交时进行,确保即使发生崩溃,修改也不会丢失。

9、binlog_cache

  • 如果开启了二进制日志(用于复制和/或恢复),修改操作同时被格式化后写入到binlog_cache中。

10、事务提交

  • 用户如果执行了提交(COMMIT)操作,事务将会提交。
  • 提交时,redo log buffer的内容会被刷到磁盘上的redo log文件中(如果尚未刷盘)。
  • 此时,undo log记录也会生成,以便在必要时候可以回滚事务。

11、binlog

  • 提交时,binlog_cache中的内容会被刷(flush)到磁盘上的二进制日志文件(binlog)中。

12、清理锁和缓冲池写回

  • 提交完成后,释放所有在事务过程中获取的锁。
  • 最终,InnoDB后台线程会负责将脏页从缓冲池刷回到磁盘上。

整个流程需要注意的是,某些步骤可能会有重叠,并不是完全线性的,比如redo log buffer可能在修改操作进行的同时就逐渐被写入到redo log中,而不一定要等到整个事务提交。同时,为了提高效率,很多操作都是批量处理的,例如缓冲池的写回并不会立即发生,而是由InnoDB的后台线程按需批量刷盘。还有,锁的粒度和类型会根据实际操作、存在的索引、隔离级别等多种因素有所变化。

注意:不同的mysql版本执行流程也略有差异。

你可能感兴趣的:(mysql,mysql,数据库)