MySQL 原理研究:mysql(innodb存储引擎)事务日志

来源:互联网 发布:java生成范围随机数 编辑:程序博客网 时间:2024/05/29 10:37


一、缘由:我的mysql中有两个文件(ib_logfile0、ib_logfile1),对于它们的作用等等,不甚了解。


二、内容:

ib_logfile0、ib_logfile1是mysql中innodb下的的事务日志,因为myisam不支持事务,所以也就没有事务日志了。可以通过innodb_log_file_size=50M参数设置大小。


事务日志的作用:

主要是用来保证innodb事务的安全性,通过在线redo日志和记录在表空间的undo信息来保证事务安全性,redo日志记录了innodb所有做的物理变更和事务信息。个人认为事务日志应该包括redo log和undo log,而redo日志则记录在上面的两个文件中。上面已经说了undo log记录在表空间里面。事务日志里面记录的是关于每个页的更改的物理情况。


LSN:日志序列号,innodb的日志序列号是一个64位的整数。


1.checkpoint写入:

innodb实现了fuzzy checkpoint的机制,每次取到最老的脏页,然后确保此脏页对应的LSN之前的LSN都已经写入了日志文件,再将此脏页的LSN作为checkpoint点记录到日志文件,意思就是"这个LSN之前的LSN对应的日志和数据都已经写入了磁盘文件"。恢复数据文件的时候,innodb扫描日志文件,当发现LSN小于checkpoint对应的LSN,就认为恢复已经完成。

checkpoint写入的位置在日志文件开头固定的偏移量处,即每次写checkpoint都覆盖之前的checkpoint信息。



2.管理机制:

日志生命周期和对应的日志信息:

创建事务日志-------------------------------------log sequence number(LSN1)

日志刷盘-----------------------------------------log flushed up to(LSN2)

数据刷盘-----------------------------------------oldest modified data log(LSN3)

写checkpoint-------------------------------------last checkpoint at(LSN4)


创建日志:事务创建一条日志。

日志刷新:日志写入到磁盘上的日志文件。

数据刷频:日志对应的脏数据写入到磁盘上的数据文件。

写checkpoint:日志被当做checkpoint写入日志文件。


对应的4个阶段,系统记录了上面的4个日志相关信息:

log sequence number(LSN1):表明事务日志产生到了什么位置。

log flushed up to(LSN2:表明写到事务日志的磁盘文件的位置。

oldest modified data log(LSN3):表明脏数据写入到数据文件的位置。

last checkpoint at(LSN4):表明事务日志文件和数据的磁盘文件都完成的位置。


对于mysql系统来说它们的关系为LSN1>=LSN2>=LSN3>=LSN4

我们可以看到我们自己的mysql中的log信息:

------------------------------------------------------------------------------------------------------

mysql> show engine innodb status\G

---

LOG

---

Log sequence number 1817745

Log flushed up to   1817745

Last checkpoint at  1817745

0 pending log writes, 0 pending chkp writes

285 log i/o's done, 0.00 log i/o's/second

-------------------------------------------------------------------------------------------------------



3.保护机制:

innodb的数据并不是实时写盘的,为了避免宕机时数据丢失,保证数据的ACID属性,innodb至少要保证数据对应的日志不能丢失,对于不同的情况。innodb采用不同的策略:


宕机导致日志丢失:

innodb有日志刷盘机制,可以通过innodb_flush_log_at_trx_commit参数进行控制。


日志覆盖导致日志丢失:

innodb日志文件大小是固定的,写入的时候通过取余来计算偏移量,这样存在两个LSN写入到同一位置的可能,后面写的把前面写的就覆盖掉了,例如:LSN=100000000和LSN=1600000000两个日志的偏移量是相同的。这种情况下,为了保证数据一致性,必须要求LSN=100000000对应的脏页数据都已经刷新到磁盘中,也就是要求last checkpoint对应的LSN一定要大于100000000,否则覆盖后日志也没有了,数据也没有刷盘,一旦宕机,数据就丢失了。


为了解决第二种情况导致数据丢失的问题,innodb实现了一套日志保护机制,如下所示:


O----ckp age----buf age----buf async----buf sync----ckp async----ckp sync----O

在上面,整条线代表了日志空间(log cap,约等于日志文件总大小*0.8,0.8是一个安全系数),ckp age和buf age是两个浮动的点,buf async、buf sync、ckp async、ckp sync是几个固定的点。概念如下:


ckp age  LSN1-LSN4  还没有做Checkpoint的日志范围,若Ckp age超过日志空间,说明被覆盖的日志(LSN1-LSN4-Log cap)对应日志和数据“可能”还没有刷到磁盘上。


buf age LSN1-LSN3   还没有将脏页刷盘的日志的范围,若Buf age超过日志空间,说明被覆盖的日志(LSN1-LSN3-Log cap)对应数据“肯定”还没有刷到磁盘上。


buf async 日志空间大小*7/8   

强制将Buf age-Buf async的脏页刷盘,此时事务还可以继续执行,所以为async,对事务的执行速度没有直接影响(有间接影响,例如CPU和磁盘更忙了,事务的执行速度可能受到影响)。


buf sync 日志空间大小*15/16   强制将2*(Buf age-Buf async)的脏页刷盘,此时事务停止执行,所以为sync,由于有大量的脏页刷盘,因此阻塞的时间比Ckp sync要长。


ckp async 日志空间大小*31/32   强制写Checkpoint,此时事务还可以继续执行,所以为async,对事务的执行速度没有影响(间接影响也不大,因为写Checkpoint的操作比较简单)。


ckp sync 日志空间大小*64/64   强制写Checkpoint,此时事务停止执行,所以为sync,但由于写Checkpoint的操作比较简单,即使阻塞,时间也很短。



当事务执行速度大于脏页刷盘速度时,Ckp age和Buf age会逐步增长,当达到async点的时候,强制进行脏页刷盘或者写Checkpoint,如果这样做还是赶不上事务执行的速度,则为了避免数据丢失,到达sync点的时候,会阻塞其它所有的事务,专门进行脏页刷盘或者写Checkpoint。

因此从理论上来说,只要事务执行速度大于脏页刷盘速度,最终都会触发日志保护机制,进而将事务阻塞,导致MySQL操作挂起。


由于写Checkpoint本身的操作相比写脏页要简单,耗费时间也要少得多,且Ckp sync点在Buf sync点之后,因此绝大部分的阻塞都是阻塞在了Buf sync点,这也是当事务阻塞的时候,IO很高的原因,因为这个时候在不断的刷脏页数据到磁盘。



4.数据恢复:


4.1系统故障造成数据库不一致的原因有两个:

4.1.1.未完成事务对数据库的更新,可能已写入数据库。

4.1.2.已提交事务对数据库的更新,可能还留在缓冲区没有来得及写入数据库。



4.2.恢复的方法:

4.2.1正向扫描日志文件(从头到尾),找出故障发生前已经提交的事务(存在begin transaction和commit记录),将其标示记入重做(redo)队列。同事找出故障发生时未完成的事务(只有begin transaction,没有commit),将其标识记入(undo)队列。


4.2.2对undo队列的各事务进行撤销处理。进行undo的处理方法是,反向扫描日志文件,对每个undo事务的更新操作执行反操作,即将日志记录中"更新前的值"写入数据库。


4.2.3对重做日志中的各事务进行重做操作。进行redo的处理方法是,正向扫描日志文件,对每个redo事务重新执行日志文件登记操作。即将日志中"更新后的值"写入数据库。


文章转载自:http://hi.baidu.com/green_lizard/item/051005d2736f910de1f46fc6

0 0