技术博客

阅读约 7 分钟

Palantir 带火了本体,APM 排障有了本体就够吗?

从 4 条 Trace 拆出 APM 本体(实体 + 关系 → metric 表),再用连接池说明「光有本体不够」——排障还要逻辑建模(入口 → 操作 → 远程三要素)。

这两年,"本体论"突然又火了。Palantir Foundry 不把自己当又一个数据仓库,而是给企业建一套本体(Ontology):把散落在各系统里的表、流水、传感器,统一抽象成"客户""订单""设备",再定义关系和动作,让数据变成能被程序拿去推理的世界模型。大模型一来,这套说法更吃香——模型再强,也得先知道世界里有什么。国防、金融、制造都在谈。

那落到我们这行:APM 的本体到底是什么?建了它,故障定位就能解决吗?分开看。

问题一如何构建 APM 的本体模型

APM 的本体就两步:从 Trace 提取实体和关系,再把它们 落地成指标表。用一个最小例子走一遍。

场景:服务 A 有两个实例 A-1、A-2;服务暴露两个接口;两个接口都调同一个数据库 DB、跑同一条 SQL1。上报 4 条 Trace:

#serviceinstanceendpoint调用sql
T1AA-1接口1DBSQL1
T2AA-1接口2DBSQL1
T3AA-2接口1DBSQL1
T4AA-2接口2DBSQL1

第一步,提取。先看 T1:实体是服务 A、实例 A-1、接口1、数据库 DB、SQL1;关系是 A 有实例 A-1,A 暴露接口1,A-1 接到接口1 的请求,接口1 调用 DB,接口1 执行 SQL1,DB 拥有语句 SQL1。P99、耗时、调用次数不是实体,是挂在实体上的观测值。

4 条合在一起,实体和关系就是:

  • Service hasInstance Instance:A → A-1、A-2
  • Service exposes Endpoint:A → 接口1、接口2
  • Instance serves request on Endpoint:A-1 / A-2 都会接到接口1、接口2 的请求
  • Endpoint calls DB:接口1 → DB;接口2 → DB
  • Endpoint executes SQL:接口1 → SQL1;接口2 → SQL1
  • DB hasStatement SQL:DB → SQL1

第二步,落地。提取完,还要把一部分实体写成可查询的指标表。以 DataBuff 的 Doris 为例(这层 schema 已开源:github.com/databufflabs/databuff):表的维度列就是实体,call_count / resp_time 是观测值:

维度(实体)观测值(不是实体)对应哪些实体
metric_serviceservicecall_count / resp_time / error_countService
metric_service_instanceservice + instance同上Service、Instance
metric_service_httpservice + instance + endpoint同上Service、Instance、Endpoint
metric_service_dbservice + instance + db + sqldb 调用数 / db 耗时Service、Instance、DB、SQL

上面 4 条 Trace 按这些维度一聚合,各表长这样:

  • metric_service:按服务 A 汇总 → 1 行,入口 4 次(T1–T4)
  • metric_service_instance:按实例拆 → 2 行,A-1 / A-2 各 2 次
  • metric_service_http:按实例 + 接口拆 → 4 行,A-1·接口1、A-1·接口2、A-2·接口1、A-2·接口2 各 1 次
  • metric_service_db:按实例 + 数据库 + SQL 拆 → 2 行,A-1·DB·SQL1、A-2·DB·SQL1 各 2 次

到这里,本体就建完了:Trace 里认出谁、谁连谁;再按这些实体落成指标表。

问题二只有本体,够吗?

实体和关系建全了,排障是不是就通了?先看连接池日常怎么转:

数据库连接池获取连接与归还连接的逻辑流程图
连接池:获取连接的请求分支、使用/归还,以及后台维护。实体关系图画不出这些分支与状态流转。

就算本体里补上 ConnectionPool,写成 Service → ConnectionPool → Database,也只标得出图里那几个大方块。中间的分支和状态,多加一个实体盖不住——这一层就是逻辑

连接池:实体关系画不出的分支
  1. 获取连接:有空闲 → 返回;能扩容 → 新建;池满 → 锁等待;再等 → 超时抛错
  2. 使用 / 归还:执行 SQL → 归还 → 唤醒等待者
  3. 后台维护:心跳检测,剔除失效或空闲过久的连接

只认实体、不管逻辑,最常见的坑是接口变慢:

只看本体带上逻辑
推理接口 → DB,接口慢 ⇒ DB 慢卡在锁等待,SQL 还没跑
现象接口耗时升DB 执行侧可能完全正常;超时更常表现为报错
结论根因在 DB根因是等连接,不在执行 SQL

第一性原理:程序 = 数据 + 逻辑。落到排障,两边分工不同:

本体(实体 + 关系)逻辑
管什么有什么、谁连谁请求怎么走、卡在哪
锁什么目标实体(接口1 / A-1 / DB)最后一公里根因(等连接 / 执行 SQL / 超时)
缺了会怎样不知道往哪下钻停在「谁有问题」,到不了原因

实体关系好抽象,容易画成拓扑、落成指标表;连接池这类分支多半还在一线专家经验里,公开讨论少。若光掌握本体就够当一线专家,专家早该遍地都是——差的正是这层逻辑。

连接池只是一个切片:它说明「光认实体不够」,但还没回答——整应用的逻辑该怎么系统建模?不妨先看 DataBuff 怎么把「一个接口的耗时」拆开,再由此扩到整应用。

问题三如何为逻辑建模

问题一里的「接口」,是入口的一种。DataBuff 对接口做逻辑建模时,不只给一条总响应时间,还按下游操作把耗时拆开——HTTP、RPC、DB、Redis、MQ,以及接口自身:

DataBuff 接口响应时间与耗时分解:按 HTTP、RPC、DB、Redis、MQ 等下游操作拆分
DataBuff:接口响应时间(上)+ 耗时分解(下)。总耗时不再是黑盒,能看到时间砸在哪类操作上。

读图即可:入口平均约 240ms;分解里 HTTP service-b 约 100ms、RPC service-b 约 80ms,其余落在 MySQL、ES、Redis、Kafka 等。这就是对一个入口的逻辑建模——盯住入口的耗时与状态,再盯住入口之后每种操作的耗时与状态。

把范围扩到真实应用:一堆容器,对外提供 RPC / HTTP 等服务,同时依赖别的应用,以及 DB、Redis、MQ。逻辑侧要全面掌控健康,其实还是这两件事:

  • 该应用所有入口的「耗时」和「状态」
  • 每个入口之后每种操作的「耗时」和「状态」

入口不止 HTTP。常见还有:

  • RPC 服务入口
  • HTTP 服务入口
  • MQ 消费消息入口
  • 定时 Job 入口
  • 其他入口

请求进了某个入口之后,后面大致会落到 5 种操作 + 1 种行为(通常还带机房维度)——上图里的分解项,就是这些操作在观测上的落地;问题一里的 DB / SQL,只是其中「DB 远程操作」这一行:

类型关键属性(维度)
DB 远程操作dal group / table / operation(select、update、insert…)/ sql
Redis 远程操作command
MQ 远程操作(写消息)exchange / routingKey / vhost
RPC 远程操作依赖方服务 / remote method
Local 操作(除上述四种远程外的本地操作)暂无额外属性
抛出异常(行为)异常 name

所以逻辑侧要落地统计的,就是:每个入口的耗时与状态,入口之后这 5 种操作的耗时与状态,以及异常次数——和问题一「按实体落成指标表」同构,只是维度从「谁」换成了「请求怎么走」。

对远程操作,光有总耗时还不够,最好再拆成三块:

远程操作耗时三要素
  1. 客户端:建立连接、发送请求、接收响应
  2. 网络:客户端到服务端的传输
  3. 服务端:对端真正执行

有这三块,才能判断远程到底卡在哪。问题二的连接池就落在这里:池满锁等待,时间记在客户端,SQL 可能还没到服务端——所以「等连接 ≠ SQL 慢」。现实里三要素不一定采得全,但模型得先立住;采不全时,至少把「入口耗时」和「远端执行耗时」拆开,别糊成一个数。

模型立住之后,故障来了就可以按同一条链路往下问:

由粗到细的故障结论
  1. 哪些入口受影响了?
  2. 受影响入口的本地操作是否异常?
  3. 受影响入口的哪些远程操作异常?具体到哪些操作属性(某张表、某条 SQL、某个 method…)?
  4. 客户端 → 服务端网络出问题,还是服务端出问题?
  5. 受影响入口都抛出了哪些异常

至此两边对齐了:本体回答「应用里有哪些实体、谁连谁」;逻辑模型回答「请求从哪个入口进来、后面经历了哪些操作、远程耗时卡在哪一段」。合在一起,才既找得到对象,也找得到原因。

本体锁实体,逻辑锁根因。逻辑不是再堆几个实体,而是按「入口 → 操作 →(远程)三要素 → 异常」把请求怎么走建模清楚,并统计每层的耗时与状态。AI 要分清「等连接」和「SQL 慢」,得先沿本体落到实体,再沿这套逻辑走到具体原因。