[MySQL 5.6] Innodb 新特性之 multi purge thread

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介:
在做5.6.12 vs 5.6.11的性能对比时,大量update产生了很长的purge history list。手贱把innodb_fast_shutdowns设置为0了,结果Purge线程一直干活了,差不多两个小时才结束….

我们知道,在MySQL5.5版本中,就已经开始将purge 任务从master线程中独立出来,而到了5.6,已经支持多个purge线程同时进行,简单的理了下代码逻辑.

////////////////////////////////////////////////////////


在5.6中,提供了参数Innodb_purge_threads来控制做purge操作的后台线程数,最大允许设置为32.

purge线程被分为两类,一类是coordinator thread,只有一个这样的线程,另外的Innodb_purge_threads-1个是worker线程.

线程在Innodb启动时创建
quoted code in innobase_start_or_create_for_mysql:
2584         if (!srv_read_only_mode
2585             && srv_force_recovery < SRV_FORCE_NO_BACKGROUND) {
2586
2587                 os_thread_create(
2588                         srv_purge_coordinator_thread,
2589                         NULL, thread_ids + 5 + SRV_MAX_N_IO_THREADS);
2590
2591                 ut_a(UT_ARR_SIZE(thread_ids)
2592                      > 5 + srv_n_purge_threads + SRV_MAX_N_IO_THREADS);
2593
2594                 /* We've already created the purge coordinator thread above. */
2595                 for (i = 1; i < srv_n_purge_threads; ++i) {
2596                         os_thread_create(
2597                                 srv_worker_thread, NULL,
2598                                 thread_ids + 5 + i + SRV_MAX_N_IO_THREADS);
2599                 }
2600
2601                 srv_start_wait_for_purge_to_start();
2602
2603         } else {
2604                 purge_sys->state = PURGE_STATE_DISABLED;
2605         }
a. coordinator thread
协调线程的入口函数是srv_purge_coordinator_thread

分为三个阶段:
正常工作阶段,在一个while循环中调用:
2754                 rseg_history_len = srv_do_purge(
2755                         srv_n_purge_threads, &n_total_purged);

 

从while break的条件由函数srv_purge_should_exit确定,这里有一个特殊情况,当innodb_fast_shutdown设置为0时,如果上一次purge的Page数不为0,则返回false,表示不退出循环,这是因为当fast shutdown为0时,需要做完所有的purge操作才会结束线程任务


第二个阶段是确认innodb_fast_shutdown被设置为0时,所有的记录都被purge掉了;这可以避免在退出上述循环后,有新的记录加入。这里已经不再使用worker线程了(trx_purge的第一个参数为1)

2769         while (srv_fast_shutdown == 0 && n_pages_purged > 0) {
2770                 n_pages_purged = trx_purge(1, srv_purge_batch_size, false);
2771         }
最后对history list做一次truncate,并确保所有worker线程退出

2773         /* Force a truncate of the history list. */
2774         n_pages_purged = trx_purge(1, srv_purge_batch_size, true);
这里有两个需要关注的函数:

a1)srv_do_purge:

在协调线程的主要工作都是在这个函数中,注意,不是有多少工作线程,就会用多少的,这里 实际上是把多余的线程当做一个池子,只有purge跟不上更新的时候,才会去调度这些线程:

调整n_use_threads
>>当trx_sys->rseg_history_len相比上次purge有增长时,或者超过了innodb_max_purge_lag_delay(不为0),++n_use_threads
>>否则,如果存在activity(srv_check_activity),–n_use_threads

执行purge调度
2577                 n_pages_purged = trx_purge(
2578                         n_use_threads, srv_purge_batch_size, false);

每128次purge, truncate一次history list,这也是为什么我们每隔一会,才看到history list长度变小的原因

2580                 if (!(count++ % TRX_SYS_N_RSEGS)) {
2581                         /* Force a truncate of the history list. */
2582                         n_pages_purged += trx_purge(
2583                                 1, srv_purge_batch_size, true);
2584                 }
a2)  trx_purge
trx_purge是purge任务调度的核心函数,包含三个参数:
* n_purge_threads —>使用到的worker线程数
* batch_size  —-> 由innodb_purge_batch_size控制,表示一次Purge的记录数
* truncate —>是否truncate history list 

持有purge_sys->latch的x锁,并建立read view,然后释放锁
purge_sys->view = read_view_purge_open(purge_sys->heap);

读取需要purge的undo记录,记录数不超过batch size,这些purge任务被指派给n_purge_threads个thr
1213         /* Fetch the UNDO recs that need to be purged. */
1214         n_pages_handled = trx_purge_attach_undo_recs(
1215                 n_purge_threads, purge_sys, &purge_sys->limit, batch_size);
当n_purge_threads>1时,会将n_purge_threads-1个thr加入到工作队列,由worker线程来消费

1221                 /* Submit the tasks to the work queue. */
1222                 for (i = 0; i < n_purge_threads - 1; ++i) {
1223                         thr = que_fork_scheduler_round_robin(
1224                                 purge_sys->query, thr);
1225
1226                         ut_a(thr != NULL);
1227
1228                         srv_que_task_enqueue_low(thr);
1229                 }

 

调度线程同样也需要运行一个thr任务,而不是仅仅只做分配;完成后,还需要等待其他worker线程完成任务
1243 run_synchronously:
1244                 ++purge_sys->n_submitted;
1245
1246                 que_run_threads(thr);
1247
1248                 os_atomic_inc_ulint(
1249                         &purge_sys->bh_mutex, &purge_sys->n_completed, 1);
1250
1251                 if (n_purge_threads > 1) {
1252                         trx_purge_wait_for_workers_to_complete(purge_sys);
1253                 }

当truncate为true时,还需要调用trx_purge_truncate–>trx_purge_truncate_history从回滚段中移除历史记录。


b.worker thread


入口函数是srv_worker_thread
2473         do {
2474                 srv_suspend_thread(slot);
2475
2476                 os_event_wait(slot->event);
2477
2478                 if (srv_task_execute()) {
2479
2480                         /* If there are tasks in the queue, wakeup
2481                         the purge coordinator thread. */
2482
2483                         srv_wake_purge_thread_if_not_active();
2484                 }
2485
2486                 /* Note: we are checking the state without holding the
2487                 purge_sys->latch here. */
2488         } while (purge_sys->state != PURGE_STATE_EXIT);
purge_sys->state是协调线程在将要退出前设置成PURGE_STATE_EXIT。因此worker线程总是在coordinator线程退出之后再退出


srv_task_execute()的工作流程也很简单: 持有srv_sys->tasks_mutex锁,从srv_sys->tasks中取出一个thr,然后释放tasks_mutex。再调用que_run_threads(thr)完成purge

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
20天前
|
JSON 关系型数据库 MySQL
MySQL 8.0 新特性
MySQL 8.0 新特性
95 10
MySQL 8.0 新特性
|
1月前
|
存储 Oracle 关系型数据库
Oracle和MySQL有哪些区别?从基本特性、技术选型、字段类型、事务、语句等角度详细对比Oracle和MySQL
从基本特性、技术选型、字段类型、事务提交方式、SQL语句、分页方法等方面对比Oracle和MySQL的区别。
Oracle和MySQL有哪些区别?从基本特性、技术选型、字段类型、事务、语句等角度详细对比Oracle和MySQL
|
2天前
|
SQL 安全 关系型数据库
MySQL8.2有哪些新特性?
【10月更文挑战第3天】MySQL8.2有哪些新特性?
9 2
|
5天前
|
存储 缓存 关系型数据库
详细解析MySQL中的innodb和myisam
总之,InnoDB和MyISAM各有千秋,选择合适的存储引擎应基于对应用程序特性的深入理解,以及对性能、数据完整性和可扩展性的综合考量。随着技术发展,InnoDB因其全面的功能和日益优化的性能,逐渐成为更广泛场景下的首选。然而,在特定条件下,MyISAM依然保留其独特的价值。
13 0
|
2月前
|
监控 关系型数据库 MySQL
在Linux中,mysql的innodb如何定位锁问题?
在Linux中,mysql的innodb如何定位锁问题?
|
2月前
|
SQL 存储 关系型数据库
"MySQL增列必锁表?揭秘InnoDB在线DDL,让你的数据库操作飞一般,性能无忧!"
【8月更文挑战第11天】在数据库领域,MySQL凭借其稳定高效的表现深受开发者喜爱。对于是否会在给数据表添加列时锁表的问题,MySQL的行为受版本、存储引擎等因素影响。从5.6版起,InnoDB支持在线DDL,可在改动表结构时保持表的可访问性,避免长时间锁表。而MyISAM等则需锁表完成操作。例如,在使用InnoDB的表上运行`ALTER TABLE users ADD COLUMN email VARCHAR(255);`时,通常不会完全锁表。虽然在线DDL提高了灵活性,但复杂操作或大表变更仍可能暂时影响性能。因此,进行结构变更前应评估其影响并择机执行。
59 6
|
2月前
|
算法 关系型数据库 MySQL
一天五道Java面试题----第七天(mysql索引结构,各自的优劣--------->事务的基本特性和隔离级别)
这篇文章是关于MySQL的面试题总结,包括索引结构的优劣、索引设计原则、MySQL锁的类型、执行计划的解读以及事务的基本特性和隔离级别。
|
2月前
|
SQL 算法 关系型数据库
(二十)MySQL特性篇:2022年的我们,必须要懂的那些数据库新技术!
 MySQL数据库从1995年诞生至今,已经过去了二十多个年头了,到2022.04.26日为止,MySQL8.0.29正式发行了GA版本,在此之前版本也发生了多次迭代,发行了大大小小N多个版本,其中每个版本中都有各自的新特性,所有版本的特性加起来,用一本书的篇幅也无法完全阐述清楚,因此本章主要会挑重点特性来讲,具体各版本的特性可参考MySQL官网的开发手册。
|
3月前
|
存储 SQL 关系型数据库
(十三)MySQL引擎篇:半道出家的InnoDB为何能替换官方的MyISAM?
MySQL是一款支持拔插式引擎的数据库,在开发过程中你可以根据业务特性,从支持的诸多引擎中选择一款适合的,例如MyISAM、InnoDB、Merge、Memory(HEAP)、BDB(BerkeleyDB)、Example、Federated、Archive、CSV、Blackhole.....
|
4月前
|
关系型数据库 MySQL 测试技术
深入探索MySQL 8:隐藏索引与降序索引的新特性
深入探索MySQL 8:隐藏索引与降序索引的新特性