暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

深度干货 | OceanBase 主动切主技术解读

OceanBase 2021-09-14
810

OceanBase(以下以 OB 简称)中有一个总控节点服务称之为 RootService,用于管理整个集群中的负载均衡等操作。该服务启动在 __all_core_table
的主副本(Leader)所在的服务器上。本文主要介绍 RootService(以下以 RS 简称)对切主的管理。主要分为以下五个部分:
  • 涉及到的基础概念

  • 负载均衡场景描述

  • 对主动切主的管理

  • 切主是如何触发的

  • 切主是如何执行的

涉及到的基础概念

我们先来认识或回顾下 5 个本文会涉及到的基本概念:
1.1. 主副本(Leader)
OB 通过 Multi-Paxos 选举协议来保证集群内数据的高可用。选举以分区为单位,每个分区的多个数据副本形成一个独立的 Paxos 组,其中只有一个分区副本会被选举为主副本(Leader,可读可写),其余通常情况下是从副本(Follower)。
切主,指的是数据分区副本角色在主副本和从副本间的切换,即 OB 对各个分区的主副本位置的调整。

1.2. 均衡组

租户内的切主是以均衡组为维度进行的,而不是简单的将该租户下的所有分区的leader打散。
目前,均衡组有以下 3 种存在形态:
  • PARTITION_TABLE_BALANCE_GROUP

针对不在表组(TableGroup)中的多分区表,每个分区表自己是一个均衡组。
  • TABLE_GROUP_BALANCE_GROUP

针对在表组(TableGroup)中的多分区表,每个表组是一个均衡组。
  • NON_PARTITION_TABLE_BALANCE_GROUP

如果是只有一个分区的分区表、单分区表或非分区表,会统一归入一个均衡组。

1.3. 租户(Tenant)

OB 实现了租户级别的资源隔离,每个数据库租户实例不会感知其他实例的存在,并通过综合的权限控制确保租户数据的安全性。

1.4. 首选区(Primary Zone)

如果你还不太了解 OB 关于区(Zone)的概念,请查阅 OB 官方手册。
首选区可用以指定分区主副本在选择 Zone 时的优先级顺序。OB 可在租户级别指定数据副本 Leader 在 Zone 间的分布策略。此外,向下递进,OB 允许在 Scheme 级别(MySQL 中术语是 Database 级别)、TableGroup级别、直至表级别上配置首选区。创建租户时,属性 primary_zone
缺省为 RANDOM
;其它级别该属性缺省为依次向上继承,直到发现非空属性:表 ➡︎(TableGroup 或 Schema)➡︎ 租户。如果对该属性有显式制定,以显式指定为准。
以下例来具体解读 Zone 的优先策略:
    primary_zone = zone1,zone2;zone3,zone4;zone5
    注意标点符号的文法含义:逗号代表两边优先级相同;分号代表前者优先级更高、后者次之。
    这个例子中,五个 Zone 优先级从高到低被分号划分为三个级别:前两个 zone1
    zone2
    (以逗号隔开)的优先级相同且优先级最高,zone3
    zone4
    (以逗号隔开)的优先级并列,最后面的 zone5
    的优先级最低。

    1.5. 首选域(Primary Region)

    OB 缺省将 Primary Zone 所在的地区(Region,例如北京或上海)指定为 Primary Region。每个 Primary Region 至少要有两个 Zone,这是出于可用和性能考虑 OB 做出的硬性限制,这有助于避免跨 Region 切主导致的性能劣化。
    举例,若三个 Zone 分别属于不同的地区(如杭州、上海和深圳),那么,
    不可取的设置是只将一个 Zone 设置成 Primary Region:
    • zone1; zone2; zone3

    • zone1; zone2, zone3

    可行的设置为:
    • random
      (缺省设置)

    • zone1,zone2,zone3
      (效果和 random
      相同)

    • zone1,zone2;zone3

    负载分摊场景描述

    我们通过代入一个具体场景来简单介绍数据副本 Leader 的负载分摊。
    在同一 Region,新建一个拥有 3 个 Zone 的集群,每个 Zone 中都包含 3 台服务器(OBServer)。
    创建一个数据库租户,该租户的数据根据资源配置允许分布在全部 9 台服务器上。
    假设创建租户时 Primary Zone 没有指定,则其缺省采用 RANDOM 策略。
    随后,用户在该租户下创建了一张拥有 9 个分区的分区表,未指定 Primary Zone。
    此时分区表的 primary_zone 属性继承租户的 RANDOM 缺省配置。
    RS 会将表的 9 个分区的 Leader 均匀打散到 3 个 Zone 的 9 台服务器上。
    分区的 Leader 分布如下:
    当然,如果业务需要,用户也可选择将 Leader 都集中布在某个 Zone 上。比如,我们选择把这张表所有分区 Leader 都布在 Zone1,具体做法:在创建表时直接指定 primary_zone='zone1'
    ;或,在已存在的租户上调整 primary_zone
    配置。
    新的分区 Leader 分布情况如下:

    对主动切主的管理

    RS 是根据当前集群的状态来主动决定各个租户的 Leader 分布。其中集群的状态包括但不限于 Zone 状态、服务器状态、是否发生迁移、租户是否发生 Locality 变更、管理员主动发起切主等。OB 中不同租户相互间资源隔离、相互不影响,同样切主行为也是各个租户相互独立的,因此 RS 对切主的管理是以租户为维度进行的。
    OB 中强读和写都是在 Leader上进行的。若分区 Leader 没有打散而是聚集在个别几台服务器上,大量的 IO 会导致这些服务器的性能很快达到瓶颈。有时用户希望利用所有的服务器来避免达到瓶颈,将 primary_zone
    设置成 RANDOM
    ,语义就是将 Leader 均匀地打散到该租户下所有可用 Zone
    的所有可用服务器上。当然,有时用户就是希望所有的 Leader 都集中在一两个 Zone 上,以避免发生跨城市切主,造成访问延迟,对业务产生不良影响。因此,RS 对切主的管理,首要还是根据用户的需要,结合当前集群的状态,来调整各个租户下所有分区的 Leader 分布。

    切主是如何触发的

    OB 中有一套完整的无主选举的逻辑,当网络不稳定、服务器宕机等非预期的情况导致的各种无主场景,会由选举(Election)模块进行无主选举。本文并不讨论无主选举的场景,只讨论预期内的切主,即所有的切主情况都是在集群正常情况下。
    RS 管理切主有一个重要的配置项,叫做 enable_auto_leader_switch
    ,该配置项决定是否运行 RS 的后台切主线程。
      Alter system set enable_auto_leader_switch = true/false;
      下面的操作 4.1 和 4.2 都不依赖这个开关,即使开关关闭,操作都是可以正常进行的,但其余的操作都依赖这个开关,当开关关闭时,都不能正常执行切主操作。

      4.1. 手动切主

      管理员可手动触发对某个分区的切主,指定其 Leader 或 Follower 的位置。
      参考命令:
        alter system switch replica leader/follower partition = '$partition_id' server = '$server_ip:$server_port';
        alter system switch replica leader/follower zone = '$zone_name' tenant = '$tenant_name';
        alter system switch replica leader/follower server = '$server_ip:$server_port' tenant = '$tenant_name';
        💡注意:该操作不依赖配置项 enable_auto_leader_switch

        4.2. 容灾操作

        容灾操作包括 locality 变更、资源(resource unit)迁移、紧急停机(stop server/zone)及服务器隔离(isolate/fence)。其中对于 locality 变更和资源迁移,当操作的数据副本是 Leader 时,会先行切主,继而进行相关的操作,当变更和迁移完成后再根据当前环境来决定是否需要将主切回。紧急停机及服务器隔离都会先行切主,该命令是阻塞的,即需要等待全部 Leader 都切走后,才能完成相关操作。
        注意:上述操作也不依赖配置项 enable_auto_leader_switch

        4.3. 修改 Primary Zone

        在对象创建时或创建好后,primary_zone
        属性都是可以动态调整的。
        参考命令:
          alter tenant/database/tablegroup/table $name primary_zone = '$new_pz_strategy';

          4.4. 自动均衡

          以均衡组为维度对租户进行自动均衡,OB 中均衡的含义是不同 Zone、不同服务器之间的 Leader 数相差不超过 1。由于均衡是以均衡组为维度,因此具体到一个租户下的 Leader 分布可能不是完全均衡的。
          举例说明,集群有 3 个 Zone,每个 Zone 只有一台服务器。某租户现有 3 张分区表,每张表拥有 10 个分区。每个表自己是一个均衡组,那么每个表的 10 个分区副本 Leader 均衡分布为4:3:3。如果每个均衡组将 4 个 Leader 的 Zone 都指定为Zone1,那么虽然每个均衡组自己内部是均衡的,但租户层面上 Leader 的分布是不均衡的,因为该租户分区副本 Leader 的分布为 12:9:9。
          当然内部进行了一些优化,在创建表的时候,会指定好每个表的 Leader 分布。对于非分区表均衡组的指定是根据当前的非分区表分布,来决定新表的 Leader 分布。而对于分区表,在创建时是根据表的 ID 编号,计算出其 Leader 的分布,如果是连续的表,那么其 Leader 位置是错开的,比如 1 号表的起始 Leader 在 Zone1 上,那么 2 号表的起始 Leader 是在 Zone2 上,依次类推。从而尽量避免在整体上出现 Leader 分布不均衡的问题。

          4.5. 轮转合并

          当打开轮状合并开关时,对于合并目标 Zone,会首先将该 Zone 上的 Leader 都切走,然后才开始对该 Zone 的合并。

          切主是如何执行的

          5.1. Leader 的分配原则

          首先介绍一下 RS 的切主原则。
          集群中可能出现的一种场景是虽然设置了 Primary Zone,但当前的 Leader 分布却与设置不符,这是由于 RS 决定 Leader 分布不仅仅只参考 Primary Zone 信息。
          RS 会获取每个分区的每个副本的相关信息,综合分析后决定 Leader 职责到底分配在哪个数据副本上。
          下面是几个比较关键的参考信息:
          • 服务器或 Zone 的可用状态

          • Primary Region 的设置

          • 合并状态

          • 候选数量(candidate_cnt)

          • 拉黑列表(In_blacklist)

          • Primary Zone 的设置

          这些副本的信息从上到下优先级逐渐降低。
          1、最先看这个副本所在的服务器或 Zone 是否可用,若处于停止状态,则不能将 Leader 切到该副本上;
          2、Primary Region 就是 Primary Zone 所在的地区,RS 会优先将 Leader 切到 Primary Region 中;
          3、查看副本是否处于合并状态,RS 不倾向于将副本的 Leader 切到处于合并状态的 Zone 上;
          4、候选数量(candidate_cnt)是由 clog 层返回给 RS 分区候选 Leader 的服务器列表,RS 根据列表计算出每个服务器能够作为候选分区的数量,数量越大,则该服务器上的副本越有可能成为 Leader;
          5、拉黑列表(in_blacklist)代表该服务器是否在黑名单中,当该分区发生无故卸任时,clog 会将原来 Leader 所在的服务器放入到黑名单中,RS 会根据这份黑名单拉黑对应服务器的 Leader 候选资格;
          6、最后才是 Primary Zone。需要通过前面的 5 项判断,RS 才会听取 Primary Zone 的建议。
          这里没有完整罗列 RS 的全部检查,但已经很清楚,有各种各样的因素会导致 RS 没有按照 Primary Zone 去分配 Leader。

          5.2. Leader 的切换流程

          在启动 RS 服务时,会创建一个切主管理的线程。当自动切主开关打开时,该线程会一直在后台轮询遍历所有的租户,每个租户的切主流程是一致的,下面以一个租户为例,介绍一下租户的切主流程。
          首先通过遍历内部表的方式,获取这个租户下所有的分区信息,包括比较优先级时需要的各种副本信息。RS 是基于一个时间点获取的租户下所有分区信息,由于集群可能是在变化的,因此获取的信息可能会过期失效,根据过期的信息得到的切主方案可能也是不准确的。幸运的是 RS 也考虑到这一点,切主管理线程在后台不断轮询、更新信息,即使有变化,但当集群最终达到稳态时,也可保证 Leader 分布是符合预期的。
          随后根据获取到的信息,按照均衡组,比较分区各个副本的优先级,决定每个均衡组中的分区是否需要进行切主。如果通过比较没有找到需要切主的分区,那么后续的所有操作都跳过,直接遍历下一个租户。否则,根据 RS 生成的切主信息,向底层发送 RPC 消息,来获取和确认当前真实准确的副本信息,从而进一步判断是否真的需要进行切主:如果不需要切,那么直接结束;否则,就根据需要进行切主的信息,给当前的 Leader 发送切主命令 —— 命令其卸任。
          最后等待所有的分区切主完成,等待的方法就是检查内部表,检查待切主分区的 Leader 是否在预期的位置,如果与预期相符,则认为该分区切主成功。否则,继续等待。限定每个租户最长只会等待 64 秒,若超过这个时间还没有检查到对应分区的 Leader 在预期的服务器上,就会强行结束该租户的等待,进行下一个租户的遍历。失败的租户,等待下一轮遍历到该租户时再重复进行同样的切主操作。
          写在最后
          感兴趣的同学可点击阅读原文查看OB 开源源码中的 ob_leader_coordinator.cpp 文件,该文件包含全部 RS 管理切主的流程。RS 对切主的管理,流程相对清晰,但是涉及概念比较多,本文简单对 RS 的切主管理进行了梳理,希望能对大家有所帮助。
          如果您有任何疑问,可以通过以下方式与我们进行交流:
          github:
          https://github.com/oceanbase/oceanbase
          博客问答:
          https://www.oceanbase.com/community/answer
          gitee:
          https://gitee.com/oceanbase

          往期精选

          戳这里
          文章转载自OceanBase,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

          评论