InnoDB对于数据文件和日志文件的刷盘遵守WAL(Write ahead redo log)Force-log-at-commit两种规则,二者保证了事务的持久性。WAL要求数据的变更写入到磁盘前,首先必须将内存中的日志写入到磁盘;Force-log-at-commit要求当一个事务提交时,所有产生的日志都必须刷新到磁盘上,如果日志刷新成功后,缓冲池中的数据刷新到磁盘前数据库发生了宕机,那么重启时,数据库可以从redolog日志中恢复数据。
    image.png

    如上图所示,InnoDB在缓冲池中变更数据时,会首先将相关变更写入redolog buffer重做日志缓冲中,然后再按时或者当事务提交时写入磁盘,这符合Force-log-at-commit原则;当重做日志写入磁盘后,缓冲池中的变更数据才会依据checkpoint机制择时写入到磁盘中,这符合WAL原则。

    Buffer Pool中数据更新后的数据页称为脏页,将这些脏页罗盘的过程称为刷脏。当然刷脏页也有一定的策略,具体参考另一篇文章。MySQL刷脏页机制
    刷脏过程中会根据redolog日志,将 checkpoint 向前推进,推进的这部分日志对应的脏页就需要刷入磁盘。此时所有的更新全部阻塞,会导致性能抖动。

    在checkpoint择时机制中,就有重做日志文件写满的判断,所以,如前文所述,如果重做日志文件太小,经常被写满,就会频繁导致checkpoint将更改的数据写入磁盘,导致性能抖动。

    操作系统的文件系统是带有缓存的,当InnoDB向磁盘写入数据时,有可能只是写入到了文件系统的缓存中,没有真正的“落袋为安”。 InnoDB的innodb_flush_log_at_trx_commit属性可以控制每次事务提交时InnoDB的行为。当属性值为0时,事务提交时,不会对重做日志进行写入操作,而是等待主线程按时写入;当属性值为1时,事务提交时,会将重做日志写入文件系统缓存,并且调用文件系统的fsync,将文件系统缓冲中的数据真正写入磁盘存储,确保不会出现数据丢失;当属性值为2时,事务提交时,也会将日志文件写入文件系统缓存,但是不会调用fsync,而是让文件系统自己去判断何时将缓存写入磁盘。日志的刷盘机制如下图所示。
    Innodb落盘过程 - 图2
    innodb_flush_log_at_commit是InnoDB性能调优的一个基础参数,涉及InnoDB的写入效率和数据安全。

    • 当参数值为0时,写入效率最高,但是数据安全最低;
    • 参数值为1时,写入效率最低,但是数据安全最高;
    • 参数值为2时,二者都是中等水平。一般建议将该属性值设置为1,以获得较高的数据安全性,而且也只有设置为1,才能保证事务的持久性。

    参考文章
    MySQL探秘(三):InnoDB的内存结构和特性