【华为云MySQL技术专栏】TaurusDB多租户管理与资源隔离特性解读
1. 技术背景
SaaS(Software as a Service,软件即服务)在云上部署,可以将分散的计算存储资源集中利用,并让原来的用户都到云上来共享这些资源。云服务厂商为此提供了一种解决方案,一个实例可支持多个租户的数据存储与访问,降低了SaaS和租户的使用成本。基于此,云厂商还需要提供资源隔离的能力,以确保SaaS租户的数据安全。
为了满足这个需求,多租户技术应运而生。该技术旨在解决如何在同一系统或组件下,让多个租户共享计算和存储资源的同时,确保各租户的数据隔离和资源隔离。
租户是一个逻辑概念,当一个企业或者组织在云平台注册账户后,平台就认为该账户是一个租户,云服务厂商将以租户为基本单位来分配资源,并且确保这些资源相互独立。用户则是这些资源的使用者,一个租户可以有多个用户,每个用户都可以访问到该租户所分配到的资源。
以SaaS为例,通常SaaS服务提供商被视为一个租户。然而,当SaaS服务提供商面向多个不同客户时,这些客户同样应当被视作不同的租户。因此,为了有效管理和支持这些多租户环境,云计算供应商需要在其现有单租户实例配置的基础上,引入多租户的概念。
从广义上来看,多租户机制扩展了数据库实例能够服务的目标群体,使得单一实例能够同时服务于多个独立的客户或租户。而从狭义上来看,对于SaaS服务中的每一个租户而言,多租户架构不仅提高了资源利用率,还确保了各租户之间的良好隔离,即每个租户的数据和操作不会相互干扰,从而保障了服务的安全性和稳定性。
2. 概念简介
当前云上提供给租户管理的最小单元是数据库实例,即一个数据库实例服务于一个租户。然而,SaaS服务需要更灵活的租户管理方式,因此,TaurusDB推出了多租户管理及资源隔离功能,包括:
1)支持租户级数据隔离,确保不同租户只能访问自己的数据;
2)支持租户级资源隔离,通过以租户为单位的实例资源划分,允许各租户的资源配比可动态调整,既提高了系统的资源利用率,又能及时应对不同租户的业务高峰和波谷;
3)支持用户级资源隔离,通过以用户为单位的租户资源划分,实现了更细粒度的资源管理方案。
在多租环境下,我们是以“数据库”为粒度,来区分不同租户的数据。如图1所示,租户分为系统租户和普通租户。
图1 多租户管理概念图
每个实例在创建时都会默认拥有系统租户,该租户下的用户可以访问实例上的所有数据库,主要用于系统的管理和运维,这些用户也被称为系统用户。普通租户需要在系统租户下创建,其下的用户只能授权访问由该租户创建的数据库,无法访问其他租户的数据库,这类用户被称为普通用户。
此外,每个普通租户可以创建和管理多个用户数据库,但每个用户数据库只能属于一个普通租户。相比之下,系统租户可以管理所有数据库,包含普通租户创建的数据库,以及系统默认数据库,如mysql,sys,performance_schema,information_schema等,这些数据库均属于系统租户的管理范畴。
3. 实现原理
3.1 数据隔离
在当前模式下,有多种方案可以实现数据隔离。
一是TaurusDB支持通过ACL语句来设置不同租户对数据库和表的访问权限,从而实现不同租户身份的数据隔离。此方案的优势在于不需要对内核进行修改,但限制在于不同租户无法拥有名称相同的数据库。
二是通过修改mysql.db和mysql.user系统表的结构,来记录数据库和用户所属的租户信息,从而保证不同租户之间能够创建相同的数据库名称,并且实现数据隔离。但是,此方案涉及到系统表的修改,对实例升级、其他依赖于系统表的功能兼容性不友好。
基于此,为实现各普通用户仅能访问到所属租户的数据库, TaurusDB提出了一个更为优化的解决方案。该方案新增多租系统表,用来记录当前实例的租户信息。在连接TaurusDB时,普通用户的登录名由“用户名@租户名”两部分构成。当前会话将保存对应的租户名,在创建数据库、用户或表空间时,将对各自的名称拼接租户名后缀后完成创建。当用户下一次登录时,TaurusDB会对所要访问的数据库、表空间等名称进行解析,得到所属的租户信息,并和当前会话的租户名称进行比较。
不同租户下数据库和用户的命名,如图2所示。
图2 不同租户下数据库和用户的命名
最终,根据普通用户只能访问所属租户数据的约束逻辑,实现租户间的数据隔离。此方案不仅修改量小,并且在兼容性方面表现更加友好。
系统用户可以访问当前实例的所有数据库,普通用户只能访问所属租户下的数据库。
以图3为例,以系统租户的身份,创建数据库db1和db3,db1和db3只有系统租户才能访问。以普通租户A下的用户,创建数据库db1,db1存储的名字会自动调整为db1@tenantA;以普通租户B下的用户,创建数据库db2,db2的名称将自动调整为db2@tenantB。无论是系统用户或者普通用户,在查找数据库时,系统都会在数据库名(db)后面加上“@租户名”来寻找对应的数据库,从而确保不同租户只能访问自己租户名管理下的数据库数据。
图3 数据隔离原理图
此外,在启用多租特性前,实例如果已经有部分存量的数据库(图3中的db2和db4),这些老版本的数据库db2和db4将默认属于系统租户。不过, TaurusDB支持将已存在的数据库所属租户修改为其他普通租户。如图2 中db2的所属租户可以从系统租户切换为租户A,db4的所属租户可以从系统租户切换为租户B,数据库与租户的映射关系将记录在系统表tenant_db中。
3.2 资源隔离
CPU的资源隔离是基于CGroup(Control Groups)[1,2]和Thread Pool技术实现。
CGroup作为一种资源隔离的机制,它允许用户根据需求,将一系列系统任务及其子任务整合到按资源划分等级的不同组内,为系统资源管理提供一个统一的框架。通过CGroup,用户可以限制、记录和优先分配任务组所使用的资源,从而有效地管理系统的性能和资源分配。
CGroup本质就是一个目录树,其中包含若干配置文件,用于配置当前层级的资源限额,并可以通过创建子目录的方式,把资源继续划分给到下一层级。值得注意的是,子目录分配的资源之和,不能超过父目录的资源配额。进程或者线程可以绑定到CGroup目录树的某一层级,从而让进程或线程受到绑定目录中的资源限制。
具体到本特性,核心的设计包含两个方面:
1)划分CGroup目录树
按照租户、用户、后台线程等不同类别的资源隔离需求设置目录树,每层目录下包含CPU.Shares,CPU.Cfs_quota_us,Tasks等配置项,分别用于配置本层级可使用的最大CPU资源、最小CPU资源以及与本层级目录绑定的线程ID。
2)线程绑定到CGroup目录树节点
分为后台线程绑定和业务线程绑定,通过Thread Pool线程池的逻辑,将各个层级的CGroup目录和Thread Pool中的Worker Threads关联,这样,系统可以识别当前连接所属的租户/用户,并将其分配给已经绑定到对应CGroup目录的Worker Thread进行处理。
TaurusDB内部有一个专用的线程Main Listener,用来监听数据库的连接请求。当一旦收到一个新的请求时,该线程会将请求转发给默认的ThreadGroup,此ThreadGroup会解析并记录连接身份信息,并将此请求均匀地分配给其他的Listener线程。Listener线程负责监听连接请求,并根据连接身份将请求转发给对应ThreadGroup。
在TaurusDB中,每个用户都对应线程池中的一组ThreadGroup。当用户连接执行SQL时,由对应的ThreadGroup中的Worker Thead来执行,同时,这些ThreadGroup会与CGroup目录树中的特定目录进行绑定。
由于租户和用户是一对多的关系,一个用户只能属于一个租户,所以每个租户包含了多个ThreadGroup。TaurusDB支持租户级和用户级的CPU资源隔离,如果设置了用户级的资源隔离,则会在用户所属租户的CGroup目录下,创建此用户的CGroup子目录。
ThreadGroup具体绑定在CGroup目录树上的哪个目录,需要根据“租户名、用户名以及是否设置了此用户的资源隔离”而定。如果设置了用户级的资源隔离,该用户对应的ThreadGroup会被绑定到此用户对应的CGroup目录上,否则该用户对应的ThreadGroup会被绑定到所属租户对应的CGroup目录上,并由租户的CGroup配置限制其资源使用。
CGroup目录分为三层,分别是进程层(process level)、租户层(tenant level)和用户层(user level),如图4所示。
图4 CGroup目录树结构
进程层用于区分后台线程和用户线程等,包括default、admin、others三个目录。
default目录用于绑定系统关键线程,此类线程(如主线程、连接监听线程、定时任务线程等)对时延敏感,必须及时获取到所需的CPU资。因此,将其放在根目录下,避免其他线程的“扰邻”影响。
admin目录用于绑定系统超级管理员的线程,即超级管理员访问TaurusDB时产生的前台线程将绑定到admin目录下,同样放在根目录下,确保不受其他线程的“扰邻”影响。
others目录用于绑定后台线程和租户线程,其中bg目录用于绑定后台线程,tenants目录用于绑定各租户线程,即系统租户或者普通用户访问TaurusDB时,产生的前台线程将绑定在tenants目录下。若需实现用户级资源隔离,还会进一步在tenants目录下创建consumer group子目录,以实现租户内部各用户间的资源划分。
3.3元数据管理
元数据管理将提供多个多租系统表,用于数据隔离和资源隔离;资源隔离所涉及到的元数据,包括租户信息、租户及其下用户的资源配置等,都将被保存在多租系统表中。TaurusDB内部的后台线程将定时把这些数据固化为CGroup目录树下的CPU.Shares,CPU.Cfs_quota_us配置项。
由于tenants目录下的不同子目录对应不同租户和租户所属的用户,当Thread Pool创建Worker线程时,会将线程ID、租户、用户等信息传递给后台线程,由具有root权限的后台线程ID写入对应CGroup目录的Task文件中,与Task文件同一目录的CPU.Shares,CPU.Cfs_quota_us配置项将对Task文件中保存的线程发挥作用,进而实现基于CGroup的资源隔离。
4. 业务场景/流程
4.1 数据隔离
使用系统租户下的管理员账号连接数据库,并创建多种资源配置信息,用于对租户资源进行限制。
CREATE resource_config cfg_1 MAX_CPU {max_cpu_val} MIN_CPU {min_cpu_val};
CREATE resource_config cfg_2 MAX_CPU {max_cpu_val} MIN_CPU {min_cpu_val};
使用系统租户下的管理员账号连接数据库,分别创建tenant_1和tenant_2两个租户,且在创建租户时,与指定的资源配置信息绑定。
CREATE tenant tenant_1 RESOURCE_CONFIG cfg_1;
CREATE tenant tenant_2 RESOURCE_CONFIG cfg_2;
创建租户tenant_1下的用户user1,tenant_2下的用户user2。
CREATE USER 'user1@tenant_1'@'%' IDENTIFIED
WITH mysql_native_password BY {pwssword};
GRANT priv ON *.* TO 'user1@tenant_1'@'%' WITH GRANT OPTION;
CREATE USER 'user2@tenant_2'@'%' IDENTIFIED
WITH mysql_native_password BY {pwssword};
GRANT priv ON *.* TO 'user2@tenant_2'@'%' WITH GRANT OPTION;
创建成功后,通过“user1@tenant_1”账户连接数据库,并创建数据库test1。
再以相同的方式通过“user2@tenant_2”账户连接数据库,创建数据库test2。
对于系统用户root,能看到租户tenant_1和tenant_2下的数据库;对于用户user1,只能看到数据库test1;对于用户user2,只能看到数据库test2,具体效果如上图2所示。
4.2 租户级资源隔离
对tenant_1和tenant_2进行sysbench压测,对比不同租户下的CPU使用率。
sysbench
--db-driver=mysql
--mysql-host=<host> --mysql-port=<port>
--mysql-user=user1@tenant_1
--mysql-password=<password>
--mysql-db=sbtest --table_size=25000 --tables=250
--time=600 --range_selects=0 --skip-trx=1 --threads=128
--percentile=95 --report-interval=1 oltp_read_only run
sysbench
--db-driver=mysql
--mysql-host=<host> --mysql-port=<port>
--mysql-user=user1@tenant_2
--mysql-password=<password>
--mysql-db=sbtest --table_size=25000 --tables=250
--time=600 --range_selects=0 --skip-trx=1 --threads=128
--percentile=95 --report-interval=1 oltp_read_only run
结果图5所示,10:58-11:00对应租户tenant_1的结果,11:01-11:03对应租户tenant_2测试结果。
图5 租户级隔离指标对比图
结果显示,tenant_1相比tenant_2的CPU使用率,比例接近2:1;tenant_1相比tenant_2的QPS使用率,比例接近2:1。
4.3 用户级资源隔离
在此场景中,继续沿用创建的租户tenant_1,以及该租户下的用户user1和user2。接下来,通过租户tenant_1连接数据库,并创建两个资源消费组group1和group2。然后,将消费组group1绑定到用户user1,将消费组group2绑定到用户user2。
CALL dbms_resource_manager.create_consumer_group
('group1', 'comment');
CALL dbms_resource_manager.create_consumer_group
('group2', 'comment');
CALL dbms_resource_manager.set_consumer_group_mapping
('USER', 'user1', 'group1');
CALL dbms_resource_manager.set_consumer_group_mapping
('USER', 'user2', 'group2');
首先,创建一个资源计划plan1,并为其配置资源计划指令plan_directive_for_user1,该指令将plan1与group1进行关联。接着,再创建资源计划plan2,并为其配置资源计划指令plan_directive_for_user2,plan_directive_for_user2将plan2与group2关联。
CALL dbms_resource_manager.create_plan
('plan1', 'comment')
CALL dbms_resource_manager.create_plan_directive
('plan1', 'group1', 'plan_directive_for_user1', 10, 40);
CALL dbms_resource_manager.create_plan_directive
('plan1', 'group2', 'plan_directive_for_user2', 10, 40);
启用资源计划
CALL dbms_resource_manager.set_resource_manager_plan('plan1');
对租户tenant_1下的用户user1和user2执行sysbench测试,对比不同用户下的CPU使用率和QPS。
sysbench
--db-driver=mysql
--mysql-host=<host> --mysql-port=<port>
--mysql-user=user1@tenant_1
--mysql-password=<password>
--mysql-db=sbtest --table_size=25000 --tables=250
--time=600 --range_selects=0 --skip-trx=1 --threads=128
--percentile=95 --report-interval=1 oltp_read_only run
sysbench
--db-driver=mysql
--mysql-host=<host> --mysql-port=<port>
--mysql-user=user2@tenant_1
--mysql-password=<password>
--mysql-db=sbtest --table_size=25000 --tables=250
--time=600 --range_selects=0 --skip-trx=1 --threads=128
--percentile=95 --report-interval=1 oltp_read_only run
结果下图6所示,11:15-11:17对应租户tenant_1下user1的测试结果,11:19-11:21对应租户tenant_1下user2的测试结果。
图6 用户级隔离指标对比图
结果显示,user1相比user2的CPU使用率,比例接近1:1。user1相比user2的QPS使用率,比例接近1:1。
5. 总结
本文从实现原理和业务场景两个方面,介绍了TaurusDB的多租户管理与资源隔离特性。该特性不仅支持租户间数据隔离,还支持租户级和某个租户下的用户级的资源隔离,为使用者供了更加灵活和安全的资源配置和管理方案。
如果想进一步了解此特性,可以参考官方文档:多租户管理与资源隔离 ,https://support.huaweicloud.cn/kerneldesc-gaussdbformysql/gaussdbformysql_20_0053.html
6. 参考
[1] Linux管理之CGroup简介:https://tech.meituan.com/2015/03/31/cgroups.html
[2] https://kubernetes.io/docs/concepts/architecture/cgroups/
- 点赞
- 收藏
- 关注作者
评论(0)