假设,创建一个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版本执行流程也略有差异。