跳转至

集群隔离

AIR 支持通过配置进行 Master 范围选举,可独立部署查询集群和构建任务集群,实现集群间的资源隔离。

节点角色

AIR Tardis 应用节点分为以下 3 个角色:

角色 说明
Follower 节点不参与选举,仅作为从节点运行
Candidate 参与选举 Leader,所有节点的默认角色
Leader 通过选举产生的集群唯一 Leader 节点,负责集群的管理和协调工作

配置方式

登录 Tardis 应用机器,编辑 config.properties 配置文件,新增以下配置项:

node.role=FOLLOWER
  • 参数说明
配置项 可选值 默认值 说明
node.role FOLLOWER / CANDIDATE CANDIDATE 设置当前 Tardis 节点的角色
  • 设置为 FOLLOWER:该节点不参与 Leader 选举,适用于仅需要处理查询或构建任务的节点
  • 设置为 CANDIDATE:该节点参与 Leader 选举(默认行为),具备成为 Leader 的资格

引擎选择路由指南

1. 概述

AIR 通过 QueueRule 实现基于规则的引擎路由。分别为查询(Query)构建(Build)两类场景提供规则匹配。

核心流程

请求进入 → 按 priority 排序的规则列表 → 逐条匹配 expr → 命中则返回对应 queueName → 全部未命中则回退到默认引擎

2. QueueRule 字段说明

字段 类型 必填 说明
name String 规则名称,用于标识
description String 规则描述
expr String 是* Aviator 表达式,求值结果必须为 Boolean
queueName String 匹配成功后路由到的队列名,需与 ComputeUnit.queueName 对应
priority int 优先级,数字越小优先级越高,优先匹配

3. 可用变量

3.1 查询规则(queryRules)可用变量

变量名 类型 默认值 说明
userName String "" 执行查询的用户名
groupName String "" 用户所属分组
queryCost double 0.0 查询预估代价
clientType String "" 客户端类型
clientIp String "" 客户端 IP 地址
catalogs String "" 逗号分隔的 catalog 名称列表
originalCatalogs String "" 原始 catalog 名称列表
schemas String "" 逗号分隔的 schema 名称列表
originalSchemas String "" 原始 schema 名称列表
tables String "" 逗号分隔的表名列表
originalTables String "" 原始表名列表
sourceTypes String "" 逗号分隔的数据源类型列表

3.2 构建规则(buildRules)可用变量

变量名 类型 默认值 说明
owner String "" 数据集 / 投影 的 owner
queryCost double 0.0 构建预估代价
datasetPath String "" 数据集路径
taskType String "" 任务类型,如 "RP""EXPORT"
sourceTypes String null 逗号分隔的数据源类型列表
rpName String "" RP 名称(RP 构建时使用)
exportName String "" 导出名称(Export 任务时使用)

3.3 内置变量取值范围

clientType 取值

变量名 说明
PRESTO_JDBC Presto JDBC 连接
SERVICE 数据服务调用
REPORT 报表查询
ACCELERATION 加速/投影构建查询
UI DWS UI 界面查询
INTERNAL 内部系统查询
EXPORT 导出任务查询
MYSQL_JDBC MySQL 协议连接
PyHive Python DB-API
TRINO_JDBC Trino JDBC 连接
UNKNOWN 无法识别的客户端,兜底值

taskType 取值

变量名 说明
RP 投影构建
EXPORT 数据导出任务

sourceTypes 取值

HIVE2,HIVE3,HIVE2_JDBC,HIVE3_JDBC,ICEBERG_HIVE,ICEBERG_HADOOP,MYSQL,MYSQL5,POSTGRESQL,ORACLE,MSSQL,MSSQL_2022,TIDB,
DAMENG,GAUSS,OPEN_GAUSS,HANA,DB2,VERTICA,INTER_SYSTEMS,OCEANBASE_MYSQL,OCEANBASE_ORACLE,STARROCKS,DORIS,CLICKHOUSE,
CMB_CLICKHOUSE,PRESTO,PRESTO_JDBC,SPARK,TRINO,IMPALA,CALCITE,DATABRICKS,ATHENA,SNOWFLAKE,HUAWEI_DWS,ELASTICSEARCH,
HBASE,MONGODB,NEBULA,ARGODB,HDFS,FILE,S3,AZURE_BLOB,API,AIR,LARKSUITE,UNKNOWN_SOURCE_TYPE

4. 表达式语法(Aviator 5.3.0)

expr 字段使用 AviatorScript 表达式语言,求值结果必须返回 truefalse

4.1 运算符

比较运算符

运算符 说明 示例
== 等于 userName == "admin"
!= 不等于 clientType != "JDBC"
> 大于 queryCost > 1000
>= 大于等于 queryCost >= 500
< 小于 queryCost < 100
<= 小于等于 queryCost <= 200

逻辑运算符

运算符 说明 示例
&& 逻辑与 userName == "admin" && queryCost > 100
\|\| 逻辑或 clientType == "JDBC" \|\| clientType == "REST"
! 逻辑非 !string.contains(sourceTypes, "MYSQL")

三元运算符

queryCost > 1000 ? true : false

正则匹配运算符

运算符 说明 示例
=~ 正则匹配 userName =~ /admin.*/

4.2 内置字符串函数

以下是在规则表达式中最常用的函数

函数 返回值 说明 示例
string.contains(s, sub) boolean 字符串包含判断 string.contains(sourceTypes, "DORIS")
string.startsWith(s, prefix) boolean 前缀判断 string.startsWith(datasetPath, "/prod/")
string.endsWith(s, suffix) boolean 后缀判断 string.endsWith(rpName, "_daily")
string.length(s) long 字符串长度 string.length(userName) > 0
string.substring(s, begin, end) String 子串截取 string.substring(catalogs, 0, 4) == "prod"
string.indexOf(s, sub) int 查找子串位置,不存在返回 -1 string.indexOf(sourceTypes, "HIVE") >= 0
string.replace_first(s, regex, replacement) String 正则替换第一个 -
string.replace_all(s, regex, replacement) String 正则替换全部 -
string.split(s, regex, limit) String[] 字符串分割 count(string.split(sourceTypes, ",", -1)) > 2

4.3 内置数学函数

函数 说明 示例
math.abs(n) 绝对值 math.abs(queryCost) > 100
math.ceil(n) 向上取整 math.ceil(queryCost) > 500
math.floor(n) 向下取整 -
math.round(n) 四舍五入 -
math.pow(a, b) 幂运算 -
math.log(n) 自然对数 -
math.log10(n) 以10为底对数 -
math.sqrt(n) 平方根 -

4.4 其他常用内置函数

函数 说明 示例
nil 空值常量 sourceTypes != nil
count(collection) 集合/数组元素个数 count(string.split(catalogs, ",", -1)) > 1
include(collection, element) 集合是否包含元素 include(string.split(sourceTypes, ",", -1), "DORIS")
seq.every(seq, predicate) 所有元素满足条件 -
seq.some(seq, predicate) 至少一个元素满足条件 -
seq.filter(seq, predicate) 过滤元素 -
seq.map(seq, function) 映射函数 -

5. 配置示例

在 queryComputeUnits / buildComputeUnits 中为需要路由的引擎配置 “queueName”,然后配置对应的逻辑规则 queryRules / buildRules

5.1 按数据源类型路由构建引擎

{
  "buildRules": [
    {
      "name": "iceberg-hive-doris-build-rule",
      "description": "Route ICEBERG_HIVE and DORIS RP builds to dedicated engine",
      "expr": "string.contains(sourceTypes, \"ICEBERG_HIVE\") || string.contains(sourceTypes, \"DORIS\")",
      "queueName": "doris_build_queue",
      "priority": 1
    }
  ]
}

5.2 按用户名路由查询引擎

{
  "queryRules": [
    {
      "name": "admin-query-rule",
      "description": "Admin users route to high-priority engine",
      "expr": "userName == \"admin\" || userName == \"root\"",
      "queueName": "high_priority_queue",
      "priority": 1
    }
  ]
}

5.3 按查询代价路由

{
  "queryRules": [
    {
      "name": "heavy-query-rule",
      "description": "Route heavy queries to dedicated engine",
      "expr": "queryCost > 10000",
      "queueName": "heavy_query_queue",
      "priority": 1
    },
    {
      "name": "light-query-rule",
      "description": "Route light queries to fast engine",
      "expr": "queryCost <= 100",
      "queueName": "fast_query_queue",
      "priority": 2
    }
  ]
}

5.4 按客户端 IP 段路由

{
  "queryRules": [
    {
      "name": "internal-ip-rule",
      "description": "Internal network uses dedicated engine",
      "expr": "string.startsWith(clientIp, \"10.5.\")",
      "queueName": "internal_queue",
      "priority": 1
    }
  ]
}

5.5 组合条件

{
  "queryRules": [
    {
      "name": "specific-catalog-user-rule",
      "description": "Specific user querying specific catalog",
      "expr": "userName == \"analyst\" && string.contains(catalogs, \"prod_catalog\")",
      "queueName": "analyst_prod_queue",
      "priority": 1
    }
  ]
}

5.6 正则匹配

{
  "queryRules": [
    {
      "name": "table-pattern-rule",
      "description": "Route queries on tmp tables to temp engine",
      "expr": "tables =~ /.*tmp_.*/",
      "queueName": "temp_query_queue",
      "priority": 1
    }
  ]
}

5.7 按任务类型路由构建引擎

{
  "buildRules": [
    {
      "name": "export-task-rule",
      "description": "Export tasks use dedicated engine",
      "expr": "taskType == \"EXPORT\"",
      "queueName": "export_queue",
      "priority": 1
    }
  ]
}

5.8 精确匹配数据源类型(使用 split + include)

{
  "buildRules": [
    {
      "name": "exact-doris-rule",
      "description": "Exact match DORIS in sourceTypes (avoid matching DORIS_XXX)",
      "expr": "include(string.split(sourceTypes, \",\", -1), \"DORIS\")",
      "queueName": "doris_queue",
      "priority": 1
    }
  ]
}

构建实例自动重启

AIR 支持对 Kyuubi Spark 引擎实例进行定时检测和自动重启,避免 Spark Driver 长时间运行导致内存泄漏或性能下降。

配置方式

编辑集群配置中对应的 ComputeUnit(buildComputeUnits)内添加 sparkManagerConfig 字段,或通过集群管理 API 更新配置。

配置项说明

配置项 类型 必填 默认值 说明
kyuubiRestUrl String - Kyuubi REST API 地址,支持逗号分隔多个地址
yarnUrl String - YARN ResourceManager REST API 地址
maxRuntimeHours Integer 24 Spark 实例最大运行时长(小时),超过后触发重启
stopImmediately boolean false 立即停止模式:超时后直接停止并重启,不等待运行中的任务完成
stopGraceful boolean false 优雅停止模式:先从 ZooKeeper 下线(不再接受新任务),等待运行中的任务完成后再停止并重启

重启策略

根据 stopImmediatelystopGraceful 的配置,支持三种重启策略:

策略 配置 行为 适用场景
优雅停止 stopGraceful = true 先下线、等无任务后再停止重启 生产环境推荐
立即停止 stopImmediately = true 超时后直接停止重启,不等待任务 对中断容忍度高的场景
空闲停止 两者均为 false(默认) 仅在无运行中任务时才停止重启 一般场景

注意stopImmediately 优先级高于 stopGraceful,两者同时为 true 时按立即停止处理。

配置示例

cluster.json 配置

{
  "buildComputeUnits": [
    {
      "priority": 4,
      "computeUnitName": "spark_kyuubi_build",
      "computeUnitType": "BUILD",
      "engineType": "KYUUBI_SPARK",
      "connectionConfigs": {
        "url": "jdbc:kyuubi://zk1:2181,zk2:2181,zk3:2181/;serviceDiscoveryMode=zooKeeper;zooKeeperNamespace=kyuubi-tardis;",
        "username": "devops"
      },
      "sparkManagerConfig": {
        "kyuubiRestUrl": "http://kyuubi-host:10099",
        "yarnUrl": "http://yarn-rm-host:8088/",
        "maxRuntimeHours": 24,
        "stopGraceful": true,
        "stopImmediately": false
      }
    }
  ]
}

关闭自动重启

如果不需要自动重启功能,不配置 sparkManagerConfig 字段即可。已配置的情况下,移除 sparkManagerConfig 或将 kyuubiRestUrl / yarnUrl 置空即可关闭。