• 多租户设计
    • 背景
    • 设计原则
    • 名词解释
    • 主要思路
    • 设计方案
    • SQL 接口
    • 详细设计

    多租户设计

    该功能为实验性质,暂不建议在生产环境使用。

    背景

    Doris作为一款PB级别的在线报表与多维分析数据库,对外通过开放云提供云端的数据库服务,并且对于每个云上的客户都单独部署了一套物理集群。对内,一套物理集群部署了多个业务,对于隔离性要求比较高的业务单独搭建了集群。针对以上存在几点问题:

    • 部署多套物理集群维护代价大(升级、功能上线、bug修复)。
    • 一个用户的查询或者查询引起的bug经常会影响其他用户。
    • 实际生产环境单机只能部署一个BE进程。而多个BE可以更好的解决胖节点问题。并且对于join、聚合操作可以提供更高的并发度。
      综合以上三点,Doris需要新的多租户方案,既能做到较好的资源隔离和故障隔离,同时也能减少维护的代价,满足共有云和私有云的需求。

    设计原则

    • 使用简单
    • 开发代价小
    • 方便现有集群的迁移

    名词解释

    • FE: Frontend,即 Doris 中用于元数据管理即查询规划的模块。
    • BE: Backend,即 Doris 中用于存储和查询数据的模块。
    • Master: FE 的一种角色。一个Doris集群只有一个Master,其他的FE为Observer或者Follower。
    • instance:一个 BE 进程及时一个 instance。
    • host:单个物理机
    • cluster:即一个集群,由多个instance组成。
    • 租户:一个cluster属于一个租户。cluster和租户之间是一对一关系。
    • database:一个用户创建的数据库

    主要思路

    • 一个host上部署多个BE的instance,在进程级别做资源隔离。
    • 多个instance形成一个cluster,一个cluster分配给一个业务独立的的租户。
    • FE增加cluster这一级并负责cluster的管理。
    • CPU,IO,内存等资源隔离采用cgroup。

    设计方案

    为了能够达到隔离的目的,引入了虚拟cluster的概念。

    • cluster表示一个虚拟的集群,由多个BE的instance组成。多个cluster共享FE。
    • 一个host上可以启动多个instance。cluster创建时,选取任意指定数量的instance,组成一个cluster。
    • 创建cluster的同时,会创建一个名为superuser的账户,隶属于该cluster。superuser可以对cluster进行管理、创建数据库、分配权限等。
    • Doris启动后,汇创建一个默认的cluster:default_cluster。如果用户不希望使用多cluster的功能,则会提供这个默认的cluster,并隐藏多cluster的其他操作细节。
      具体架构如下图: Multi Tenant 多租户 - 图1

    SQL 接口

    • 登录

    默认集群登录名: user_name@default_cluster 或者 user_name

    自定义集群登录名:user_name@cluster_name

    mysqlclient -h host -P port -u user_name@cluster_name -p password

    • 添加、删除、下线(decommission)以及取消下线BE

    ALTER SYSTEM ADD BACKEND "host:port" ALTER SYSTEM DROP BACKEND "host:port" ALTER SYSTEM DECOMMISSION BACKEND "host:port" CANCEL DECOMMISSION BACKEND "host:port"

    强烈建议使用 DECOMMISSION 而不是 DROP 来删除 BACKEND。DECOMMISSION 操作会首先将需要下线节点上的数据拷贝到集群内其他instance上。之后,才会真正下线。

    • 创建集群,并指定superuser账户的密码

    CREATE CLUSTER cluster_name PROPERTIES ("instance_num" = "10") identified by "password"

    • 进入一个集群

    ENTER cluster_name

    • 集群扩容、缩容

    ALTER CLUSTER cluster_name PROPERTIES ("instance_num" = "10")

    当指定的实例个数多于cluster现有be的个数,则为扩容,如果少于则为缩容。

    • 链接、迁移db

    LINK DATABASE src_cluster_name.db_name dest_cluster_name.db_name

    软链一个cluster的db到另外一个cluster的db ,对于需要临时访问其他cluster的db却不需要进行实际数据迁移的用户可以采用这种方式。

    MIGRATE DATABASE src_cluster_name.db_name dest_cluster_name.db_name

    如果需要对db进行跨cluster的迁移,在链接之后,执行migrate对数据进行实际的迁移。

    迁移不影响当前两个db的查询、导入等操作,这是一个异步的操作,可以通过SHOW MIGRATIONS查看迁移的进度。

    • 删除集群

    DROP CLUSTER cluster_name

    删除集群,要求先手动删除的集群内所有database。

    • 其他

    SHOW CLUSTERS

    展示系统内已经创建的集群。只有root用户有该权限。

    SHOW BACKENDS

    查看集群内的BE instance。

    SHOW MIGRATIONS

    展示当前正在进行的db迁移任务。执行完db的迁移后可以通过此命令查看迁移的进度。

    详细设计

    • 命名空间隔离
      为了引入多租户,需要对系统内的cluster之间的命名空间进行隔离。

    Doris现有的元数据采用的是image + journal 的方式(元数据的设计见相关文档)。Doris会把涉及元数据的操作的记录为一个 journal (操作日志),然后定时的按照图1的方式写成image,加载的时候按照写入的顺序读即可。但是这样就带来一个问题已经写入的格式不容易修改,比如记录数据分布的元数据格式为:database+table+tablet+replica 嵌套,如果按照以往的方式要做cluster之间的命名空间隔离,则需要在database上增加一层cluster,内部元数据的层级变为:cluster+database+table+tablet+replica,如图2所示。但加一层带来的问题有:

    • 增加一层带来的元数据改动,不兼容,需要按照图2的方式cluster+db+table+tablet+replica层级写,这样就改变了以往的元数据组织方式,老版本的升级会比较麻烦,比较理想的方式是按照图3在现有元数据的格式下顺序写入cluster的元数据。
      • 代码里所有用到db、user等,都需要加一层cluster,一工作量大改动的地方多,层级深,多数代码都获取db,现有功能几乎都要改一遍,并且需要在db的锁的基础上嵌套一层cluster的锁。
        Multi Tenant 多租户 - 图2

    综上这里采用了一种通过给db、user名加前缀的方式去隔离内部因为cluster之间db、user名字冲突的问题。

    如下,所有的sql输入涉及db名、user名的,都需要根据自己所在的cluster来拼写db、user的全名。

    Multi Tenant 多租户 - 图3

    1. 采用这种方式以上两个问题不再有。元数据的组织方式也比较简单。即采用**图3**每个cluster记录下属于自己clusterdbuser,以及节点即可。
    • BE 节点管理
      每个cluster都有属于自己的一组instance,可以通过SHOW BACKENDS查看,为了区分出instance属于哪个cluster以及使用情况,BE引入了多个状态:

      • free:当一个BE节点被加入系统内,此时be不属于任何cluster的时候处于空闲状态
      • using:当创建集群、或者扩容被选取到一个cluster内则处于使用中。
    • cluster decommission:如果执行缩容量,则正在执行缩容的be处于此状态。结束后,be状态变为free。
    • system decommission:be正在下线中。下线完成后,该be将会被永久删除。
      只有root用户可以通过SHOW PROC "/backends"中cluster这一项查看集群内所有be的是否被使用。为空则为空闲,否则为使用中。SHOW BACKENDS只能看到所在cluster的节点。以下是be节点状态变化的示意图。

    Multi Tenant 多租户 - 图4

    • 创建集群

    只有root用户可以创建一个cluster,并指定任意数量的BE instance。

    支持在相同机器上选取多个instance。选择instance的大致原则是:尽可能选取不同机器上的be并且使所有机器上使用的be数尽可能均匀。

    对于使用来讲,每一个user、db都属于一个cluster(root除外)。为了创建user、db,首先需要进入一个cluster。在创建cluster的时候系统会默认生成这个cluster的管理员,即superuser账户。superuser具有在所属cluster内创建db、user,以及查看be节点数的权限。所有的非root用户登录必须指定一个cluster,即user_name@cluster_name

    只有root用户可以通过SHOW CLUSTER查看系统内所有的cluster,并且可以通过@不同的集群名来进入不同的cluster。对于除了root之外的用户cluster都是不可见的。

    为了兼容老版本Doris内置了一个名字叫做default_cluster的集群,这个名字在创建集群的时候不能使用。

    Multi Tenant 多租户 - 图5

    • 集群扩容

    集群扩容的流程同创建集群。会优先选取不在集群之外的host上的BE instance。选取的原则同创建集群。

    • 集群缩容、CLUSTER DECOMMISSION

    用户可以通过设置 cluster 的 instance num 来进行集群缩容。

    集群的缩容会优先在BE instance 数量最多的 host 上选取 instance 进行下线。

    1. 用户也可以直接使用 `ALTER CLUSTER DECOMMISSION BACKEND` 来指定BE,进行集群缩容。

    Multi Tenant 多租户 - 图6

    • 建表

    为了保证高可用,每个分片的副本必需在不同的机器上。所以建表时,选择副本所在be的策略为在每个host上随机选取一个be。然后从这些be中随机选取所需副本数量的be。总体上做到每个机器上分片分布均匀。

    因此,加入需要创建一个3副本的分片,即使cluster包含3个或以上的instance,但是只有2个或以下的host,依然不能创建该分片。

    • 负载均衡

    负载均衡的粒度为cluster级别,cluster之间不做负载均衡。但是在计算负载是在host一级进行的,而一个host上可能存在多个不同cluster的BE instance。 cluster内,会通过每个host上所有分片数目、存储使用率计算负载,然后把负载高的机器上的分片往负载低的机器上拷贝(详见负载均衡相关文档)。

    • LINK DATABASE(软链)

    多个集群之间可以通过软链的方式访问彼此的数据。链接的级别为不同cluster的db。

    通过在一个cluster内,添加需要访问的其他cluster的db的信息,来访问其他cluster中的db。

    当查询链接的db时,所使用的计算以及存储资源为源db所在cluster的资源。

    被软链的db不能在源cluster中删除。只有链接的db被删除后,才可以删除源db。而删除链接db,不会删除源db。

    • MIGRATE DATABASE
      db可以在cluster之间进行物理迁移。

    要迁移db,必须先链接db。执行迁移后数据会迁移到链接的db所在的cluster,并且执行迁移后源db被删除,链接断开。

    数据的迁移,复用了负载均衡以及副本恢复中,复制数据的流程(详见负载均衡相关文档)。具体实现上,在执行MIRAGTE命令后,Doris会在元数据中,将源db的所有副本所属的cluster,修改为目的cluster。

    Doris会定期检查集群内机器之间是否均衡、副本是否齐全、是否有多余的副本。db的迁移即借用了这个流程,在检查副本齐全的时候同时检查副本所在的be是否属于该cluster,如果不属于,则记入要恢复的副本。并且副本多余要删除的时候会优先删除cluster外的副本,然后再按照现有的策略选择:宕机的be的副本->clone的副本->版本落后的副本->负载高的host上的副本,直到副本没有多余。

    Multi Tenant 多租户 - 图7

    • BE的进程隔离
        为了实现be进程之间实际cpu、io以及内存的隔离,需要依赖于be的部署。部署的时候需要在外围配置cgroup,把要部署的be的进程都写入cgroup。如果要实现io的物理隔离各be配置的数据存放路径需要在不同磁盘上,这里不做过多的介绍。

    原文: http://doris.apache.org/guides/multi-tenant.html