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