MySQL(InnoDB剖析):07---Master Thread(MySQL后台核心线程)

Master Thread

  • 通过前面介绍InnoDB体系架构我们知道,InnoDB存储引擎的主要工作都是在一个单独的后台线程Master Thread中完成的,本文就介绍该线程的具体实现以及该线程可能存在的问题

一、InnoDB 1.0.x版本之前的Master Thread

  • Master Thread具有最高的线程优先级别。其内部由多个循环(loop)组成:
    • 主循环(loop)、后台循环(backgrounp loop)、刷新循环(flush loop)、暂停循环(suspend loop)
    • Master Thread会根据数据库运行的状态在各种循环之间进行切换

主循环(Loop)

  • Loop被称为主循环,因为大多数的操作是在这个循环中,其中有两大部分的操作——每秒钟的操作和每10秒钟的操作
  • 伪代码如下,可以看到:
    • loop循环通过thread sleep来实现,这意味着所谓的每秒一次或每10秒一次的操作是不精确的。在负载很大的情况下可能会有延迟,只能说大概在这个频率下。当然,InnoDB源代码中还通过了其他方法来尽量保证这个频率

  • 每秒一次的操作如下:
    • ①日志缓冲刷新到磁盘,即使这个事务还没有提交(总是):
      • 即使某个事务还没有提交,InnoDB仍然每秒会将重做日志缓冲中的内容刷新到重做日志文件。这一点是必须要知道的,因为这可以很好地解释为什么再大的事务提交(commit)的时间也是很短的
    • ②合并插入缓冲(可能):
      • 合并插入缓冲(Insert Buffer)并不是每秒都会发生的。InnoDB会判断当前一秒内发生的IO次数是否小于5次,如果小于5次,InnoDB认为当前的IO压力很小,可以执行合并插入缓冲的操作
    • ③至多刷新100个InnoDB的缓冲池中的脏页到磁盘(可能):
      • 刷新100个脏页也不是每秒都会发生的。InnoDB通过判断当前缓冲池脏页的比例(buf_get_modified_ratio_pct)是否超过了配置文件中innodb_max_dirty_pages_pct这个参数(默认为90,代表90%),如果超过了这个阈值,InnoDB认为需要做磁盘同步操作,将100个脏页写入磁盘
    • ④如果当前没有用户活动,则切换到background loop(可能):
  • 根据每秒一次的操作,对主循环的伪代码具体化如下:

  • 每10秒一次的操作如下:
    • ①刷新100个脏页到磁盘(可能的情况下)
      • InnoDB会先判断过去10秒之内磁盘IO操作是否小于200次,如果是,InnoDB认为当前有足够的磁盘IO操作能力,因此将100个脏页刷新到磁盘
    • ②合并至多5个插入缓冲(总是)
      • InnoDB会合并插入缓冲。不同于每秒一次操作时可能发生的合并插入缓冲操作,这次的合并插入缓冲操作总会在这个阶段进行
    • ③将日志缓冲刷新到磁盘(总是)
      • InnoDB会再进行一次日志缓冲刷新到磁盘的操作。这和每秒一次发生的操作是一样的
    • ④删除无用的Undo页(总是)
      • InnoDB会执行“full purge”清理操作,删除无用的Undo页
      • 对表进行update、delete这类操作时,原先的行被标记为删除,但是因为一致性读的关系,需要保留这些行版本的信息。但在full purge过程中,InnoDB会判断当前事务系统中已被删除的行是否可以删除,比如有时候可能还有查询操作需要读取之前版本的undo信息,如果可以删除,InnoDB会立即将其删除
      • 从源代码中可以发现,InnoDB在执行full purge操作时,每次最多尝试回收20个undo页
    • ⑤刷新100个或者10个脏页到磁盘(总是)
      • InnoDB会判断缓冲池中脏页的比例(buf_get_modified_ratio_pct),如果有超过70%的脏页,则刷新100个脏页到磁盘,如果脏页的比例小于70%,则只需要刷新10%的脏页到磁盘
    • 通过上面的所有步骤,可以将主循环的伪代码归结为如下形式:

后台循环(background loop)

  • 若当前没有用户活动(数据库空闲时)或者数据库关闭(shutdown),就会切换到这个循环
  • background loop会执行以下操作:
    • 删除无用的Undo页(总是)
    • 合并20个插入缓冲(总是)
    • 跳回到主循环(总是)
    • 不断刷新100个页直到符合条件(可能,跳转到flush loop中完成)

暂停循环(suspend loop)

  • 若flush loop中也没有什么事情可以做了,InnoDB会切换到suspend loop,将Master Thread挂起,等待事件的发生
  • 若用户启用(enable)了InnoDB存储引擎,却没有使用任何InnoDB存储引擎的表,那么Master Thread总是处于挂起的状态
  • 最终,Master Thread完整的伪代码如下:

二、InnoDB 1.2.x版本之前的Master Thread

InnoDB 1.0.x版本之前的缺陷

  • 通过上面对InnoDB 1.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个插入缓冲。同时,当发生宕机需要回复时,由于很多数据还没有刷新回磁盘,会导致恢复的时间可能需要很久,尤其是对于insert buffer来说
  • 这些问题最初由Google的工程师Mark Callaghan提出

innodb_io_capacity参数

  • 针对上面的问题,InnoDB官当对齐进行了修正并发布了补丁。InnoDB的开发团队参考了Google的补丁,提供了类似的方法来修正该问题
  • InnoDB Plugin(从InnoDB 1.0.x开始)提供了参数innodb_io_capacity,用来表示磁盘IO的吞吐量,默认值为200。对于刷新到磁盘页的数量,会按照innodb_io_capacity的百分比进行控制。规则如下:
    • 在合并插入缓冲时,合并插入缓冲的数量为innodb_io_capacity值的5%
    • 在从缓冲区刷新脏页时,刷新脏页的数量为innodb_io_capacity
show variables like 'innodb_io_capacity'\G;

  • 如果用户使用了SSD类的磁盘,或者将几块磁盘做了RAID,当存储设备拥有更高的IO速度时,完全可以将innodb_io_capacity的值调得高一点,直到符合磁盘IO的吞吐量位置

innodb_max_dirty_pages_pct的默认值问题

  • 在InnoDB1.0.x版本之前,该值的默认为90,意味着脏页占缓冲池的90%才会进行Checkpoint。但是该值“太大”了,因为InnoDB存储引擎在每秒刷新缓冲池和flush loop时会判断这个值,如果该值大于innodb_max_dirty_pages_pct,才刷新100个脏页,如果有很大的内存,或者数据库服务器的压力很大,这时刷新脏页的速度反而会降低。同样,在数据库的恢复节点可能需要更多的时间
  • 在很多论坛上都有对这个问题的讨论,哟人甚至将这个值调到了20或10,但是实验证明将innodb_max_dirty_pages_pct调到20或10会增加磁盘的压力,系统的负担还是会有所增加的。Google在这个问题上进行了测试,证明20并不是一个最优值。从Innodb 1.0.x版本开始,innodb_max_dirty_pages_pct默认值变成了75,和Google测试的80比较接近,这样既可以加快刷新脏页的频率,又能保证了磁盘IO的负载

innodb_adaptive_flushing参数

  • innodb_adaptive_flushing(自适应地刷新)是InnoDB 1.0.x版本带来的另一个参数,该值影响每秒刷新脏页的数量
  • 该参数默认关闭
  • 原来的刷新规则是:
    • 脏页在缓冲池所占的比例小于innodb_max_dirty_pages_pct时,不刷新脏页
    • 大于innodb_max_dirty_pages_pct时,刷新100个脏页
  • 随着innodb_adaptive_flushing参数的引入,InnoDB存储引擎会通过一个名为“buf_flush_get_desired_flush_rate”的函数来判断需要刷新脏页最合适的数量。粗略地翻阅源代码后发现该函数通过判断产生重做日志(redo log)的速度来决定最合适的刷新脏页数量。因此,当脏页的比例小于innodb_max_dirty_pages_pct时,也会刷新一定量的脏页
show variables like 'innodb_adaptive_flushing'\G;

innodb_purde_batch_size参数

  • 之前每次进行full purge操作时,最多回收20个Undo页(在上面的主循环loop有介绍)
  • 从InnoDB 1.0.x版本开始引入了参数innodb_purde_batch_size,该参数可以控制每次full purge回收的Undo页的数量
  • 该参数的默认值为20,并可以动态地对其进行修改
show variables like 'innodb_purde_batch_size'\G;

查看Master Thread的状态

  • 从InnoDB 1.0.x开始,可以从下面命令的信息中查看当前Master Thread的信息:
    • 主循环进行了1次
    • 每秒挂起(sleep)的操作进行了1次
    • 10秒一次的活动进行了10次
    • background loop进行了1次
    • flush loop也进行了1次
show engine innodb status\G;

  • 备注:在数据库负载压力小的情况下,1_second和sleeps的值应该会相等。但是当有负载压力下,两者就并不相等了。通常,通过这两者的差值来反映当前数据库的负载压力
  • 从InnoDB 1.0.x版本开始,Master Thread的伪代码有所改变,最终如下:

三、InnoDB 1.2.x版本的Master Thread

  • 在InnoDB 1.2.x版本中再次对Master Thread进行了优化,由此可以看出Master Thread对性能所起到的关键作用
  • 在InnoDB 1.2.x版本中,Mastert Thread的伪代码如下:
    • srv_master_do_idle_tasks():就是上面介绍的每10秒的操作
    • srv_master_do_active_tasks():是之前每秒中的操作
    • 对于刷新脏页的操作:从Master Thread线程分离到一个单独的Page CLeaner Thread,从而减轻了Master Thread的工作,同时进一步提高了系统的并发性

发布了1367 篇原创文章 · 获赞 918 · 访问量 27万+

猜你喜欢

转载自blog.csdn.net/qq_41453285/article/details/104095240