6.innodb之Master thread 工作方式

1.前言

  innodb存储引擎的主要工作都是在一个单独的后台线程Master Thread中完成的,这一节将具体解释该线程的具体实现及该线程可能存在的问题。

2.innodb 1.0.x版本之前的 Master Thread

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

  Loop被称为主循环,因为大多数的操作是在这个循坏中,其中有两大部分的操作--每秒中的操作和每10秒的操作,  

void master_thread(){

loop:
for(int I=0; I<10; I++){
    
    do thing once per second
    sleep 1 second if necessary  
}
    do thing once per ten seconds
    goto loop;

}

  可以看到,loop循坏通过thread sleep 来实现,这意味着所谓的每秒一次或者每10秒一次的操作是不精确的,在负载很大的情况下可能会有延迟(delay),只能说大概在这个频率下。当然,innodb源代码中还通过了其他的方法来尽量保证这个频率。

  每秒一次的操作包括:

  • 日志缓冲刷新到磁盘,即使这个事务还没有提交(总是)
  • 合并插入缓冲(可能)
  • 至多刷新100个innodb的缓冲池中的脏页到磁盘(可能)
  • 如果当前没有用户活动,则切换到background loop(可能)

  即使某个事务还没有提交,innodb存储引擎仍然每秒会将重做日志缓冲中的内容刷新到重做日志文件。这一点是必须要知道的,因为这可以很好地解释为什么再大的事务提交(commit)的时间也是很短的。

  合并插入缓冲(insert buffer)并不是每秒都会发生的。innodb存储引擎会判断当前一秒内发生的iO次数是否小于5次,如果小于5次,innodb认为当前io压力很小,可以执行合并插入缓冲的操作。

  同样,刷新100个脏页也不是每秒都会发生的,innodb存储引擎通过判断当前缓冲池中脏页的比例(buf_get_modified_ratio_pct)是否超过了配置文件中innodb_max_dirty_pages_pct这个参数,如果超过了,innodb存储引擎认为需要做磁盘同步的操作,将100个脏页写入磁盘中。

  每10秒的操作包括

  • 刷新100个脏页到磁盘(可能的情况下)
  • 合并至多5个插入缓冲(总是)
  • 将日志缓冲刷新到磁盘(总是)
  • 删除无用的Undo页(总是)
  • 刷新100个或10个脏页到磁盘(总是)

  在以上的过程中,innodb存储引擎会先判断过去10秒之内磁盘的IO操作是否小于200次,如果是,innodb存储引擎认为当前有足够的磁盘io操作能力,因此将100个脏页刷新到磁盘。接着,innodb存储引擎会合并插入缓冲,不同于每秒一次操作时可能发生的合并插入缓冲操作,这次的合并插入缓冲操作总会在这个阶段进行。之后innodb存储引擎会再进行一次将日志缓冲刷新到磁盘的操作。这和每秒一次时发生的动作是一样的。

  接着innodb存储引擎会进行一步执行full purge操作,即删除无用的undo页。对表进行update、delete这类操作时,原先的行被标记为删除,但是因为一致性读(consistent read)的关系,需要保留浙西行版本的信息。但是在full purge过程中,innodb存储引擎会判断当前事务系统中已被刹车农户的行是否可以删除,比如有时候可能还有查询操作需要读取之前版本的undo信息,如果可以删除,innodb会立即将其删除。从源代码中可以发现,innodb存储引擎在执行full purge操作时,每次最多尝试回收20个undo页。

  然后,innodb存储引擎会判断缓冲池中脏页的比例(buf_get_modified_ratio_pct),如果有超过70%的脏页,则刷新100个脏页到磁盘,如果脏页的比例小于70%,则只需要刷新10%的脏页到磁盘。

  如下为主循环(main loop)的伪代码:

  6.innodb之Master thread 工作方式

  接着来看backuground loop,若当前没有用户活动(数据库空间时)或者数据库关闭(shutdown),就会切到这个循坏,background loop会执行以下操作:

  • 删除无用的Undo页(总是)
  • 合并20个插入缓冲(总是)
  • 跳回到主循环(总是)
  • 不断刷新100个页知道复合条件(可能,跳转到flush loop中完成)

  若flush loop中也没有什么事情可做了,innodb存储引擎会切换到suspend_loop,将Master thread挂起,等待事件发生。若用户启动(enable)了innodb存储引擎,且没有使用任何innodb存储引擎的表,那么Master Thread总是处于挂起的状态。

3.innodb1.2.x版本之前的Master thread

  在了解了1.0.x版本之前的Master Thread的具体实现过程后,细心的读者会发现innodb存储引擎对于IO其实是有限制的,在缓冲池向磁盘刷新时其实都做了一定的硬编码(hard coding)。在磁盘技术飞速发展的今天,当固态磁盘(SSD)出现时,这种规定在很大程度上限制了innodb存储引擎对磁盘IO的性能,尤其是写入性能。

  从前面的伪代码来看,无论和何时,innodb存储引擎最大只会刷新100个脏页到磁盘,合并20个插入缓冲。Master Thread似乎会‘忙不过来’,或者说它总是做的很慢。即使磁盘能在1秒内处理多于100个页的写入和20个插入缓冲的合并,但是由于hard conding,master thread也只会选在刷新100个脏页和20个插入缓冲。同时,当发生宕机需要恢复时,由于很多数据还没有刷新回磁盘,会导致恢复时间可能需要很久,尤其是对insert buffer来说。

  但是上述问题在innodb开发团队的优化下进行改写,因此从innodb1.2.x版本开始提供了参数innodb_io_capacity,用来表示磁盘IO的吞吐量,该值默认200,对于刷新到磁盘页的数量,会按照innodb_io_capacity的百分比来进行控制。规则如下:

  • 在合并插入缓冲时,合并插入缓冲的数量为innodb_io_capacity值的5%
  • 在从缓冲区刷新脏页时,刷新脏页的数量为innodb_io_capacity.
root@localhost 10:41:  [information_schema]> show variables like innodb_io_capacity;
+--------------------+-------+
| Variable_name      | Value |
+--------------------+-------+
| innodb_io_capacity | 200   |
+--------------------+-------+
1 row in set (0.00 sec)

  若用户使用了SSD类的磁盘,或者将几款磁盘做了RAID,当存储设备拥有更高的IO速度时,完全可以将Innodb_io_capacity的值调的再高一些,直到符合磁盘IO的吞吐量为止。

  关于innodb_max_dirty_pages_pct参数,在innodb 1.0.x之前的版本,是90%,但是由于该值太大,因此从innodb1.0.x开始,该值默认是75%, 最后经过google测试的80%的性能是最好的

  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通过判断产生重做日志(redo log)的速度来决定最合适刷新脏页数量,因此,当脏页的比例小于innodb_max_dirty_pages_pct时,也会刷新一定量的脏页

  还有一个改变量,之前每次进行full purge操作时,最多回收20个Undo页,从innodb1.0.x版本开始引入参数innodb_purge_batch_size,该参数可以控制每次full page回收的Undo页的数量,该参数的默认值为300.

  很多测试都显示,innodb1.0.x版本在性能方面取得了极大的提高,其实这个前面的Master thread的改动是密不可分的,因为innodb存储引擎的核心操作大部分都集中在Master Thread后台线程中。 

root@localhost 11:12:  [information_schema]> show engine innodb status\G;
*************************** 1. row ***************************
  Type: InnoDB
  Name: 
Status: 
=====================================
2021-09-05 11:12:25 0x7ff4ac099700 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 1 seconds
-----------------
BACKGROUND THREAD
-----------------
srv_master_thread loops: 2 srv_active, 0 srv_shutdown, 79591 srv_idle
srv_master_thread log flush and writes: 79593

4.innodb1.2.x版本的Master Thread

  在innodb 1.2.x版本中再次对Master Thread进行了优化,由此也可以看出Master Thread对性能所起到的关键性作用,在innodb 1.2.x版本中,Master thread的伪代码如下: 

if innodb is idle
    srv_master_do_idle_tasks();
else
    srv_master_do_active_tasks();

  其中srv_master_do_idle_tasks()就是之前版本中每10秒的操作,srv_master_do_active_tasks()处理的是之前每秒的操作,同时对于刷新脏页的操作,从Master Thread线程分离到一个单独的page cleaner thread 从而减轻了Master thread的工作,同时进一步提高了系统的并发性。

 

    

  

6.innodb之Master thread 工作方式

上一篇:常见C语言编译错误解析【转】


下一篇:详述Centos中的ftp命令的使用方法