要搞清楚mysql的redo log,还是要先从buffer pool说起。
1.Buffer Pool
众所周知,mysql对数据的存储是以页为单位存储在磁盘上的,每个页面大小为16kb。innodb存储引擎在处理客户端请求时,会将访问到的数据所在的那个数据页整个加载到内存中。而且访问过后,会先该数据页暂存到内存中,而非立即写回磁盘。这么做的目的也很明确,就是为了避免磁盘I/O带来的巨大开销。这块内存,就叫Buffer Pool。它是InnoDB向操作系统申请的一段连续的内存空间。可以通过innodb_buffer_pool_size来调整它的大小。
InnoDB使用了一些链表来管理buffer pool.
主要的有: free链表,管理空闲的缓冲页;flush链表,管理脏页(即修改了但还没刷新回磁盘的数据页)链表;lru链表,buffer pool中没有空闲缓冲页时,用来淘汰某些页面的策略链表。
淘汰的可以是干净页(与磁盘数据页一致),也可以是脏页(还没有持久化的页面)。如果淘汰的是脏页,必须先把脏页flush到磁盘了,变成干净页了,才能重用。
可能触发flush的时机:
a.buffer pool满了 ,即内存不够用了,需要淘汰一些页面
b.redo log写慢了,即要写redo log的位置,追上了checkpoint的位置,需要将checkpoint位置前进一些,好留出空间继续写redo log。这个时候就会触发刷脏页到磁盘,推进checkpoint向前偏移。
c. 系统空闲的时候会刷脏页
d.系统正常关闭时,会在关闭前把脏页都刷到磁盘。
2.redo log
redo log中记录了事务执行过程中对数据页做了哪些修改。
2.1 redo log buffer
innodb为了解决磁盘速度慢的问题引入了buffer pool,对应的,也引入了用于缓存redo log的缓存区----redo log buffer,下文简称log buffer。该区域也是从操作系统申请的一段连续内存空间。
在事务执行过程中的修改会被先写入redo log buffer中。对页面的一次原子访问结束后,log buffer会写入一组或一个redo 日志记录,此外,还会将该次原子访问过程中修改过的页面加入到Buffer Pool中的flush链表中。 原子访问是:比如一条insert语句,但是由于可能会页分裂等操作导致修改多个数据页,从而产生多条redo log。虽然会产生多条redo log,但是innodb会保证这个过程是原子性的。
在以下情况会将redo log buffer写到磁盘中到redo log文件中 :
a. log buffer空间不足时。
b.事务提交时。
c.后台有个线程会大概以每秒一次到频率刷新log buffer到磁盘
d.正常关闭服务器
e.做checkpoint时。
2.2 redo log 日志文件组
log buffer中的日志是刷新到磁盘到redo log文件组中的。默认有ib_logfile0和ib_logfile1一组两个日志文件。一组redo log的大小是有限的,当redo log写满了后,会循环写,即后面的日志将会覆盖前面的日志。redo log判断否可以被覆盖的原理是:如果某redo log虽然已经持久化到redo log日志文件中了,但是它们修改过的数据页,假设为数据页a,还在buffer pool的flush链表中,那该redo log在磁盘中的空间将不能被覆盖。等到后面,页a被刷新到了磁盘,也就会从flush链表中移除了,这时候,这条redo log就没有用了,也就是可以被覆盖了。这种由于刷脏页而使得对应redo log可以被覆盖的操作,就是执行checkpoint.
之前讲过,buffer pool中的脏页是后台线程通过lru链表和flush链表刷新到磁盘到,这个过程是很慢的。如果当前系统修改页面很频繁,这样将会使得写redo log日志很频繁。这样,如果后台线程不能及时将脏页刷出,将会导致无法执行checkpoint,这样将没有空间来写redo log了。这种情况,将需要用户线程从flush链表把哪些最早修改(flush链表节点有字段标识修改时间,具体将是oldest_modifecation最小的)的脏页刷到磁盘,然后再去执行checkpoint。
3.崩溃恢复
redo log强调如何让已经提交的事务保持持久化。当系统崩溃后,再重启时将可以通过redo log将数据恢复到崩溃前的状态。因为要么执行结束的事务的产生redo log都持久化到磁盘了,要么执行完的事务产生的脏页已经被刷到磁盘了。具体恢复过程:从redo log文件找到最近的偏移量,一直根据redo log进行数据恢复,直到最后一个block为止。redo log是以block为单位的。该过程中,使用哈希表可以将对同一个页面修改的redo log放到同一个槽中,加速恢复过程,并且通过具体标记的值可以跳过已经刷新到磁盘中的页面。
mysql崩溃恢复策略:
1.如果redo log中又有prepare,又有完整的commit标识,则直接提交。
2.如果redo log中只有prepare标识,没有commit标识, 则判断binlog:若binlog是完整的,则直接提交;若binlog不完整,则回滚。
redo log和commit是通过XID关联起来的。binlog是有完整格式的,用来判断一个binlog是否完整。