资源管控是指为不同任务提供可独立使用的计算资源以避免它们互相干扰。当前存在很多资源管控技术,比如硬件虚拟化、虚拟化、Cgroups、Linux Container等。资源管控功能可以为数据库内部重要的任务预留资源,避免出现用户负载高导致数据库自身故障的情况;另一方面如果用户本身就有QoS要求不同的业务同时在一套库中,通过资源管控给不同的业务分配不同的资源,可以保障数据库更稳定地运行。本文整体介绍TiDB新版本中的资源管控(Resource Control)功能。
在介绍TiDB资源管控功能之前,首先想澄清两个概念:
(1) TiDB资源管控功能不等价于多租户功能。
(2) TiDB资源管控定义的资源单元与一般数据库中的Resource Unit不同。
接着,我们开始介绍TiDB的资源管控有哪些能力。
一. TiDB的资源单元(RU)与其它数据库有何不同?
提供多租户或资源隔离功能的数据库一般都有资源单元这个概念,下面介绍几种数据库中的资源单元。
- OceanBase
OceanBase中的资源单元Unit是 CPU、内存、存储空间、IOPS 等物理资源的集合,也是资源调度的基本单位。一个典型的创建OB Unit示例如下,其含义是创建名为 unit1 的 Unit,资源规格为:CPU 为 4 核;内存为 5G;日志盘容量为 10G;IOPS 的上限为 1280,下限为 1024,同时 IOPS 的权重为 1。
CREATE RESOURCE UNIT unit1 MAX_CPU 4, MEMORY_SIZE '5G', MAX_IOPS 1280, LOG_DISK_SIZE '10G', MIN_IOPS=1024, IOPS_WEIGHT=1;
- Greenplum
Greenplum采用资源组(以前版本主要是资源队列)来实现资源管控,资源组使用Linux cgroup 进行资源限制,当用户执行查询时,数据库会根据资源组定义的限制进行评估。资源组中限制的参数包括最大并发事务数、CPU百分比或CPU核数、内存百分比、共享内存百分比等。一个典型的创建Greenplum中的资源组示例如下,它表示此资源组使用cgroup管理内存、允许最大并发事务为10、可用CPU核数为1、可使用总内存的30%。
create resource group rgroup_test with (MEMORY_AUDITOR=cgroup, CONCURRENCY=10, CPUSET='1', MEMORY_LIMIT=30);
- EsgynDB
EsgynDB里面的资源单元Compute Unit指被分配给租户的资源大小 ,默认情况下,1个Compute Unit=4 cores/32 GB。
- TiDB
TiDB中资源管控的基本单位叫Request Unit(RU)而非Resource Unit,RU是系统资源的一个抽象计量单位,表示对数据库的单个请求消耗的资源量。请求消耗的RU数量取决于多种因素,比如操作类型或正在检索或修改的数据量,以下截图来自官方文档。
通过对比上述几种数据库中针对资源单元的定义,可以看出一般数据库定义的资源单元都与物理资源(CPU、内存、磁盘空间)相关,要么是实际的数值(如2个CPU核50G内存),要么是百分比的形式。而TiDB中的资源单元是专门抽象出来的计量单位,这种计量单位的好处是开发人员可以在完全不了解底层硬件资源的情况下能够简单的进行资源管控。当前很多企业中应用开发人员和系统运维人员(如DBA)的分工很明确,经常发现一些开发人员连某套系统底层使用的服务器是物理机还是虚拟机都不清楚,更别提让他评估一套系统需要多少CPU核和多少G内存。TiDB的RU很好的解决了这个问题,只要真实业务基于TiDB运行过一段时间,无须任何DBA的协助,开发人员就可以随时对业务做资源管控的操作。
二. TiDB的资源管控(Resource Control)到底有什么能力?
TiDB资源管控的能力包括:
- 流量控制。前面的RU就是流量的基本单位。给每个资源组配置合适的RU_PER_SEC参数(表示每秒RU数),如果实际使用超过这个数值,将会等待资源。
- 优先级调度。给每个资源组设置一个PRIORITY参数(代表任务优先级),当资源不足时,优先级高的将先进行调度。
- 查询限制。官方称为Runaway Queries,针对资源消耗超预期的语句进行管理限制,比如直接Kill或降低优先级。
三. 资源管控功能应该怎么使用?
在目前较新的TiDB版本中,资源管控功能都是默认打开的,由参数tidb_enable_resource_control及resource-control.enabled控制。TiDB中资源管控的使用基于资源组来实现,限制多大RU_PER_SEC、设置什么样的PRIORITY以及配置什么样的查询限制能力,这些都在定义资源组(resource group)对象的时候指定。
创建资源组的语法可参考官网文档 CREATE RESOURCE GROUP | PingCAP 文档中心 ,重要的几个参数及描述如下:
- RU_PER_SEC。每秒 RU 填充的速度。比如RU_PER_SEC = 500 表示此资源组每秒回填 不得超过500 个 RU。
- PRIORITY。任务在 TiKV 上处理的绝对优先级。PRIORITY = HIGH 表示优先级高,若未指定,则默认为 MEDIUM。
- BURSTABLE。允许对应的资源组超出配额后使用空余的系统资源。此参数一般用于上线开始阶段评估系统需要设置多大的上限。
- QUERY_LIMIT。当查询执行满足该条件时,识别该查询为 Runaway Query 并进行相应的控制。
以下列出几个创建资源组的示例,
/* 创建 rg1 资源组,限额是每秒 500 RU,并且允许这个资源组的应用超额占用资源。*/
CREATE RESOURCE GROUP IF NOT EXISTS rg1 RU_PER_SEC = 500 BURSTABLE;
/* 创建 rg2 资源组,RU 的回填速度是每秒 600 RU。在系统资源充足的时候,不允许这个资源组的应用超额占用资源。*/
CREATE RESOURCE GROUP IF NOT EXISTS rg2 RU_PER_SEC = 600;
/* 创建 rg3 资源组,设置绝对优先级为 HIGH。绝对优先级目前支持 LOW|MEDIUM|HIGH,资源组的默认绝对优先级为 MEDIUM。 */
CREATE RESOURCE GROUP IF NOT EXISTS rg3 RU_PER_SEC = 100 PRIORITY = HIGH;
资源组创建好之后,我们可以使用3种方式来使用资源组:
- 第一种是直接将资源组分配给一个用户,使用ALTER USER … RESOURCE GROUP …分配,也可以在创建用户的时候直接指定一个资源组。
/* 在session中绑定资源组到用户 */
mysql> CREATE RESOURCE GROUP IF NOT EXISTS rg1 RU_PER_SEC = 500 QUERY_LIMIT=(EXEC_ELAPSED='10s', ACTION=KILL);
Query OK, 0 rows affected (1.02 sec)
mysql> ALTER USER root RESOURCE GROUP rg1;
Query OK, 0 rows affected (0.02 sec)
/* 重新连接session执行SQL */
mysql> select count(*) from test.test1 t1, test.test2 t2;
ERROR 8253 (HY000): Query execution was interrupted, identified as runaway query
- 第二种是直接在一个会话里面指定资源组,在session中使用SET RESOURCE GROUP …指定当前会话使用哪个资源组。
/* session用户未绑定资源组,在session中临时设置资源组 */
mysql> set resource group rg1;
Query OK, 0 rows affected (0.00 sec)
mysql> select count(*) from test.test1 t1, test.test2 t2;
ERROR 8253 (HY000): Query execution was interrupted, identified as runaway query
- 第三种是在语句级别使用指定资源组,使用HINT方式指定,如SELECT /*+ RESOURCE_GROUP(rg1) */ FROM…。
/* 直接在sql级别用hint设置resource group */
mysql> select /*+ RESOURCE_GROUP('rg1')*/count(*) from test.test1 t1, test.test2 t2;
ERROR 8253 (HY000): Query execution was interrupted, identified as runaway query
四. 如何使用Runaway Queries限制查询语句?
创建资源组的前三个参数都比较容易理解,最后一个参数QUERY_LIMIT具体应该怎么用需要详细说明一下。QUERY_LIMIT的字面含义就是对QUERY语句进行限制,那么这就涉及到2个问题:
- 什么样的QUERY可以限制?目前TiDB中的QUERY_LIMIT支持根据语句(仅SELECT)的执行时间EXEC_ELAPSED来进行限制,比如对执行时间超过60秒的语句进行限制。
- 怎么限制?换句话说,当语句达到限制条件时数据库采取何种应对措施。目前TiDB支持对满足限制条件的SQL做三种操作:(1)DRYRUN,就是不做任何处理,仅记录识别的语句,这主要用于观测设置条件是否合理;(2)COOLDOWN,将语句的优先级降到最低,查询以低优化级继续执行;(3)KILL,将查询直接终止,查询会直接报错返回Query execution was interrupted, identified as runaway query。
- 并发问题怎么解决?为避免并发语句造成资源问题,查询限制支持Runaway Query监控机制,通过WATCH子句实现。WATCH子句允许对一个DURATION时间内匹配的语句识别为Runaway Query。语句匹配有三种方式:EXACT(完全相同的SQL)、SIMILAR(SQL Digest匹配)、PLAN(Plan Digest匹配)。
以下示例代表创建一个资源组rg1,限额是每秒500RU,并且定义超过 60 秒的为 Runaway Query,并对匹配的语句直接进行KILL操作;同时在接下来的 10 分钟里,把相同模式的查询直接标记为 Runaway Query。
CREATE RESOURCE GROUP IF NOT EXISTS rg1 RU_PER_SEC = 500 QUERY_LIMIT=(EXEC_ELAPSED='60s', ACTION=KILL , WATCH=SIMILAR DURATION='10m');
除了在创建资源组的时候指定QUERY_LIMIT中的WATCH子句,TiDB也支持通过单独的QUERY WATCH语句为资源组添加Runway Queries。QUERY WATCH的语法允许设置资源组名称、ACTION以及SQL Text匹配,详细语法参考 QUERY WATCH | PingCAP 文档中心 。
当语句被添加到QUERY WATCH监控中,可以通过以下命令来查看监控列表,如果想删除某一条监控,使用QUERY WATCH REMOVE <watch-id>。
mysql> SELECT * FROM INFORMATION_SCHEMA.RUNAWAY_WATCHES ORDER BY id;
+----+---------------------+---------------------+-----------+---------+------------------------------------------------------------------+--------+--------+
| ID | RESOURCE_GROUP_NAME | START_TIME | END_TIME | WATCH | WATCH_TEXT | SOURCE | ACTION |
+----+---------------------+---------------------+-----------+---------+------------------------------------------------------------------+--------+--------+
| 3 | rg1 | 2024-02-26 19:15:14 | UNLIMITED | Similar | 3836825468c0e3ab6ae83b8f3f2a9341e9b937565e740059b21ee0c4baab9a39 | manual | Kill |
+----+---------------------+---------------------+-----------+---------+------------------------------------------------------------------+--------+--------+
1 row in set (0.00 sec)
mysql> query watch remove 3;
Query OK, 0 rows affected (0.01 sec)
本文相对完整的介绍了TiDB的资源管控能力,包括流量控制、优先级调度以及查询限制。基于资源管控的功能,可以将多个中小型应用放在一个TiDB集群中,不同的应用设置不能的资源组,可以保证个别应用负载升高也不会影响其他业务的正常运行;当集群遇到突发性能问题,也可以使用资源组来临时限制某批SQL的资源消耗。从TiDB 7.4.0版本开始,资源管控还引入了对后台任务的管理,如备份恢复、DDL、lightning导入等,通过将某些任务设置为后台任务,可以避免此类任务在执行时对其他前台任务造成影响。