跳到主要内容
版本:Nightly

声明实体与关系

注意

实体声明目前处于实验阶段。选项 key、内置约定和声明边表在未来版本中可能变化。

一张表通过声明"它的行描述了哪些实体、哪些列标识每个实体"加入语义图。常见情况由内置约定覆盖,其余的用 greptime.semantic.entity.* 表选项声明。

内置约定

约定随二进制发布,不可配置。某个实体类型上的显式声明总是覆盖该类型的约定声明,包括显式声明本身不可用的情况:写错的声明不会悄悄退回到另一套身份。

OTLP trace 表

任何 table_data_model = greptime_trace_v1 的表,无需任何选项,就会从展开的 resource attributes 得到以下声明:

实体标识列描述列
serviceservice_name
service.instanceservice_nameresource_attributes.service.instance.id
hostresource_attributes.host.idresource_attributes.host.name
k8s.podresource_attributes.k8s.pod.uidresource_attributes.k8s.pod.nameresource_attributes.k8s.namespace.name
k8s.noderesource_attributes.k8s.node.name
k8s.containerresource_attributes.k8s.pod.uidresource_attributes.k8s.container.nameresource_attributes.container.idresource_attributes.container.name
containerresource_attributes.container.idresource_attributes.container.name

只有当一行上某个声明的全部标识列都存在且非空时,该声明才在这一行生效。在携带完整 k8s.container 身份的行上,通用的 container 声明不再生效,因此 pod 里的容器是一个节点,而不是两个。

OTLP trace 接入路径在建表时还会写入 greptime.semantic.entity.service.id = service_name。这个显式选项优先于约定的 service 声明,因此从 trace 派生的 service id 就是不带前缀的服务名。

service.instance 没有被写入显式选项,因此适用约定:resource_attributes.service.namespace 存在时,id 渲染为 <namespace>/<service_name>,<instance.id>。其中的首个分量与 Prometheus 的 job 标签一致——OpenTelemetry 兼容性规范把 job 定义为 <service.namespace>/<service.name>

两条规则在 service 这一层并不一致。当 service.namespaceshopservice.nameapi 时,同一个服务会以两个节点进入图:

来源entity_typeentity_id
OTLP trace 表serviceapi
target_infogreptime_otel_resource_infoserviceshop/api
OTLP trace 表service.instanceshop/api,<instance.id>

只有 service.namespace 为空、job 就等于服务名本身时,两者才会重合。在这个差异存在期间要合并它们,需要在其中一侧用携带对方取值的列显式声明身份。

Prometheus 描述性指标

由 remote write 路径打上 signal_type = metricsource = prometheus 的表,如果表名命中白名单内的 kube-state-metrics 或 target_info 描述性指标,且标识列存在,就会得到隐式声明:

实体(标识列)
kube_pod_infok8s.poduid)、k8s.nodenode
kube_node_infok8s.nodenode
kube_pod_ownerk8s.poduid)、k8s.workloadnamespaceowner_kindowner_name
kube_pod_container_infok8s.poduid)、k8s.containeruidcontainer
kube_pod_init_container_infok8s.poduid)、k8s.containeruidcontainer
kube_service_infok8s.serviceuid
target_infoservicejob)、service.instancejobinstance

这些表同时贡献描述属性——pod 名和 namespace、节点内核版本、容器镜像——并按表上实际存在的列过滤,因为 kube-state-metrics 的标签集随版本变化。target_info 还会把剩余的全部 tag 列快照到 service.instance 实体上。

普通指标表不会被扫描出实体:带 jobinstance 标签的指标本身不贡献实体,这些 service 由 target_info 引入。

OTLP 资源描述表

GreptimeDB 可以从写入的 OTLP metrics 的 resource attributes 中合成一张 greptime_otel_resource_info 表,让只有指标的 service 也能进入图。该功能默认关闭;开启后会创建并写入一张客户端没有发送的表。

[otlp]
experimental_enable_resource_info = true

开启后,该表声明 servicejob)、service.instancejobinstance)、hosthost.id)、k8s.podk8s.pod.uid)、k8s.nodek8s.node.name)、k8s.containerk8s.pod.uidk8s.container.name)和 containercontainer.id),取代规则与 trace 侧相同。

在自己的表上声明实体

每种实体类型有三个选项 key:

greptime.semantic.entity.<entity_type>.id          = 逗号分隔的列名
greptime.semantic.entity.<entity_type>.descriptive = 逗号分隔的列名 (可选)
greptime.semantic.entity.<entity_type>.scope = 逗号分隔的列名 (可选)

<entity_type> 是一个或多个用点分隔的 [a-z0-9_] 片段,例如 servicek8s.podgen_ai.agent,也可以是自定义类型。与语义词汇表的其余部分不同,实体类型是开放的。

DDL 阶段强制四条规则:

  • 列必须在表上存在。
  • 列必须能渲染成字符串。BinaryJsonVectorListStructDictionary 类型会被拒绝;把被引用的列 ALTER TABLE ... MODIFY COLUMN 改成这些类型同样会被拒绝。
  • 列可以是 tag,也可以是 field。
  • id 列的顺序是身份的一部分。entity_id 是这些列的值按该顺序连接的结果,因此声明同一实体类型的各张表必须以相同顺序(从宽到窄)列出它们。
CREATE TABLE app_request_latency (
ts TIMESTAMP(3) TIME INDEX,
service_name STRING,
instance STRING,
host STRING,
env STRING,
latency DOUBLE,
PRIMARY KEY (service_name, instance, host, env)
) WITH (
'greptime.semantic.signal_type' = 'metric',
'greptime.semantic.entity.service.id' = 'service_name',
'greptime.semantic.entity.service.scope' = 'env',
'greptime.semantic.entity.service.instance.id' = 'service_name,instance',
'greptime.semantic.entity.host.id' = 'host'
);

service.instanceservice_name 排在 instance 之前,与该类型的内置身份保持一致。只用实例名不唯一:两个服务都把实例命名为 0 时会合并成一个节点。

标识列为 NULL 或空字符串的行不标识任何实体,会被跳过。

scope 不属于身份,只是把命名空间或环境值作为过滤和展示列暴露出来。真正用于区分两个实体的命名空间应该放进 id

表可以在创建之后加入或退出图:

ALTER TABLE app_request_latency SET 'greptime.semantic.entity.process.id' = 'service_name,host';

ALTER TABLE app_request_latency UNSET 'greptime.semantic.entity.process.id';

实体在下一次查询图时出现,不回填,也不重写数据。

由同一行实体身份派生的关系

一行同时携带两个实体的身份时,系统会根据内置规则派生二者之间的关系。产生边的实体组合及其方向如下:

源端目标端rel_type
service.instancehostruns_on
service.instancek8s.podruns_on
service.instancecontainerruns_on
service.instancek8s.containerruns_on
service.instanceservicepart_of
processhostruns_on
containerhostruns_on
k8s.containerhostruns_on
k8s.podk8s.noderuns_on
k8s.podk8s.containercontains
k8s.podk8s.workloadpart_of

另有两条只作用于 trace 表:

源端目标端rel_type
gen_ai.agentgen_ai.modeluses
gen_ai.agentgen_ai.toolinvokes

这样派生出的边 provenanceattribute,agent 边除外——它们是 span 结构的观测,provenancetrace。仅仅共享一个列值不会派生出任何边:组合必须在上述词汇内,且两个身份必须声明在同一张表上。

以上两张表就是完整的规则集。要关联没有任何一张表共同声明的实体,需要人工声明边。

人工声明边

greptime_private.semantic_relationships_declared 存放你自己断言的边。它的行会 union 进 semantic_relationships,与派生边一起出现,provenancedeclared

这张表的定义由 GreptimeDB 管理:首次 INSERT 时按规范 schema 创建,CREATEALTER 以及把别的表改名成这个名字都会被拒绝;INSERTDELETEDROP 允许,下一次写入会重新创建这张表。

INSERT INTO greptime_private.semantic_relationships_declared
(observed_at, src_type, src_id, rel_type, dst_type, dst_id, provenance, scope, generation_id)
VALUES
(now(), 'service', 'frontend', 'depends_on', 'service', 'users-db', 'declared', '', '');
说明
observed_at时间索引,即声明时间。
src_typesrc_idrel_typedst_typedst_idprovenancescopegeneration_idtag 列,构成主键。主键要求全部提供,不使用 scopegeneration_id 时传 ''
valid_fromvalid_until业务有效期。valid_fromNULL 表示自声明起有效;valid_untilNULL 表示只要行存在就有效。
confidencerequest_counterror_countduration_sumduration_count可选,声明边通常留空。
attributes可选的 JSON。

自己断言的边把 provenance 设为 declared,LLM 推断出的边设为 agent。因为 provenance 是边身份的一部分,推断出的边始终与观测到的结构可区分,也无法覆盖后者。

这条边在下一次查询时出现在 semantic_relationships 中,没有填写的列为 NULL

用相同的边主键再次插入会存入一个新版本,读取时保留截至查询窗口上界的最新版本。要下线一条边,把 valid_until 设为过去的时间,或者删除该行:

DELETE FROM greptime_private.semantic_relationships_declared
WHERE src_id = 'frontend' AND dst_id = 'users-db' AND rel_type = 'depends_on';

这张表的 TTL 是 90 天,一直不再断言的边最终随行过期。

查看声明

information_schema.table_semanticsentity_declarations 列报告一张表贡献的全部声明,显式声明和约定声明都在内:

SELECT table_name, entity_declarations
FROM information_schema.table_semantics
WHERE entity_declarations IS NOT NULL;

JSON 数组的每个元素描述一条声明:

字段说明
entity_type声明的类型。
origindeclared 表示来自表选项,convention 表示来自内置规则。
id标识列,按声明顺序排列。
id_qualifier约定使用的、限定第一个 id 分量的列。
superseded_by更具体类型的标识列;行上带齐这些列时由该类型接管。
descriptivescope承担这两种角色的列。

用它确认一张表实际贡献了什么,以及显式声明是否替换掉了约定声明。