美文网首页
MySQL/InnoDB trx_rseg_history_le

MySQL/InnoDB trx_rseg_history_le

作者: 王晓宇_xiaoyuwang | 来源:发表于2018-08-02 22:04 被阅读0次

    1. 故事的起源

    我们在学习MySQL/InnoDB purge的过程中,使用select name, subsystem, count from information_schema.innodb_metrics where name="trx_rseg_history_len";查看系统当前回滚段历史链表长度(可以把这个历史链表近似理解为:尚未被清理的Undo物理页面),效果如下:

    然而,我们发现即使在无负载时,trx_rseg_history_len也不会降低为0,这一点,网上早有反馈https://bugs.mysql.com/bug.php?id=76750,阿里印风给出了解答https://yq.aliyun.com/articles/400891?spm=a2c4e.11155435.0.0.23f84f18pf0kqh。我们在MySQL8.0.3-rc版本上发现了此问题,所以基于MySQL8.0.3-rc,我们展开讨论。

    2.trx_rseg_history_len是什么?

    我们可以将trx_rseg_history_len近似地理解为系统中尚未被清理的Undo物理页面数。
    查询trx_rseg_history_len,实际查询的是trx_sys.rseg_history_len,那么trx_sys.rseg_history_len在什么条件下增长、什么条件下缩减呢?

    2.1 trx_sys.rseg_history_len的增长

    事务提交时,update类型的undo页面将被添加到历史链表,此时trx_sys->rseg_history_len随之+1。

    trx_commit-->trx_commit_low-->trx_write_serialisation_history-->trx_undo_update_cleanup-->trx_purge_add_update_undo_to_history-->os_atomic_increment_ulint(&trx_sys->rseg_history_len, 1)
    

    2.2 trx_sys.rseg_history_len的缩减

    Purge线程清理Undo页面时,将Undo页从历史链表移除,此时trx_sys->rseg_history_len随之-1。

     srv_do_purge-->trx_purge-->trx_purge_truncate-->trx_purge_truncate_history-->trx_purge_truncate_rseg_history-->trx_purge_free_segment-->trx_purge_remove_log_hdr-->os_atomic_decrement_ulint(&trx_sys->rseg_history_len, 1)
    

    3. Purge的工作机制?

    既然trx_sys->rseg_history_len不能降回0,那么我们就关注Purge为何不能将其降0,从Purge线程说起。

    3.1 Purge协调线程

    Purge coordinator线程,其大致逻辑如下:

    srv_purge_coordinator_thread //Purge coordinator线程函数主体
      srv_do_purge
        trx_purge
          srv_que_task_enqueue_low //如果需要的话,唤醒一些Purge工作线程
          que_run_threads //协调线程本身也purge数据行
          trx_purge_truncate //清理Undo表空间,和2.2对应上了!
    

    3.2 Purge工作线程

    聚焦trx_purge_truncate上下文,不介绍Purge工作线程

    3.3 为什么不能降0?

    说回到2.2的函数调用过程,单说下面这部分,只要执行trx_purge_truncate,一定会调用后续函数(期间无分支跳出此调用过程),最终trx_sys->rseg_history_len - 1。

    trx_purge_truncate-->trx_purge_truncate_history-->trx_purge_truncate_rseg_history-->trx_purge_free_segment-->trx_purge_remove_log_hdr-->os_atomic_decrement_ulint(&trx_sys->rseg_history_len, 1)
    

    那么,进入trx_purge_truncate的条件是什么?
    trx_purge调用trx_purge_truncate:

    trx_purge(ulint n_purge_threads, ulint batch_size, bool truncate)
    {
      ...
      if (truncate || srv_upgrade_old_undo_found) {
        trx_purge_truncate();
      }
      ...
    }
    

    srv_do_purge调用trx_purge:

    srv_do_purge(ulint n_threads, ulint* n_total_purged)
    {
      ...
      n_pages_purged = trx_purge(n_use_threads, 
                        srv_purge_batch_size, 
                        (++count % rseg_truncate_frequency) == 0);
      ...
    }
    

    可以看到,满足(++count % rseg_truncate_frequency) == 0则进入trx_purge_truncate。
    那么count是什么?rseg_truncate_frequency又是什么?

    srv_do_purge(ulint n_threads, ulint* n_total_purged)
    {
      ...
      static ulint count = 0; //count记录了执行trx_purge的次数
      ...
      ulint rseg_truncate_frequency = ut_min(
                static_cast<ulint>(srv_purge_rseg_truncate_frequency),
                undo_trunc_freq); //rseg_truncate_frequency是truncate undo表空间的频率,缺省值128
    }
    

    count和rseg_truncate_frequency共同实现了:每执行rseg_truncate_frequency次trx_purge,truncate一次undo表空间,清理undo物理页面。

    3.4 小结

    如果Purge线程执行了n次,n%rseg_truncate_frequency != 0,则n%rseg_truncate_frequency个Undo页面得不到清理,导致:

    trx_sys->rseg_history_len = n % rseg_truncate_frequency
    

    比如,trx_purge调用127次即清理完全部Undo信息,则这127个Undo页面,就不被清理。

    4. 实验

    为验证上述想法,我们修改了srv_do_purge,使得每次执行trx_purge都truncate表空间(这样会带来大量小I/O,影响性能)。

    srv_do_purge(ulint n_threads, ulint* n_total_purged)
    {
      ...
      n_pages_purged = trx_purge(n_use_threads, 
                        srv_purge_batch_size, 1);
      ...
    }
    

    我们使用Sysbench对此实例压测,产生足量Undo信息后等待Purge线程清理完成,最终可观察到,trx_sys->rseg_history_len = 0,如下示:


    5. 最后

    5.1 结论

    为了减少I/O,Purge线程每执行一定次数进行一次Undo物理页面清理工作,导致了查询trx_rseg_history_len无法归0。

    5.2 trx_rseg_history_len !=0 有啥影响吗?

    历史数据的purge工作已经完成,保证了数据正确性。只是Undo物理页面滞后清理,后果是无用数据占用磁盘久一点,但是换取了较少的I/O啊!

    5.3 为什么MySQL实例启动立即查询,trx_rseg_history_len !=0?

    MySQL在启动时,会执行一些语句(具体内容还不清楚,参考scripts目录,该目录内容在实例初始化时会被执行),产生Undo信息。

    5.4 暂时没有被清理的Undo页面怎么办?

    Purge线程再次激活、trx_purge满rseg_truncate_frequency次时会清理的;
    MySQL实例shutdown时,会清理Undo表空间。

    相关文章

      网友评论

          本文标题:MySQL/InnoDB trx_rseg_history_le

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