ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

【自助查询】建设查询成本归因

【自助查询】建设查询成本归因

总述

基于requestId、taskId、attemptId、applicationId建立全链路成本归因链路,把租户、用户、业务线、数据集和 SQL 指纹与实际 CPU、内存、存储、网络等资源消耗关联起来。

不同情况不同计算

  1. 不同引擎的资源指标通过 EngineConn 适配层统一,再按照企业资源单价计算任务成本。
  2. 对于重试任务,我会区分 Task 总成本、每次 Attempt 成本和失败重试成本;
  3. 对于共享集群,则按实际资源使用量进行分摊。

最终将成本、SLA、执行耗时和成功率反哺路由层,让平台在满足 SLA 的前提下优先选择成本更低、复用率更高的执行环境。

论述

成本归因要解决的问题是:

  1. 查询执行消耗了多少计算资源,
  2. 这些资源应该归属到哪个租户、用户、业务线、数据集和 SQL 上
  3. 能够进一步判断哪些查询成本过高、哪些引擎或集群更划算。

我会把成本归因建立在全链路任务标识之上,而不是只根据最终结果或集群总账单估算。

整体归因分析链路是:

查询请求 -> Task -> ExecutionAttempt -> RouteDecision -> EngineConn -> 外部 Application -> 资源指标 -> 成本计算 -> 业务归因

第一,建立归因维度

提交请求时,统一入口需要补齐归因上下文:

tenantId userId businessLine application dataset sqlFingerprint taskId attemptId routeDecisionId engine cluster queue

其中:

tenantId:成本归属哪个租户 businessLine / application:成本归属哪个业务线或应用 dataset:成本由哪些数据集产生 sqlFingerprint:哪类 SQL 消耗成本最高 taskId / attemptId:哪一次任务、哪一次执行尝试产生了成本

这些信息要一路透传到 Linkis、EngineConn 和外部计算引擎,最后通过applicationId和资源监控系统关联起来。

第二,采集真实资源消耗

对于一次查询,不能只记录执行时长,还要采集实际资源:

queueTime executeTime cpuTime memoryUsage executorCores executorMemory executorCount scanBytes shuffleBytes diskBytes

例如 Spark 任务需要采集:

applicationId vcoreSeconds memorySeconds executor 数量 Executor 运行时长 Shuffle 数据量

Trino 或 JDBC 可能没有完全相同的指标,因此需要在引擎适配层统一成平台模型:

cpuCost memoryCost storageCost networkCost engineCost

这样上层不需要感知 Spark、Trino 和 JDBC 指标的差异。

第三,建立成本计算模型

成本不一定直接等于执行时间,应该根据企业实际资源单价计算。

例如:

计算成本 = CPU 使用量 × CPU 单价 + 内存使用量 × 内存单价 + 存储成本 + 网络成本 + 引擎或集群附加成本

Spark 可以按:

vcoreSeconds memorySeconds

计算。

Trino 可以按:

CPU 时间 内存使用时间 查询执行时间

计算。

如果企业暂时没有精确的云账单,也可以先建立内部成本模型:

CPU:0.01 元 / core-minute 内存:0.005 元 / GB-minute 存储:0.02 元 / GB

例如一次查询实际消耗:

CPU:40 core-minutes 内存:80 GB-minutes

那么:

CPU 成本 = 40 × 0.01 = 0.4 元 内存成本 = 80 × 0.005 = 0.4 元 总成本 = 0.8 元

重点是先保证归因口径统一,后续再替换成云厂商或集群真实计费价格。

第四,处理重试和共享资源

这里要特别区分:

Task 成本 ExecutionAttempt 成本

如果第一次路由到 Trino 失败,第二次切换到 Spark:

Task:查询订单金额 Attempt 1:Trino,消耗 0.2 元,失败 Attempt 2:Spark,消耗 1.5 元,成功

这次查询的总成本应该是:

Task 总成本 = Attempt 1 成本 + Attempt 2 成本 = 1.7 元

但在分析时还要区分:

业务查询成本:1.7 元 失败重试成本:0.2 元 有效结果成本:1.5 元

否则无法判断成本高是因为查询本身复杂,还是因为路由错误导致重复执行。

对于共享集群,也不能把整个集群成本直接归给某个租户,而应该按资源使用量进行分摊:

租户成本 = 租户实际 CPU / 集群总 CPU × 集群成本 + 租户实际内存 / 集群总内存 × 集群成本

如果无法精确获得资源使用量,可以根据:

队列 Executor 执行时长 CPU 和内存配置

进行近似分摊,但要明确这是估算成本。

第五,建立成本归因链路

成本归因简单说就是:

一次查询消耗了多少 CPU、内存和存储资源,这笔成本最后算到哪个租户、哪个业务线、哪条 SQL 上。

最终要把成本信息和查询链路关联起来:

requestId -> taskId -> attemptId -> routeDecisionId -> engineConnId -> applicationId -> sqlFingerprint -> costRecord

成本记录可以包含:

tenantId businessLine dataset sqlFingerprint taskId attemptId engine cluster cpuCost memoryCost storageCost networkCost totalCost isRetry status

这样可以回答:

哪个租户成本最高? 哪个业务线增长最快? 哪类 SQL 最耗资源? 哪个引擎执行成本最低? 哪些查询经常因为错误路由产生重试?

第六,成本如何反哺路由

成本归因不是为了做报表,而是要反过来影响路由决策。

例如同一个 SQL 指纹:

Trino: 执行 10 秒 成本 0.8 元 SLA 命中率 95% Spark: 执行 60 秒 成本 2.5 元 SLA 命中率 98%

如果用户要求 10 秒返回,系统可以优先选择 Trino。

如果查询范围扩大后:

Trino: 成本 8 元 经常内存不足 Spark: 成本 3 元 执行稳定

这时虽然 Trino 是交互式引擎,但它已经不是更优选择,路由层应该切换到 Spark。

因此路由评分可以加入成本因素:

routeScore = 性能得分 + SLA 得分 + 稳定性得分 - 资源成本 - 排队成本

同时需要设置成本预算:

单次查询成本上限 租户每日成本上限 业务线月度预算 超预算后的降级策略

如何证明成本归因有效

我会通过以下指标验证:

成本归因完整率 成本归因准确率 租户成本可见率 单位查询成本 重复查询节省成本 失败重试成本 查询结果复用率 SLA 成本

例如:

结果复用率提高 重复扫描次数下降 单位查询成本下降 错误路由导致的重试成本下降

这些指标可以证明成本归因不仅能解释“钱花在哪里”,还推动了查询性能和引擎复用率的优化。

其他

成本归因的思路

成本归因的关键是通过任务 ID 把业务上下文和外部引擎的资源消耗关联起来。任务创建时记录租户、业务线、数据集和 SQL 指纹;每次执行生成独立的 Attempt;Linkis 提交 Spark 或 Trino 后,再把外部 applicationId 和 attemptId 关联起来。

外部引擎只提供某个 applicationId 消耗了多少 CPU、内存和存储,平台再通过 applicationId -> attemptId -> taskId 向上找到租户和业务线,最终生成 costRecord。如果一个任务因为路由错误执行了两次,就把两个 Attempt 的成本相加作为 Task 总成本,同时单独统计失败重试成本。

这样就能回答哪个租户、业务线、数据集和 SQL 指纹成本最高,也能识别错误路由造成了多少资源浪费。

返回列表