1 概述
1.1 背景介绍
当OLTP负载中存在较多写类型操作时(update/insert/delete),会产生较多redo log写请求,MySQL后台的写log线程可能压力过大,影响系统吞吐量。可通过本特性提高log线程效率,避免其成为系统瓶颈。此外,如果分析得出业务本身适合NUMA亲和,在NUMA架构的多路服务器上,可通过本特性提高用户线程的访存效率。
在NUMA架构下,整个内存空间在物理上是分布式的,所有这些内存的集合就是整个系统的全局内存。每个核访问内存的时间取决于内存相对于处理器的位置,访问本地内存(本节点内)会更快一些。Linux内核从2.5版本开始支持NUMA架构,现在的操作系统也提供了丰富的工具和接口,帮助我们完成就近访问内存的优化和配置。所以,使用NUMA处理器所实现的计算机系统,通过适当的性能调优,既能够达成很好的性能,又能够解决SMP架构下的总线瓶颈问题,提供更强的多核扩展能力,以及更好更灵活的计算能力。
在MySQL OLTP场景下,高并发情况下系统会默认将MySQL的线程在OS的CPU上进行调度,如图左侧区域所示,这会导致线程频繁跨越NUMA节点进行访问,这种情况会增加CPU的开销,从而限制性能的提升。因此,需要对线程调度进行优化,以减少NUMA节点之间访问的开销,从而提高系统的性能。
采用MySQL NUMA调度优化特性后可以对MySQL前台线程和后台线程进行精细化调度,从而提高关键线程的处理效率,减少远程访问存储的次数,从而提升整个系统的性能。
-
后台线程:本特性涉及6类影响系统性能的关键后台线程,主要与redo log和purge逻辑相关。这类线程数量固定,每一类线程有且只有一个线程实例,在MySQL实例启动时启动。本特性允许用户指定某类线程仅运行在指定的CPU core(s)。因此,通过合理参数设置,可以使各类线程的CPU core(s)相互隔离,使其均能得到充分调度,避免成为系统瓶颈。
-
前台线程:MySQL是thread per connection,故前台线程数量随会话数量变化而变化。类似后台线程调度,本特性同样允许控制前台线程运行在指定的CPU core(s)。此外,本特性支持根据 NUMA 信息将CPU core(s)分组。前台线程在当前会话的生命周期内,只会在同一个组内的CPU core(s)上迁移,而不会跨NUMA迁移,因此从data locality获益。为了实现组间的负载均衡,新增的前台线程将被调度到负载较小的组上。负载程度通过本组内的会话数反应。
2 概要
2.1 配置调度策略
可以通过系统变量控制线程调度策略,注意,这里的线程调度是指控制某个线程在哪个cpu、节点/组内运行,而无关操作系统的线程的排队时调度、状态切换等。
参数 | 配置建议 |
---|---|
sched_affinity_numa_aware | 设置是否开启前台进程绑核。sched_affinity_foreground_thread不为空值时,则sched_affinity_foreground_thread设置的CPU core(s)会根据numa node进行分组,而一个会话所在的线程只会在其中特定组中的core(s)间迁移。建议配置为ON。 |
sched_affinity_foreground_thread(1) | 指定MySQL前台线程(用户线程)运行的CPU core。建议前台线程和后台线程绑在不同的core上。 |
sched_affinity_log_writer(2) | 设置MySQL log_writer线程允许运行的CPU core(s)。建议后台线程绑在同一个numa node的core上。 |
sched_affinity_log_flusher(3) | 设置MySQL log_flusher线程允许运行的CPU core(s) 。建议后台线程绑在同一个numa node的core上。 |
sched_affinity_log_write_notifier(4) | 设置MySQL log_write_notifier线程允许运行的CPU core(s) 。建议后台线程绑在同一个numa node的core上。 |
sched_affinity_log_flush_notifier(5) | 设置MySQL log_flush_notifier线程允许运行的CPU core(s) 。建议后台线程绑在同一个numa node的core上。 |
sched_affinity_log_checkpointer(6) | 设置MySQL log_checkpointer线程允许运行的CPU core(s) 。建议后台线程绑在同一个numa node的core上。 |
sched_affinity_purge_coordinator(7) | 设置MySQL purge_coordinator线程允许运行的CPU core(s) 。建议后台线程绑在同一个numa node的core上。 |
特性新增参数说明:
本特性增加如表所示MySQL系统变量,可在配置文件、启动参数或运行时设置。
表 MySQL系统变量名称和说明
系统变量名称 | 说明 |
---|---|
sched_affinity_foreground_thread | global级别参数,字符串类型,用于设置MySQL前台线程允许运行的CPU core(s) 。有效取值由代表core编号的数字组合成的字符串。core编号可通过逗号(,)分隔,可通过减号(-)表示范围。以下均为合法的CPU core(s)取值:空值50,5,70,2-5,7本参数在数据库运行时可更改,默认值是空值。当取值为空值,表示本类线程由操作系统调度,相当于未启用本参数。 |
sched_affinity_numa_aware | global级别参数,布尔类型,如果设置为ON且sched_affinity_foreground_thread不为空值,则sched_affinity_foreground_thread设置的CPU core(s)会根据numa node进行分组,而一个会话所在的线程只会在其中特定组中的core(s)间迁移。本参数运行时可更改,默认值是OFF。 |
sched_affinity_log_writer | global级别参数,字符串类型,用于设置MySQL log_writer线程允许运行的CPU core(s)。有效取值由代表core编号的数字组合成的字符串。core编号可通过逗号(,)分隔,可通过减号(-)表示范围。以下均为合法的CPU core(s)取值:空值50,5,70,2-5,7本参数在数据库运行时可更改,默认值是空值。当取值为空值,log_writer线程将恢复为由操作系统调度。 |
sched_affinity_log_flusher | global级别参数,字符串类型,用于设置MySQL log_flusher线程允许运行的CPU core(s)。有效取值由代表core编号的数字组合成的字符串。core编号可通过逗号(,)分隔,可通过减号(-)表示范围。以下均为合法的CPU core(s)取值:空值50,5,70,2-5,7本参数在数据库运行时可更改,默认值是空值。当取值为空值,log_flusher线程将恢复为由操作系统调度。 |
sched_affinity_log_write_notifier | global级别参数,字符串类型,用于设置MySQL log_write_notifier线程允许运行的CPU core(s) 。有效取值由代表core编号的数字组合成的字符串。core编号可通过逗号(,)分隔,可通过减号(-)表示范围。以下均为合法的CPU core(s)取值:空值50,5,70,2-5,7本参数运行时可更改,默认值是空值。当取值为空值,log_write_notifier线程将恢复为由操作系统调度。 |
sched_affinity_log_flush_notifier | global级别参数,字符串类型,设置MySQL log_flush_notifier线程允许运行的CPU core(s)。有效取值由代表core编号的数字组合成的字符串。core编号可通过逗号(,)分隔,可通过减号(-)表示范围。以下均为合法的CPU core(s)取值:空值50,5,70,2-5,7本参数运行时可更改,默认值是空值。当取值为空值,log_flush_notifier线程将恢复为由操作系统调度。 |
sched_affinity_log_checkpointer | global级别参数,字符串类型,设置MySQL log_checkpointer线程允许运行的CPU core(s) 。有效取值由代表core编号的数字组合成的字符串。core编号可通过逗号(,)分隔,可通过减号(-)表示范围。以下均为合法的CPU core(s)取值:空值50,5,70,2-5,7本参数运行时可更改,默认值是空值。当取值为空值,log_checkpointer线程将恢复为由操作系统调度。 |
sched_affinity_purge_coordinator | global级别参数,字符串类型,设置MySQL purge_coordinator线程允许运行的CPU core(s) 。有效取值由代表core编号的数字组合成的字符串。core编号可通过逗号(,)分隔,可通过减号(-)表示范围。以下均为合法的CPU core(s)取值:空值50,5,70,2-5,7本参数运行时可更改,默认值是空值。当取值为空值,purge_coordinator线程将恢复为由操作系统调度。 |
sched_affinity_log_closer | global级别参数,字符串类型,设置MySQL log_closer线程允许运行的CPU core(s) 。有效取值由代表core编号的数字组合成的字符串。core编号可通过逗号(,)分隔,可通过减号(-)表示范围。以下均为合法的CPU core(s)取值:空值50,5,70,2-5,7本参数运行时可更改,默认值是空值。当取值为空值,log_closer线程将恢复为由操作系统调度。须知MySQL 8.0.25中删除了log_closer线程,因此相应版本补丁中不提供该参数。 |
为支持查询调度管理器内部状态,新增如表所示MySQL状态变量:
表 MySQL状态变量名称和说明
状态变量名称 | 说明 |
---|---|
Sched_affinity_status | 返回调度管理器中各个分组的负载情况。 |
Sched_affinity_group_number | 返回系统NUMA node总数。 |
Sched_affinity_group_capacity | 返回系统每个NUMA node中的Core数量。 |
2.2 绑核
绑核就是把6大类线程,绑定到固定的组内的CPU上,而不会跨NUMA迁移,因此从data locality获益。对于每一个线程的运行来说,总是在其运行之前,先将其注册到调度管理器(将其绑定到一个最优的核上),具体绑定到那个核,绑核逻辑/算法如下:
1.获取线程类型:
获取给定进程ID(pid)对应的线程类型。 检查线程类型是否未定义:
1.1如果线程类型未定义(Thread_type::UNDEFINED),返回失败(false)。 检查线程调度是否未启用:
1.2如果线程调度未启用,表示无需进行调度亲和力绑定,返回成功(true)。 获取线程类型对应的调度亲和力组:
2.获取线程类型对应的调度亲和力组引用。 初始化变量:
定义一个常量 INVALID_INDEX 代表无效的索引。 初始化一个变量 best_index 为 INVALID_INDEX,用于记录选择的最佳调度亲和力组的索引。 选择最适合的调度亲和力组:
3.遍历线程类型对应的调度亲和力组。 3.1跳过可用 CPU 数为零的组。 3.2比较组的适合度,选择具有更多可用 CPU 或已分配线程数较少的组为最佳组。 检查是否找到有效的调度亲和力组:
如果没有找到有效的调度亲和力组,返回失败(false)。 绑定线程到选择的调度亲和力组:
4.使用 numa_sched_setaffinity 函数将线程绑定到选择的调度亲和力组的可用 CPU。 如果绑定成功,更新选择的调度亲和力组的已分配线程数,记录线程ID到组ID的映射,返回成功(true)。 如果绑定失败,返回失败(false)。 整个算法的目标是在启用线程调度的情况下,选择并绑定线程到最适合的调度亲和力组,以优化线程的调度和性能。