美文网首页MySQL
[MySQL]浅谈InnoDB存储引擎(三)InnoDB的Mas

[MySQL]浅谈InnoDB存储引擎(三)InnoDB的Mas

作者: AbstractCulture | 来源:发表于2020-08-22 17:19 被阅读0次

    回顾

    上节我们对InnoDB的脏页处理技术CheckPoint进行了一些探险,这次我们来看看InnoDB存储引擎的主要工作线程-Master Thread。

    InnoDB 1.0.x之前的Master Thread

    Master Thread是InnoDB中线程优先级最高的线程。它的内部有多个轮询的方法:

    1. 主循环loop.
    2. 后台循环background loop.
    3. 刷新循环flush loop.
    4. 暂停循环suspend loop.

    Master Thread会跟进数据库的状态在这些循环中进行切换.

    主循环 Loop

    Loop被称为主循环,也就是大部分的工作都发生在这个循环中。它有两大部分的行为:

    • every second(每秒).
    • every 10 seconds(每10秒).

    以下用java伪代码来描述这个行为

        public static void masterThread() throws InterruptedException {
            while (true) {
                for (int i = 0; i < 10; i++) {
                    //每秒需要执行的任务,详细信息看下面解析
                    syncFlushRedoLogToDisk();
                    
                    if(lastOneSecondIOs < 5){
                        mergeMostFiveInsertBuffer();
                    }
                    
                    if(buf_get_modified_ratio_pct > innodb_max_dirty_pages_pct){
                        flushDirtyPages(100);
                    }
                    
                    if(userActivity.size() = 0){
                        backgroundLoop();
                    }
    
                    //如果有必要,那么睡眠 1S
                    if (isNecessary) {
                        Thread.sleep(1000);
                    }
                }
                // 每10秒需要执行的任务   
            }
        }
    

    需要注意的是,ifNecessary这个条件并不总是为true,所以1S和10S去执行这个时间是不准确的,这只是一个预估值。当负载较大时,会出现延迟行为.

    每秒需要做的操作
    • 日志缓冲刷新到磁盘,即使当前事务没有提交(always).

    即使某个事务还没有提交,InnoDB存储引擎仍然每秒会将重做日志缓冲中的内容刷新到重做日志。这也是为什么再大的事务的提交时间也可以变得很短的原因。

    • 合并插入缓冲(maybe).

    合并插入缓冲并不是每秒都会发生的,InnoDB存储引擎会判断当前1秒内发生的IO次数是否小于5次,如果小于5次,InnoDB认为当前的IO压力h很小,可以执行合并插入缓冲的操作。

    • 至多刷新100个InnoDB的缓冲池中的脏页到磁盘(maybe).

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

    • 如果当前没有用户活动,则切换到background loop(maybe).
    每10秒需要做的操作
    • 刷新100个脏页到磁盘(maybe).

    过去10秒内IO操作是否小于200次,如果是,InnoDB存储引擎认为当前有足够的磁盘IO操作能力,因此将100个脏页刷新到磁盘。

    • 合并最多5个插入缓冲(always).

    不同于1秒内的操作,每10秒总是会合并插入缓冲。

    • 将日志缓冲刷新到磁盘(always).

    与1秒内发生的行为一致

    • 删除无用的Undo页(always).

    full purge,删除无用的Undo页.对表进行updatedelete这类操作时,原先的行被标记为删除,但是因为一致性读(consistent read)的关系,需要保留这些行版本的信息。但是在full purge过程中,InnoDB存储引擎会判断之前版本的undo信息,如果可以删除,InnoDB会立即将其删除。每次最多回收20个undo页.

    • 刷新100个或者10个脏页到磁盘(always).

    如果有超过70%的脏页,刷新100个脏页到磁盘;如果脏页的比例小于70%,则只需要刷新10%的脏页到磁盘.

    根据以上信息,我们最终得到的伪代码为:

        public static void masterThread() throws InterruptedException {
            while (true) {
                for (int i = 0; i < 10; i++) {
                    // 每秒需要执行的任务
                    // 将日志信息刷新到磁盘
                    syncFlushRedoLogToDisk();
    
                    // 如果上一秒发生的IO大于5,则执行插入缓冲合并
                    if (lastOneSecondIOs < 5) {
                        mergeMostFiveInsertBuffer();
                    }
    
                    // 如果当前脏页比例超过90%,刷新100个脏页到磁盘
                    if (buf_get_modified_ratio_pct > innodb_max_dirty_pages_pct) {
                        flushDirtyPages(100);
                    }
    
                    // 如果当前没有用户活动,进入backgroundLoop
                    if (userActivity.size() = 0) {
                        backgroundLoop();
                    }
    
                    //如果有必要,那么睡眠 1S
                    if (isNecessary) {
                        Thread.sleep(1000);
                    }
                }
                // 每10秒需要执行的任务
                // 如果上一秒发生的IO大于200
                if (lastOneSecondIOs < 200) {
                    // 刷新100个脏页到磁盘
                    flushDirtyPages(100);
                }
                // 合并插入缓冲
                mergeMostFiveInsertBuffer();
                // 将日志信息刷新到磁盘
                syncFlushRedoLogToDisk();
                // 回收无用的undo页
                fullPurge();
    
                
                // 如果脏页比例>70%
                if (buf_get_modified_ratio_pct > 0.7) {
                    // 刷新100个脏页到磁盘
                    flushDirtyPages(100);
                } else {
                    // 刷新10个脏页到磁盘
                    flushDirtyPages(10);
                }
                // 如果当前没有用户活动,进入backgroundLoop
                if (userActivity.size() = 0) {
                    backgroundLoop();
                }
            }
        }
    
    background loop

    如果当前没有用户活动(数据库空闲时间)或者数据库关闭(shutdown),就会切换到这个循环。它执行的操作为:

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

    本文不对flush loop和suspend loop(挂起) 做介绍,有兴趣的读者可以访问mysql官方网站.

    InnoDB 1.2.x之前的缺陷

    • 对IO有限制。从解析中可以看到,刷新脏页数等都是固定死的参数,无法适配当前磁盘的发展。当前的SSD固态磁盘写入的性能得到了很好的改善,但是由于hard coding,这无法得到较好的配置。
    • innodb_max_dirty_pages_pct的默认值过大,想象一下你有90%的衣服没洗你才去洗衣服,这是多么悲催的事情。因此不要堆积过多的未完成事项。
    InnoDB对此做出的优化-innodb_io_capacity和innodb_max_dirty_pages_pct.

    innodb_io_capacity:

    解决这种写死参数的方法很简单,就是把它做出配置项,动态注入到代码中即可,对此,InnoDB提供了innodb_io_capacity让用户来对IO进行控制。默认值为200,它表示磁盘的IO吞吐量为200.
    配置:

    合并缓冲大小 = innodb_io_capacity * 5;
    刷新脏页数量 = innodb_io_capacity;
    

    如果你使用的是SSD的磁盘获取将部分磁盘做了RAID,也就是你的磁盘拥有更高的IO速度时,可以放心把innodb_io_capacity调大一些,直到符合磁盘的IO吞吐量为止。


    innodb_max_dirty_pages_pct:
    调整的过小也是不好的,这样会频繁地发生刷新脏页的IO操作。InnoDB官方设定的是75%,谷歌得出的结论是80%.这样,脏页的刷新频率得到了相应的提升,磁盘IO负载也得到了保证。


    innodb_purge_batch_size
    这个简单提一下,这是一个可以控制Undo页回收数量的参数,有需要可以进行调整。

    InnoDB 1.2.X版本的Master Thread

    InnoDB引入了Page Cleaner Thread.把刷新脏页的操作交给它来执行。这样减轻了Master Thread的工作,同时进一步提高了系统的并发性。

    相关文章

      网友评论

        本文标题:[MySQL]浅谈InnoDB存储引擎(三)InnoDB的Mas

        本文链接:https://www.haomeiwen.com/subject/nrwvjktx.html