作者:八怪(高鹏) 中亦科技数据库专家

水平有限,如有错误请谅解。源码版本8.0.21。

在处理一个故障的时候怀疑大量的删除数据导致了查询比较慢,但是自己对purge线程的工作流程一直不太清楚,本文不做深入解析,只做工作流程解析,待着如下问题进行:

del flag记录是否能够及时清理

为什么History list length持续不为0,是否代表del flag记录没有清理

purge线程触发的规则是什么

一、purge线程综述

一般来讲我们理解的purge线程可以做如下的工作:

清理del flag标签的记录

清理undo的历史版本

如果需要进行undo tablespace截断。

其包含一个协调线程和多个工作线程由如下参数设置:

innodb_purge_threads=4

这代表1个协调线程和3个工作线程。协调线程也会充当一个工作线程角色。

二、协调线程循环检测变化

如下调入:

srv_purge_coordinator_thread

->srv_purge_coordinator_suspend

判断如下:

(rseg_history_len <= trx_sys->rseg_history_len) {

//如果当前history_len大于等于上一次循环的的history_len

ret =os_event_wait_time_low(slot->event, SRV_PURGE_MAX_TIMEOUT, sig_count);

//等待10毫秒后进行处理或者等待被唤醒

唤醒的条件是有事务提交或者回滚

/* Tell server some activity has happened, since the trx

does changes something. Background utility threads like

master thread, purge thread or page_cleaner thread might

have some work to do. */

srv_active_wake_master_thread();

但是需要注意的是如果长期没有新的事务进行提交,那么可能进入永久堵塞状态而不是每10毫秒醒来,直到唤醒

if (ret == OS_SYNC_TIME_EXCEEDED) { //如果是等待超时

if (rseg_history_len == trx_sys->rseg_history_len &&

trx_sys->rseg_history_len

stop = true; //设置为true,进行无限期等待,直到唤醒

}

三、克隆最老的read view

这一步没什么好说的,因为清理undo需要根据当前最老的read view来清理,否则可能清理到正在读取需要的undo。

如下调入:

srv_purge_coordinator_thread

->srv_do_purge

->trx_purge

操作如下:

trx_sys->mvcc->clone_oldest_view(&purge_sys->view); //克隆老的 read view srv_do_purge

四、从可能需要清理的purge_queue中取出undo segment(简单理解为事务)

调入如下:

srv_purge_coordinator_thread

->srv_do_purge

->trx_purge

->trx_purge_attach_undo_recs

->trx_purge_fetch_next_rec

->TrxUndoRsegsIterator::set_next

操作如下:

const page_size_t &page_size = purge_sys->rseg_iter->set_next();

注意这里是一个迭代器,迭代的就是purge_sys->purge_queue,这是std::priority_queue实现的优先队列。具体迭代的代码如下:

while (!m_purge_sys->purge_queue->empty()) { //如果有事务需要清理

if (m_trx_undo_rsegs.get_trx_no() == UINT64_UNDEFINED) {

m_trx_undo_rsegs = purge_sys->purge_queue->top();

} else if (purge_sys->purge_queue->top().get_trx_no() ==

m_trx_undo_rsegs.get_trx_no()) {

m_trx_undo_rsegs.append(purge_sys->purge_queue->top()); //弹出一个

} else {

break;

}

而事务进入purge_queue是在事务commit的时候调用trx_serialisation_number_get

purge_sys->purge_queue->push(elem);

因此到这里我们知道事务提交的时候可能会唤醒purge协调线程进行工作,并且会加入可能需要purge的事务队列purge_queue中。

五、判断是否符合清理规则

调入如下:

srv_purge_coordinator_thread

->srv_do_purge

->trx_purge

->trx_purge_attach_undo_recs

->trx_purge_fetch_next_rec

判断如下:

if (purge_sys->iter.trx_no >= purge_sys->view.low_limit_no()) {

return (nullptr);

}

这里就是判断是否需要清理事务的trx no是否大于了oldest read view的low limit no,如果不满足则返回为nullptr,如果符合那么返回需要清理的page数量,并且指向下一个需要清理的undo segment。

六、每次清理默认为300个page

这个值由参数innodb_purge_batch_size进行控制,默认为300

调入如下:

srv_purge_coordinator_thread

->srv_do_purge

->trx_purge

->trx_purge_attach_undo_recs

生效如下:

for (ulint i = 0; n_pages_handled

清理流程会一致持续到没有page需要清理为止

调入如下:

srv_purge_coordinator_thread

->srv_do_purge

判断如下:

(!srv_purge_should_exit(n_pages_purged) && n_pages_purged > 0 &&

purge_sys->state == PURGE_STATE_RUN);

//清理完成后n_pages_purged > 0 将不会满足

return (rseg_history_len); //返回 rseg_history_len

七、工作线程处理

分发给工作线程后进入如下调用,进行del flag的清理,没有仔细的看这部分,调用比较复杂。但是可以肯定是其构造row_purge_parse_undo_rec)和删除过程可能需要大量的循环和数据定位(btr_cur_search_to_nth_level)操作。

srv_worker_thread

->srv_task_execute

->que_run_threads

->que_run_threads_low

->que_thr_step

->row_purge_step

->row_purge

->row_purge_record_func

八、默认每128次batch undo清理会进行undo history清理

这个和参数innodb_purge_rseg_truncate_frequency的设置有关,默认为128,如果满负荷计算为 :

300(undo log pages)*128(truncate frequency ) = 38,400

38400个undo log pages处理完成后会进行一次undo history清理。

根据参数赋值

set_rseg_truncate_frequency(

static_cast(srv_purge_rseg_truncate_frequency));

参数判断

ulint rseg_truncate_frequency = ut_min(

static_cast(srv_purge_rseg_truncate_frequency), undo_trunc_freq); //128

n_pages_purged = trx_purge(n_use_threads, srv_purge_batch_size,

(++count % rseg_truncate_frequency) == 0);//每128次进行一次清理

判断是否进入truncate流程

if (truncate || srv_upgrade_old_undo_found) { //truncate就是根据(++count % rseg_truncate_frequency)计算而来

trx_purge_truncate();

}

但是需要注意的count是一个static局部变量,因此每次调入函数会继续上次的取值继续计数。如果压力很小那么undo可能不能及时清理:

小事务 如果都是小事务那么每个事务修改的undo page数可能达不到300个,那么必然需要等待128个事务才能进行一次清理。

大事务 如果事务比较大,有许多undo page,那么超过了300*128 那么就会进行清理。

这不是说del flag记录不清理,而是说undo history链表不清理。因此我们经常看到History list length不为0的情况。

九、清理undo history和undo空间

这里简单记录其工作的流程。不做深入函数描述(能力有限)

清理undo history

调入如下:

srv_purge_coordinator_thread

->srv_do_purge

->trx_purge

->trx_purge_truncate

->trx_purge_truncate_history

->trx_purge_truncate_rseg_history

清理的方式如下:

清理的起点:

hdr_addr = trx_purge_get_log_from_hist(

flst_get_last(rseg_hdr + TRX_RSEG_HISTORY, &mtr));

向上扫描:

hdr_addr = prev_hdr_addr;

结束条件:

if (undo_trx_no >= limit->trx_no) { //这里代表结束了

/* limit space_id should match the rollback segment

space id to avoid freeing if the page belongs to a

different rollback segment for the same trx_no. */

if (undo_trx_no == limit->trx_no &&

rseg->space_id == limit->undo_rseg_space) {

trx_undo_truncate_start(rseg, hdr_addr.page, hdr_addr.boffset,

limit->undo_no);

}

rseg->unlatch();

mtr_commit(&mtr);

return;

}

值得注意的是这个清理过程不能大于oldest read view的 trx no,否则清理结束。

truncate undo流程

调入如下:

srv_purge_coordinator_thread

->srv_do_purge

->trx_purge

->trx_purge_truncate

->trx_purge_truncate_history

->trx_purge_truncate_marked_undo

这之前有一个判定是否清理的过程

trx_purge_mark_undo_for_truncate

->Tablespace::needs_truncation

Tablespace::needs_truncation会判断是否进行undo truncate,这里涉及到两个参数

参数innodb_undo_log_truncate的作用

if (!srv_undo_log_truncate || m_rsegs == nullptr || m_rsegs->is_empty() ||

m_rsegs->is_init()) {

m_rsegs->s_unlock();

return (false); //如果没有开启undo truncate则不进行清理

}

参数innodb_max_undo_log_size的作用

page_no_t trunc_size = ut_max(

static_cast(srv_max_undo_tablespace_size / srv_page_size),

static_cast(SRV_UNDO_TABLESPACE_SIZE_IN_PAGES)); //10MB

if (fil_space_get_size(id()) > trunc_size) { //如果undo tablespace大小大于了innodb_max_undo_log_size

return (true); //则进行清理

}

十、总结

到这里开头的问题我们基本就了解了,如下:

del flag在事务提交后,由协调线程判定是否能够进行清理,如果可以清理会分发给工作线程进行清理,这是一个异步的过程,如果修改数据比较多,那么这个过程可能比较慢,并且可以看到purge的相关线程压力较大,但是还算及时。

purge线程总会积压一段时间才会进行History list length的清理,如果是小事务(每次修改的page小于innodb_purge_batch_size的设置),那么需要128个这种小事务才清理一次,如果是大事务那么修改两超过了(innodb_purge_batch_size*innodb_purge_rseg_truncate_frequency)的设置则进行一次清理,但是不管如何这个指标持续不为0是正常。如果较大那么可能意味着要么有大查询,要么purge的各个线程满负荷工作。如下,9281为一个purge的工作线程:

并且purge线程状态处于running状态

purge的协调线程会在每次事务提交的时候醒来,判断是否有需要清理的事务,如果长期没有事务到来那么会第一次等待10ms,超时过后进入长时间的堵塞等待状态。

全文完。

Enjoy MySQL :)

扫码添加作者微信

叶老师的「MySQL核心优化」大课已升级到MySQL 8.0,扫码开启MySQL 8.0修行之旅吧

java purge_浅析InnoDB purge线程相关推荐

  1. purge mysql_MySQL:Innodb purge线程略解

    水平有限,如有错误请谅解.源码版本8.0.21. 在处理一个故障的时候怀疑大量的删除数据导致了查询比较慢,但是自己对purge线程的工作流程一直不太清楚,本文不做深入解析,只做工作流程解析,带着如下问 ...

  2. 2.innodb后台线程

    innodb存储引擎架构 后台线程的作用: 刷新内存中的数据和写入磁盘的数据,并且在数据库异常时,innodb能恢复正常 内存池的作用: 维护内部使用的数据结构,缓存磁盘上的数据,重做日志(redo ...

  3. MySQL purge 线程

    MySQL中purge线程知识: https://dev.mysql.com/doc/refman/5.7/en/innodb-improved-purge-scheduling.html InnoD ...

  4. Java 多线程(七) 线程间的通信

    Java 多线程(七) 线程间的通信--wait及notify方法 线程间的相互作用 线程间的相互作用:线程之间需要一些协调通信,来共同完成一件任务. Object类中相关的方法有两个notify方法 ...

  5. java主线程捕获子线程中的异常

    java主线程捕获子线程中的异常 参考文章: (1)java主线程捕获子线程中的异常 (2)https://www.cnblogs.com/jpfss/p/10272726.html (3)https ...

  6. Java中的进程与线程

    2019独角兽企业重金招聘Python工程师标准>>> Java中的进程与线程 概念 进程与线程,本质意义上说, 是操作系统的调度单位,可以看成是一种操作系统 "资源&qu ...

  7. java多线程总结五:线程池的原理及实现

    1.线程池简介:     多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力.         假设一个服务器完成一项任务所需时间为:T1 ...

  8. JAVA中如何确保N个线程可以访问N个资源,但同时又不导致死锁?

    ● JAVA中如何确保N个线程可以访问N个资源,但同时又不导致死锁? 考察点:死锁 参考回答: 使用多线程的时候,一种非常简单的避免死锁的方式就是:指定获取锁的顺序,并强制线程按照指定的顺序获取锁.因 ...

  9. java主线程控制子线程_CountDownLatch控制主线程等子线程执行完--Java多线程

    1.[代码]CountDownLatch控制主线程等子线程执行完--Java多线程 package com.sihuatech.common; import java.util.concurrent. ...

最新文章

  1. Linux下安装软件的一般步骤
  2. 汇编语言w3c_w3cschoolc语言教程
  3. Linux下安装Python-3.3.2【转】
  4. 再次携号转网_湖北省通信管理局召开视频会议 再次强调携号转网服务要求
  5. C#中获取指定路径下特定开头和后缀的所有文件
  6. IOS启程06—iOS设置圆角图片
  7. 第三章 springboot + jedisCluster(转载)
  8. Android编译默认英语,Android 编译系统 (一)
  9. 如何判断一家公司靠不靠谱?
  10. [置顶] Android之Handler用法总结
  11. Python 大文件处理
  12. Linux下安装Mysql详解
  13. ROS - 科大讯飞语音包使用
  14. java入门到精通第六版_java从入门到精通-第6章.pdf
  15. MySQL优化系列2-索引原理和优化
  16. web前端简易网页制作
  17. 一台计算机有两个用户怎样共享,两台计算机如何共享一台打印机?
  18. 瑞尔IPO:一桩资本逼宫的上市计划
  19. 【UVM基础】虚序列器与虚序列(virtual sequencer与virtual sequence)快速上手指南
  20. python为什么用号做注释符_Python 为什么用 # 号作注释符?

热门文章

  1. 计算机桌面太亮,电脑屏幕太亮太刺眼了怎么调整
  2. 今天谷歌金山词霸2.0正式发布了
  3. genymotion安卓模拟器
  4. 交通标志识别所需图标
  5. SpringCloud商城day07 商品搜索-2021-10-12
  6. Tableau 学习 (3)合适的数据融合方式
  7. docker基本概述+部署(一)
  8. Java开发笔记(五)之集合框架 Map
  9. SQL Server数据库简单查询
  10. Eclipse 调试器:零距离接触实战技巧