孙坚运:OceanBase 技术专家
从事存储引擎相关工作,曾参加 TPC-C 项目攻坚和蚂蚁双 11 大促支持,目前主要负责 IO 调度、DDL 等方向的设计和研发工作。
此前,在《为什么资源隔离对 HTAP 至关重要?》一文中,我们谈了 HTAP 为何需要资源隔离及其实现方法。资源隔离作为一种能力,基于它可以生长出许多有意思的场景,不仅是 HTAP,还包括多租户、Pay as You Go 等。本质上,资源隔离叠加上资源池化(云)之后,可以让每种资源就像自来水一样,用多少就取多少。OceanBase 4.0 已经具备了 CPU、内存等资源的隔离能力,我们在本次 4.1 版本升级中,着重强化了磁盘 IO 的隔离能力,并提供了简洁灵活的使用方式。
我们认为,磁盘 IO 隔离是资源隔离至关重要的组成部分,实现磁盘 IO 隔离可以带给用户更完整的资源管控能力。本篇文章将分享 OceanBase 对磁盘 IO 隔离的思考,介绍磁盘 IO 隔离的配置方法,并通过实验展示 4.1 磁盘 IO 隔离能力的实际效果。
一、为什么要做磁盘 IO 隔离
有人问:资源隔离尤其是磁盘 IO 隔离,有必要做吗?为什么不直接拆分机器呢?比如将 TP 和 AP 负载放在不同的副本、放在不同的机器执行,而多租户更是可以将不同租户放在不同机器,直接实现物理隔离。在我看来,这确实是一种简便的解决方案,但它也存在很多局限,其中最主要的问题是成本。我们假设某游戏公司有 A、B 两个租户分别处理国际与国内业务,由于时差问题,这两个租户的负载基本互补,这两个租户固然可以分别独占机器,但这样会浪费一半的资源。
对于磁盘 IO 资源而言,数据紧耦合的负载并不容易拆分机器。比如数据库中的备份、迁移、重整等操作,都强依赖于数据的大量读写。如果没有磁盘 IO 隔离,这些任务很可能影响业务的吞吐量和响应时间。其实 TP 和 AP 的负载也很难像想象中一样拆分到不同机器,且不说 TP 和 AP 之间很多时候并没有明显界线,即便都是 TP 负载,也会存在业务上的优先级区分,此时又该如何处理呢?
磁盘 IO 作为一种柔性资源,负载之间可以互相挤占。内存空间等资源属于刚性资源,因为这类资源的描述是标量,一块内存被 A 占用了,就不能再分配给 B 使用。而磁盘 IO 属于柔性资源,因为这类资源的描述都是单位时间的处理能力,A、B 负载可以同时读写磁盘。对于刚性资源的隔离,可以像切蛋糕一样做简单切分。但对于柔性资源,还可以考虑负载间的互相挤占,就像一条小河同时灌溉 A、B 两块农田,当 A 农田的水流调小时,B 农田水流可以自然调大。
二、好的磁盘 IO 隔离什么样
OceanBase 要实现什么样的磁盘 IO 隔离呢?首先要搞清楚,客户需要什么样的磁盘 IO 隔离,但这说起来有点复杂,正如一千个读者就有一千个哈姆雷特,不同客户对磁盘 IO 隔离的理解与需求也不尽相同:
- 有的客户希望 IO 隔离保证资源独占,承诺给到 200M 磁盘带宽,那必须预留好,不能用作他用;
- 有的客户希望磁盘 IO 隔离可以限制资源消耗,想保证某些负载的资源用量一定不超过给定阈值;
- 还有的客户说:我只想在资源不够时按权重分配资源,资源充足时不需要考虑资源隔离。
其实,在资源隔离技术领域,上面三点需求分别对应了 reservation, limitation, proportion(保留、限制、比例)三种隔离语义,OceanBase 磁盘 IO 隔离的目标就是实现这三种语义。
三、如何配置磁盘 IO 隔离
有了上文提到的三种语义后,如何让用户方便地使用呢?我们设计了一个灵活好用的配置方法。
租户间的磁盘 IO 隔离比较容易配置,如果以 IOPS 为度量单位的话,我们可以在 unit config 中指定一个租户的 MIN_IOPS, MAX_IOPS,以及 IOPS_WEIGHT,契合上述三种不同的隔离需求。例如:
alter resource unit set tp_unit min_iops=20000, max_iops=40000, iops_weight=500;
租户内具体负载的磁盘 IO 隔离又该如何配置呢?考虑到兼容用户已有的使用习惯,OceanBase 的选择是扩展 Oracle 的 ResourceManager 包。
以下我们将举例说明利用 ResourceManager 如何隔离 tp 和 ap 两种负载磁盘 IO 资源:
- 首先我们定义一个资源管理计划 htap_plan 和两个资源消费组 tp_group 和 ap_group;
- 然后我们将 tp_group 和 ap_group 绑定到 htap_plan 并分配资源,我们为 tp_group 分配更多资源,为 ap_group 分配更少资源,其中 MIN_IOPS,MAX_IOPS,WEIGHT_IOPS 均为占用租户 unit 的百分比;
- 最后设置负载到资源消费组的映射规则,这里举例是按照用户名映射,例如 trade 用户的负载全部使用 tp_group 的资源。
# 定义资源管理计划
BEGIN DBMS_RESOURCE_MANAGER.CREATE_PLAN(
PLAN => 'htap_plan');
END; /
# 定义资源组
BEGIN DBMS_RESOURCE_MANAGER.CREATE_CONSUMER_GROUP(
CONSUMER_GROUP => 'tp_group',
COMMENT => 'resource group for oltp applications');
END;/
BEGIN DBMS_RESOURCE_MANAGER.CREATE_CONSUMER_GROUP(
CONSUMER_GROUP => 'ap_group',
COMMENT => 'resource group for olap applications');
END;/
# 分配资源
BEGIN DBMS_RESOURCE_MANAGER.CREATE_PLAN_DIRECTIVE(
PLAN => 'htap_plan',
GROUP_OR_SUBPLAN => 'tp_group' ,
COMMENT => 'more resource for tp_group',
MGMT_P1 => 100,
MIN_IOPS => 60,
MIX_IOPS => 100,
WEIGHT_IOPS => 100);
END; /
BEGIN DBMS_RESOURCE_MANAGER.CREATE_PLAN_DIRECTIVE(
PLAN => 'htap_plan',
GROUP_OR_SUBPLAN => 'ap_group' ,
COMMENT => 'less resource for ap_group',
MGMT_P1 => 20,
MIN_IOPS => 0,
MIX_IOPS => 80,
WEIGHT_IOPS => 20);
END; /
# 负载绑定
BEGIN
DBMS_RESOURCE_MANAGER.SET_CONSUMER_GROUP_MAPPING
('USER', 'trade', 'tp_group');
END;/
BEGIN
DBMS_RESOURCE_MANAGER.SET_CONSUMER_GROUP_MAPPING
('USER', 'analysis', 'ap_group');
END;/
资源消费组的映射规则还支持函数名和列名,通过函数名的映射,可以让后台任务也通过 Resource Manager 控制资源用量,通过列名的映射,甚至可以将资源隔离粒度控制到 SQL 语句级别。例如:
# 后台任务绑定资源组
BEGIN
DBMS_RESOURCE_MANAGER.SET_CONSUMER_GROUP_MAPPING
('FUNCTION', 'CAOPACTION_HIGH', 'background_group');
END;/
# 特定SQL绑定资源组
BEGIN
DBMS_RESOURCE_MANAGER.SET_CONSUMER_GROUP_MAPPING
('COLUMN', 'test.t1.c1 = 3', 'big1_group');
END;/
四、4.X 磁盘 IO 隔离效果测试
验证磁盘 IO 隔离能力
为了验证磁盘 IO 隔离的能力,我们首先用单测做了一项仿真实验:我们设置 4 个租户,每个租户启动 64 个线程发送 IO 请求,IO 请求固定为 16KB 随机读,租户 1、2、4 的负载持续 20 秒,租户 3 的负载从第 10 秒开始,持续 10 秒。实验磁盘 IOPS 上限大概在 6w,如果不加限制,任意一个租户单独都可以打满磁盘。
首先验证租户间磁盘 IO 隔离,各租户的配置和实验结果如表 1 和图 1 所示:
- 磁盘已经打满时,新加入的租户 3 依然拥有 1 万 IOPS,因为其通过 MIN_IOPS 预留了 1 万;
- 租户 4 的 IOPS 没有超过 5 千,因为其通过 MAX_IOPS 设置了资源上限;
- 无论负载如何变化,租户 1 和租户 2 的 IOPS 比值大概为 2:1,正如权重比例要求。
接下来,我们将验证租户内负载的隔离。我们在租户 2 内设置了 4 个类别的负载,各负载的配置和实验结果如表 2 和图 2 所示:
- B 负载稳定在近 2000 IOPS,哪怕其权重为 0,因为 B 负载通过 MIN_PERCENT 预留了租户 MIN_IOPS 97% 的资源;
- A 负载稳定在 1000 IOPS 左右,因为其 MAX_PERCENT 为 1,最多只能使用租户 MAX_IOPS 1% 的资源;
- C、D 负载的 IOPS 比例始终保持大约 2:1,因为其权重为 50:25。
从上述实验可以看出,OceanBase 在支持租户间磁盘 IO 隔离的同时,还支持租户内负载间的磁盘 IO 隔离,且都满足 reservation,limitation,proportion 三种隔离语义。
磁盘 IO 隔离实时调整
有的同学可能发现,上述实验中,磁盘 IO 隔离的配置始终没变,那 OceanBase 是否支持实时调整配置呢?答案是肯定的。我们将通过以下实验来说明。
首先,我们准备了一张大表,使用并行查询做全表扫描,在扫描的同时,管理员去不断修改租户的 MAX_IOPS,视频中可以看到,操作系统监控中 IOPS 将随着管理员的修改不断变化。
细心的同学会发现,操作系统监控到的 IOPS 为什么总比管理员设置的 IOPS 少一些呢?这是因为 OceanBase 对 IO 请求的代价做了归一化。
举例来说,64KB 随机读的代价和 4KB 随机读的代价是不一样的,租户规格中配置的 IOPS 使用的基准代价是 16KB 随机读,但实际发生的 IO 请求的尺寸大约是 20KB,经过代价折算后,操作系统中看到的 IOPS 便有些差异。感兴趣的同学可以查看 ob_io_manager 相关代码。
五、写在最后
通过 OceanBase 4.x 的资源隔离能力,用户已可以灵活控制不同负载的资源分配。我们也会不断完善这项能力,解决更多用户关注的问题,例如目前用户仍需要关心租户的 unit 规格以及 unit 数量。OceanBase 希望把更好的资源隔离能力和使用体验带给大家,让用户像使用自来水一样使用数据库,随着业务流量的变化,OceanBase 自动分配所需的资源,就像使用一个无限资源的单机数据库一样。路虽远,行则将至。
最后,欢迎大家在评论区留言,一起探讨对磁盘 IO 隔离能力的看法。