MySQL InnoDB mtr源码解析
Mini Transaction
MySQL InnoDB中,mtr是一个非常重要的模块,主要控制redo日志和数据页锁。
redo日志
redo日志即数据库的预写日志(WAL),有以下两个作用:
提高事务的提交速度。事务修改的数据页在磁盘上极有可能是不连续的,刷盘将会是随机IO,效率较差。事务会将所有对数据页的修改记录成redo日志,提交时只需保证这些redo日志刷盘成功即可,不需要等待修改的脏页落盘。redo日志是顺序写入磁盘的,效率较高,故redo日志可以提高事务的提交速度,提高数据库性能。
保证事务的持久性。事务提交时,对数据页的修改可能不会立即刷盘,而是将内存中的页标记为脏页并延迟刷盘。若脏页刷盘前数据库宕机,重启恢复时,由于内存的易失性,事务对数据页的修改丢失。此时由于事务的redo日志已刷盘成功,通过redo日志可还原该事务对数据页的修改并重做,以保证事务的持久性。
页锁
MySQL中数据页可以被并发事务访问,但需要加页锁,5.6版本只有S锁和X锁。访问数据页但不修改需要加S锁,S锁和S锁不冲突,即一个页可以同时被多个线程访问。若需要修改页数据,则需要加X锁,X锁和S锁、X锁冲突,即一个页只能同时被一个线程修改。
mtr可以记录下访问B树过程中对数据页加的页锁,放到mtr->memo中,在mtr_commit时会一起释放。mtr_commit时,会先将对数据页的修改的redo日志落盘,然后即可释放页锁。
mtr源码分析
MySQL中,redo日志需要先放到mtr->log,mtr_commit时刷盘。一个mtr的所有redo日志要么都落盘成功、要么都不落盘,不可能落盘一部分,所以一个mtr中的redo日志落盘具有原子性。
1
2
3
4
5
6
7
8
9
10
11
12
struct mtr_t{
dyn_array_t memo; // 记录mtr锁住的数据页
dyn_array_t log; // redo日志
unsigned inside_ibuf:1; // 内部ibuf是否改变
unsigned modifications:1; // mtr修改了buffer页
unsigned made_dirty:1; // mtr让至少一个buffer页变成脏页
ulint n_log_recs; // 写入mtr log的页初始化记录
ulint n_freed_pages; // mtr释放的页
ulint log_mode; // 决定哪些操作需要生成redo日志
lsn_t start_lsn; // 起始lsn
lsn_t end_lsn; // 结束lsn
};
mtr_start开启一个mtr,后续使用该mtr访问B树时,对数据页加的锁会被记录到memo中,对数据页的修改的redo日志会被记录到log中。
mtr_commit主要做了两件事:调用mtr_log_reserve_and_write将所有redo日志落盘,然后调用mtr_memo_pop_all将memo中记录的所有锁释放。
未完待续