首页 > 代码库 > InnoDB存储引擎之Master Thread

InnoDB存储引擎之Master Thread

    InnoDB存储引擎的主要工作都是在一个单独的后台线程Master Thread中完成的。
    1.InnoDB 1.0.x版本之前的Master Thread

        Master Thread具有最高的线程优先级别。其内部由多个循环组成:主循环(loop)、后台循环(backgroup loop)、刷新循环(flush loop)、暂停循环(suspend loop)。Master Thread会根据数据库运行的状态在上述4状态下进行切换。Loop被称为主循环,因为大多数的操作是在这个循环中,其中有两大部分的操作:每秒的操作和每10秒的操作。伪代码如下:


        可以看到,loop循环通过thread sleep来实现,这意味着所谓的每秒一次或者每10秒一次的操作是不精确的。在负载很高的情况可能会有延迟,只能说大概在这个频率下。当然InnoDB源代码中还通过其他的方法来尽量保证这个频率。
        每秒一次的操作:
            a.日志缓冲刷新到磁盘,即使这个事务还没有提交(总是);
                即使某个是我还没有提交,InnoDB存储引擎仍然每秒会将重做日志缓冲中的内容刷新到重做日志文件。这就可以解释为什么再大的事务提交的时间也很短。
            b.合并插入缓冲(可能);
                合并插入缓冲(Insert Buffer)并不是每秒都会发生。InnoDB存储引擎会判断当前一秒内发生的IO次数是否小于5次,如果小于5次,InnoDB认为当前的IO压力很小,可以执行合并插入缓冲的操作。
            c.至多刷新100个InnoDB的缓冲池中的脏页到磁盘(可能);
                InnoDB存储引擎通过判断当前缓冲池中脏页的比例(buf_get_modified_ratio_pct)是否超过了配置文件中innodb_max_dirty_pages_pct这个参数的值,如果超过了这个阀值,InnoDB存储引擎认为需要做磁盘同步操作,将100个脏页写入磁盘。
            d.如果当前没有用户活动,则切换到background loop(可能);
        每10秒的操作:
            a.刷新100个脏页到磁盘(可能);
            b.合并至多5个插入缓冲(总是);
            c.将日志缓冲刷新到磁盘(总是);
            d.删除无用的Undo页(总是);
            e.刷新100个或者10个脏页到磁盘(总是);
        在以上的过程中,InnoDB存储引擎会先判断过去10秒内磁盘的IO操作是否小于200次,如果是,InnoDB存储引擎认为当前有足够的磁盘IO操作能力,因此将100个脏页刷新到磁盘。接着,InnoDB存储引擎会合并插入缓冲,不同于每秒一次操作时可能发生的合并插入缓冲操作,这次的合并插入缓冲总是会进行。之后InnoDB存储引擎会在进行一次将日志刷新到磁盘。这和每秒一次时发生的操作是一样的。在接下来InnoDB存储引擎会执行full purge操作,即删除无用的Undo页。在full purge过程中,InnoDB存储引擎会判断当前事务系统中已被删除的行是否可以删除,如果可以,则InnoDB会立即将其删除。每次最多尝试回收20个undo页。然后,InnoDB存储引擎会判定缓冲池中脏页的比列如果有超过70%的脏页,则刷新100个脏页到磁盘。如果比例小于70%则只需要刷新10%的脏页到磁盘。
        若当前没有用户活动(上巨款空闲时)或者数据库关闭(shutdown),就会切换到这个循环。background loop会会执行以下操作:
            a.删除无用的Undo页(总是);
            b.合并20个插入缓冲(总是);
            c.跳回到主循环(总是);
            d.不断刷新100个页直到符合条件(可能,跳转到flush loop中完成);
        若flush loop中没什么事情可以做,InnoDB存储引擎会切换到suspend loop,将Master Thread挂起,等待时间的发生。若用户启用(enable)了InnoDB存储引擎,却没有使用任何InnoDB存储引擎的表,那么Master Thread总是处于挂起的状态。
    2.InnoDB 1.2.x版本之前的Master Thread
        在InnoDB1.0.x版本之前,InnoDB存储引擎对于IO其实是有限的,在缓冲池想磁盘刷新时其实都做了一定的硬编码(hard coding)。在SSD出现之后,这种规定在很大程度上限制了InnoDB存储引擎对磁盘IO的性能,尤其是写入性能。从前面的介绍来看,无论何时,InnoDB存储引擎最大只会刷新100个页到磁盘,合并20个插入缓冲。如果是在写入密集的引用程序中,每秒可能会产生大于100个的脏页,如果是产生大于20个插入缓冲的情况,Master Thread似乎总是会"忙不过来"。即使磁盘能在1秒内处理多余100个页的写入和20个插入缓冲的合并,但是由于hard coding,Master Thread也只会选择刷新100个脏页和合并20个插入缓冲。同时,当发生宕机需要恢复是,由于很多数据还没有刷新回磁盘,回到人恢复的时间肯能需要很久。针对这个问题InnoDB Plugin(从InnoDB1.0.x版本开始)提供了参数innodb_io_catacity,用来表示磁盘IO的吞吐量,默认200。对于刷新到磁盘页的数量,会按照innodb_io_capacity的百分比来进行控制。规则如下:
            a.在合并插入缓冲时,合并插入缓冲的数量为innodb_io_capacity值的50%;
            b.在从缓冲区刷新脏页时,刷新的脏页的数量为innodb_io_capacity;
        若用户使用了SSD类的磁盘,或者将几块磁盘做了RAID。当存储设备拥有更高的IO速度是,可以将innodb_io_capacity的值调大写,知道符合磁盘IO的吞吐量。InnoDB1.0.x版本的另一个参数innodb_adaptive_flushing(自适应地刷新),该值影响每秒刷新脏页的数量。原来的刷新规则是:脏页在缓存所占比例小于innodb_max_dirty_pages_pct时,不刷新脏页,大于innodb_max_dirty_pages_pct时,刷新100个脏页。随着innodb_adaptive_flushing参数的引入,InnoDB存储引擎会通过一个buf_flush_get_desired_flush_rate的函数来判断需要刷新脏页最合适的数量。大致的做法是通过判断产生重做日志的速度来决定最合适的刷新脏页数量。因此,当脏页的比列小于innodb_max_dirty_pages_pct时,也会刷新一定量的脏页。

        还有一个改变是:之前每次进行full purge操作时,最多回收20个Undo页。从InnoDB1.0.x版本开始引入参数innodb_purge_batch_size,该参数可以控制每次full purge回收的Undo页的数量,默认20。可以动态的对其进行修改。通过命令show engine innodb status可以查看档期Master Thread的状态信息。如下图:


        有图可知,主循环进行了1154432次,每秒挂起(sleep)的操作进行了1154432次,10秒一次的活动进行了106465次,background loop进行了90829次,flush loop进行了90829次。
    3.InnoDB 1.2.x版本的Master Thread
        InnoDB 1.2.x版本中,Master Thread的伪代码如下:
           
        其中srv_master_do_idle_tasks()就是之前版本中每10秒的操作,srv_master_do_active_tasks()处理的是之前每秒中的操作。对于刷新脏页的操作,从Master Thread线程分离到一个单独的Page Cleaner Thread,从而减轻了Master Thread的工作,同时进一步提高了系统的并发性。

InnoDB存储引擎之Master Thread