
前言
自 2024/10/19 建公众号以来过了 2 个月的时间,公众号的关注来到了 200+,非常感谢大家的关注。在我的公众号的发消息界面点击 “加我滴群” 即可获取我自己建的群的二维码,里面不少大佬,欢迎来提问交流学习。
前不久讲了 Rewriter Query Rewrite Plugin ,之前有小伙伴问 MySQL 能不能限制 SQL 的资源占用,MySQL 确实支持这样一个功能 Resource Group,但是好像不咋用,也没啥名气,我就来讲讲这个怎么用吧。
什么是资源组
我们知道 numactl 可以对进程绑定 CPU 核心数,但是它的作用域是限定进程的,Resource Group 类似于 numactl 管控 CPU 核心数,也是把 CPU 组在一起再管控,只不过它的作用域缩小到了线程。
CPU 时间是一种可管理的资源,由 “virtual CPU” 概念表示,该术语包括 CPU 核心、超线程、硬件线程等。服务器在启动时确定可用的虚拟 CPU 数量,我们将这些 CPU 与资源组关联并将线程分配给资源组。
Resource Group 的配置基于本地操作系统进行,所以分布于不同机器的主从需要单独配置资源组,且资源组语句不会写入 Binlog。
操作系统
设置了 CAP_SYS_NICE
功能的 Linux 操作系统可用。
Windows 可用
在 macOS 平台上,资源组不可用
在 FreeBSD 和 Solaris 平台上,资源组的线程优先级设置会被忽略(实际上,所有线程都以优先级 0
运行。)
Linux 比较常用,我这里只配置 Linux 上的。
配置 CAP_SYS_NICE
因为 Resource Group 依赖 Linux 内核的 CAP_SYS_NICE
功能,所以这里需要配置给 mysqld
开启该功能。
sudo setcap cap_sys_nice+ep application/mysql_8_3306/bin/mysqld
使用该命令查看是否配置完成
[root@localhost ~]# getcap application/mysql_8_3306/bin/mysqld
/application/mysql_8_3306/bin/mysqld = cap_sys_nice+ep
重启启动即可
mysqld_safe --defaults-file=/etc/my.cnf
错误处理
systemd
管理的 mysqld
可能报错找不到 libcrypto.so.1.1
2024-12-27T16:26:12.686054Z mysqld_safe Starting mysqld daemon with databases from /data/mysql_8_3306/application/mysql_8_3306/bin/mysqld: error while loading shared libraries: libcrypto.so.1.1: cannot open shared object file: No such file or directory
需要配置
chown root:mysql application/mysql_8_3306/bin/mysqld
chmod 0750 application/mysql_8_3306/bin/mysqld
Resource Group 属性
资源组具有定义组的属性。所有属性都可以在创建组时设置。有些属性在创建时是固定的;其他属性可以在之后的任何时间进行修改。
每个组都有一个名称。资源组名称是标识符,如表和列名称,组名不区分大小写。
每个组都有一个类型,即
SYSTEM
或USER
(对应于FOREGROUN
和BACKGROUND
)。资源组类型会影响可分配给组的优先级值范围。此属性与允许优先级的差异一起,使系统线程能够被识别,从而保护它们免于与用户线程争用 CPU 资源。
这些属性是在创建资源组时定义的,此后可以随时修改:
CPU 绑定(
CPU Affinity
)是资源组可以使用的virtual CPUs
集。亲和性可以是可用CPU
的任何非空子集。如果一个组没有亲和性,则可以使用所有可用 CPU。线程优先级是分配给资源组的线程的执行优先级。优先级值的范围从
-20
(最高优先级)到19
(最低优先级)。系统组和用户组的默认优先级都是0
。系统组的优先级高于用户组,确保用户线程的优先级永远不会高于系统线程:
对于系统资源组,允许的优先级范围是
-20
到0
。对于用户资源组,允许的优先级范围是
0
到19
。每个组都可以启用或禁用,让管理员可以控制线程分配。线程只能分配给已启用的组。
配置 Resource Group
未配置的情况下,默认有 3 个资源组:
+---------------------+---------------------+------------------------+--------------------+-----------------+
| RESOURCE_GROUP_NAME | RESOURCE_GROUP_TYPE | RESOURCE_GROUP_ENABLED | VCPU_IDS | THREAD_PRIORITY |
+---------------------+---------------------+------------------------+--------------------+-----------------+
| USR_default | USER | 1 | 0x302D31 | 0 |
| SYS_default | SYSTEM | 1 | 0x302D31 | 0 |
| SYS_internal | SYSTEM | 1 | 0x302D31 | 0 |
+---------------------+---------------------+------------------------+--------------------+-----------------+
我的虚拟机分配了 0,1 的 2 个 CPU 核:
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 2
On-line CPU(s) list: 0,1
创建语法:
CREATE RESOURCE GROUP group_name
TYPE = {SYSTEM|USER}
[VCPU [=] vcpu_spec [, vcpu_spec] ...]
[THREAD_PRIORITY [=] N]
[ENABLE|DISABLE]
vcpu_spec: {N | M - N}
TYPE
可选 SYSTEM
或 USER
,代表系统和用户
THREAD_PRIORITY
代表资源组的线程分配优先级,默认值为 0
,SYSTEM
可选 -20 to 0
,USER
可选 0 to 19
ENABLE|DISABLE
默认开启关闭
创建一个资源组 g1
分配一个核:
create resource group g1 type=user vcpu=0 thread_priority=1 enable;
如果没有重启就去创建资源组则无法设置 thread_priority
,为不生效的结果
root@localhost [(none)] 11:50:14 > create resource group g1 type=user vcpu=0 thread_priority=1 enable;
Query OK, 0 rows affected, 1 warning (0.10 sec)
root@localhost [(none)] 11:50:15 > show warnings;
+---------+------+-------------------------------------------------------------+
| Level | Code | Message |
+---------+------+-------------------------------------------------------------+
| Warning | 3659 | Attribute thread_priority is ignored (using default value). |
+---------+------+-------------------------------------------------------------+
1 row in set (0.00 sec)
重启后即可解决此问题
root@localhost [(none)] 11:55:56 > create resource group g1 type=user vcpu=0 thread_priority=1 enable;
Query OK, 0 rows affected (0.01 sec)
创建一个资源组 g2
分配两个核:
create resource group g2 type=user vcpu=0,1 thread_priority=2 enable;
or
create resource group g2 type=user vcpu=0-1 thread_priority=2 enable;
可通过 information_schema.RESOURCE_GROUPS
查看
root@localhost [(none)] 11:59:06 > select * from information_schema.RESOURCE_GROUPS;
+---------------------+---------------------+------------------------+--------------------+-----------------+
| RESOURCE_GROUP_NAME | RESOURCE_GROUP_TYPE | RESOURCE_GROUP_ENABLED | VCPU_IDS | THREAD_PRIORITY |
+---------------------+---------------------+------------------------+--------------------+-----------------+
| USR_default | USER | 1 | 0x302D31 | 0 |
| SYS_default | SYSTEM | 1 | 0x302D31 | 0 |
| SYS_internal | SYSTEM | 1 | 0x302D31 | 0 |
| g1 | USER | 1 | 0x30 | 1 |
| g2 | USER | 1 | 0x302D31 | 2 |
+---------------------+---------------------+------------------------+--------------------+-----------------+
5 rows in set (0.02 sec)
删除资源组
drop resource group g2;
修改资源组(也可以修改部分,不写不需要的部分即可保持原值。)
ALTER RESOURCE GROUP g2 vcpu=0 THREAD_PRIORITY=5 disable;
查看
root@localhost [(none)] 12:02:46 > select * from information_schema.RESOURCE_GROUPS;
+---------------------+---------------------+------------------------+--------------------+-----------------+
| RESOURCE_GROUP_NAME | RESOURCE_GROUP_TYPE | RESOURCE_GROUP_ENABLED | VCPU_IDS | THREAD_PRIORITY |
+---------------------+---------------------+------------------------+--------------------+-----------------+
| USR_default | USER | 1 | 0x302D31 | 0 |
| SYS_default | SYSTEM | 1 | 0x302D31 | 0 |
| SYS_internal | SYSTEM | 1 | 0x302D31 | 0 |
| g1 | USER | 1 | 0x30 | 1 |
| g2 | USER | 0 | 0x30 | 5 |
+---------------------+---------------------+------------------------+--------------------+-----------------+
5 rows in set (0.01 sec)
分配线程到资源组有两种方法:
SET RESOURCE GROUP FOR thread_id配置 hint
,/*+ RESOURCE_GROUP(g2) */
可通过 performance_schema.threads
查看 thread_id
和 RESOURCE_GROUP
root@localhost [(none)] 12:06:44 > select * from performance_schema.threads where PROCESSLIST_ID=9\G
*************************** 1. row ***************************
THREAD_ID: 47
NAME: thread/sql/one_connection
TYPE: FOREGROUND
PROCESSLIST_ID: 9
PROCESSLIST_USER: root
PROCESSLIST_HOST: localhost
PROCESSLIST_DB: NULL
PROCESSLIST_COMMAND: Query
PROCESSLIST_TIME: 0
PROCESSLIST_STATE: executing
PROCESSLIST_INFO: select * from performance_schema.threads where PROCESSLIST_ID=9
PARENT_THREAD_ID: NULL
ROLE: NULL
INSTRUMENTED: YES
HISTORY: YES
CONNECTION_TYPE: Socket
THREAD_OS_ID: 864
RESOURCE_GROUP: USR_default
EXECUTION_ENGINE: PRIMARY
CONTROLLED_MEMORY: 46176
MAX_CONTROLLED_MEMORY: 100032
TOTAL_MEMORY: 296828
MAX_TOTAL_MEMORY: 309164
1 row in set (0.01 sec)
然后使用 SET
分配
set resource group g1 for 47;
再次查看可以看到 RESOURCE_GROUP
变成了 g1
root@localhost [(none)] 12:08:49 > select * from performance_schema.threads where PROCESSLIST_ID=9\G
*************************** 1. row ***************************
THREAD_ID: 47
NAME: thread/sql/one_connection
TYPE: FOREGROUND
PROCESSLIST_ID: 9
PROCESSLIST_USER: root
PROCESSLIST_HOST: localhost
PROCESSLIST_DB: NULL
PROCESSLIST_COMMAND: Query
PROCESSLIST_TIME: 0
PROCESSLIST_STATE: executing
PROCESSLIST_INFO: select * from performance_schema.threads where PROCESSLIST_ID=9
PARENT_THREAD_ID: NULL
ROLE: NULL
INSTRUMENTED: YES
HISTORY: YES
CONNECTION_TYPE: Socket
THREAD_OS_ID: 864
RESOURCE_GROUP: g1
EXECUTION_ENGINE: PRIMARY
CONTROLLED_MEMORY: 39056
MAX_CONTROLLED_MEMORY: 100032
TOTAL_MEMORY: 289796
MAX_TOTAL_MEMORY: 309164
1 row in set (0.00 sec)
再开一个窗口发出
select /*+ RESOURCE_GROUP(g1) */ *,sleep(10) from ddl.ddl;
然后就可以看见新开的连接的 RESOURCE_GROUP
为 g1
root@localhost [(none)] 12:11:25 > select * from performance_schema.threads where PROCESSLIST_ID=10\G
*************************** 1. row ***************************
THREAD_ID: 48
NAME: thread/sql/one_connection
TYPE: FOREGROUND
PROCESSLIST_ID: 10
PROCESSLIST_USER: root
PROCESSLIST_HOST: localhost
PROCESSLIST_DB: NULL
PROCESSLIST_COMMAND: Query
PROCESSLIST_TIME: 3
PROCESSLIST_STATE: User sleep
PROCESSLIST_INFO: select /*+ RESOURCE_GROUP(g1) */ *,sleep(10) from ddl.ddl
PARENT_THREAD_ID: 1
ROLE: NULL
INSTRUMENTED: YES
HISTORY: YES
CONNECTION_TYPE: Socket
THREAD_OS_ID: 26504
RESOURCE_GROUP: g1
EXECUTION_ENGINE: PRIMARY
CONTROLLED_MEMORY: 39296
MAX_CONTROLLED_MEMORY: 46416
TOTAL_MEMORY: 83711
MAX_TOTAL_MEMORY: 90325
1 row in set (0.00 sec)
执行结束变回原组
root@localhost [(none)] 12:13:51 > select * from performance_schema.threads where PROCESSLIST_ID=10\G
*************************** 1. row ***************************
THREAD_ID: 48
NAME: thread/sql/one_connection
TYPE: FOREGROUND
PROCESSLIST_ID: 10
PROCESSLIST_USER: root
PROCESSLIST_HOST: localhost
PROCESSLIST_DB: NULL
PROCESSLIST_COMMAND: Sleep
PROCESSLIST_TIME: 141
PROCESSLIST_STATE: NULL
PROCESSLIST_INFO: NULL
PARENT_THREAD_ID: 1
ROLE: NULL
INSTRUMENTED: YES
HISTORY: YES
CONNECTION_TYPE: Socket
THREAD_OS_ID: 26504
RESOURCE_GROUP: USR_default
EXECUTION_ENGINE: PRIMARY
CONTROLLED_MEMORY: 18720
MAX_CONTROLLED_MEMORY: 46416
TOTAL_MEMORY: 63071
MAX_TOTAL_MEMORY: 90325
1 row in set (0.00 sec)
总结
Resource Group 类似于 numactl 管控 CPU 核心数,也是把 CPU 组在一起再管控,只不过它的作用域缩小到了线程。
可以使用:
会话级别的 set语句级别 hint
配置加入资源组
往期回顾
更改账户身份验证插件注意事项 主从切换后导致 Event 停滞 and 主从故障 - Event 导致主从停滞的案例 INSERT 加什么锁 - 聊聊 INSERT 的加锁情况 MySQL 临时表空间 - 临时表空间(Temporary Tablespaces 及相关的变量 MySQL/PG 数据脱敏 - data_masking 插件和 postgresql_anonymizer 扩展初体验 MySQL/PG 对事务 DDL 的支持程度 - 从隐式提交聊到DDL事务 ONLINE DDL 收尾篇|如何有效实现 ONLINE - 关于 ONLINE DDL 的最佳实践 MySQL/PG 索引对于排序规则的依赖性 - 排序规则与索引的相关性问题,涉及到索引是否会失效 如何正确杀会话, KILL processlist_id/KILL QUERY? - 你知道如何正确杀掉活跃会话吗?
感谢阅读!关注我获取更多精彩内容。




