GreptimeDB 表语义层(Table Semantic Layer)RFC 全解析:用 `greptime.semantic.*` 让观测数据自带含义
2026/9/17 23:30:43 网站建设 项目流程

GreptimeDB 表语义层(Table Semantic Layer)RFC 全解析:用greptime.semantic.*让观测数据自带含义

【免费下载链接】greptimedbThe open-source observability database. One columnar engine for metrics, logs, and traces, on object storage.项目地址: https://gitcode.com/GitHub_Trending/gr/greptimedb

导读

本篇文章围绕 GreptimeDB 的 表语义层 RFC 展开,系统讲解如何在不引入新协议、不新增 DDL 关键字的前提下,把 OTLP、Prometheus Remote Write 等摄入路径在传输时携带、却在落库时被丢弃的元数据(instrument kind、temporality、unit、scope、semantic-conventions 版本等)以「表级注解」的形式保留下来,并通过information_schema.table_semantics视图对外暴露。读完本文,你将掌握语义层的完整词汇表与值域、冲突/更新语义、视图的列结构与 JSON 投影规则,并能在源码层面理解其从table_options到校验、落库、查询的完整链路,为编写面向 LLM Agent、告警生成器、仪表盘构建器、MCP Server 与 ETL 管线的消费方代码做好准备。

背景与动机:摄入时的元数据为什么会被丢掉

GreptimeDB 已经能摄入 OTLP metrics/traces/logs 与 Prometheus Remote Write,但每种协议在传输时携带的丰富元数据,在数据落表后大部分都丢失了:

  • 一张opentelemetry_traces表看起来和普通宽表没有区别,信号类型、数据来源、字段出处只能靠命名去猜;
  • v0.16+ 的 OTel→Prometheus 翻译路径会主动丢弃 scope 属性与大部分 resource 属性,而表本身从不记录「丢掉了什么」;
  • Prometheus Remote Write v1 的元数据按协议约定本就不可靠,但下游表不标记counter类型到底是「声明」的还是从_total后缀「推断」出来的;
  • OTel delta 与 Prometheus cumulative 混在同一张表时,仅凭 schema 无法恢复其 temporality。

RFC 明确指出,消费方远不止 LLM Agent:告警生成器需要在rate()与绝对值阈值之间做选择,需要单位来挑合理的边界;仪表盘构建器按信号类型选可视化;MCP Server 需要结构化工具目录而非自由文本描述;ETL 管道需要血缘信息来判定某个service_name列究竟是resource.service.name还是自由格式标签。这些元数据在摄入时本来就存在,只是没有被保存下来——表语义层要做的就是「把丢失的信息补记在表上」。

设计目标与非目标

目标

  1. 用现有 SQL 面(table_options、列COMMENT)为每张被摄入的表打上稳定身份——不引入新协议、不新增 DDL 关键字;
  2. 记录摄入路径执行的有损转换(丢弃的属性、scope 处理、类型推断 vs 声明);
  3. 暴露一个information_schema视图作为面向消费方的统一发现入口;
  4. 保持层可选、增量式——没有这些选项的表不受任何影响,照常工作。

非目标

  • 跨表关系建模(留给后续 RFC,即 实体关系与图查询 RFC);
  • 定制化存储(复用table_options与列COMMENT);
  • 查询期语义强制(该层是描述性的,不是强制性的);
  • 新线上协议(上游标准化只作为未来方向提及)。

三大机制:身份、补充、发现

语义层由三个机制组成,分别承担「表级身份与血缘」「列级补充」「消费方发现」三种职责:

  1. greptime.semantic.*表选项——承载在现有table_optionsblob 中。这正是今天承载table_data_model = 'greptime_trace_v1'otlp_metric_compat = 'prom'的同一个槽位,因此该机制是对 OTLP trace 自动建表路径已有做法的泛化。
  2. COMMENT——列级补充(如「本列是resource.service.name」「本列携带 delta 值」),是标准 SQL。
  3. information_schema.table_semantics视图——对选项的非规范化投影,通过现有with_extra_table_factories()钩子注册。一张表只要携带了greptime.semantic.*选项,或内置约定能从它推导出实体,就会出现在视图中。

源码印证:同一槽位的既有先例

在 src/table/src/requests.rs 中可以同时看到三个关键常量:

pub const TABLE_DATA_MODEL: &str = "table_data_model"; pub const TABLE_DATA_MODEL_TRACE_V1: &str = "greptime_trace_v1"; pub const OTLP_METRIC_COMPAT_KEY: &str = "otlp_metric_compat"; pub const OTLP_METRIC_COMPAT_PROM: &str = "prom";

它们与FILE_TABLE_*、metric engine 的PHYSICAL_TABLE_METADATA_KEY等一起构成VALID_TABLE_OPTION_KEYS。而语义键不走这个固定白名单,而是「保留前缀」通道:validate_table_option()中明确写着——语义层键共享一个保留前缀而非固定白名单,以便词汇表在无需改动这道闸门的情况下增长:

// Semantic-layer keys share a reserved prefix instead of a fixed allowlist so // the vocabulary can grow without touching this gate. See `semantic` module. if is_semantic_option_key(key) { return true; }

(见 src/table/src/requests.rs)

源码印证:注解的 ALTER 语义

同样的文件里还定义了AnnotationFamily(注解族)枚举,区分Semanticgreptime.semantic.*)与RepartitionHintrepartition.*)两类纯元数据标记。它们的共性是:没有 region 会消费这些值,SET/UNSET 只改写表的extra_options,因此 ALTER 完全跳过 region 分发。语义注解还额外满足两条规则(见 src/table/src/requests.rs):

  • allows_logical_tables()返回true——语义注解可以施加在 metric engine 的逻辑表上,因为物理侧没有任何东西消费它们;
  • 语义键必须与其它表选项分开 ALTER(mixed_batch_error()会提示「greptime.semantic.*options must be altered separately from other table options」)。

对应地,AlterKind中新增了SetAnnotations { family, options }UnsetAnnotations { family, keys }两个变体(见 src/table/src/requests.rs),并配套validate_annotation_keys()做批次分类与去重校验。

词汇表:小而克制的键集合

所有键都是greptime.semantic.前缀下的扁平字符串,值均为字符串。词汇表刻意做小:一个键只有当它记录了消费方无法从 schema、列集合或它已理解的指标命名约定中廉价可靠恢复的信息时,才有资格入选。那些值已按约定存在于指标名中(Prometheus_total/_bucket后缀)、对唯一生产者是常量、或只是复述已有列——例如 Prometheus Remote Write 表只带通用身份(类型/单位在名字里),resource 属性血缘不加盖(那是摄入/采集器配置问题,不是查询期语义)——都被有意省略,而不是为了完整性硬盖上去。

通用键(所有信号)

示例值说明
greptime.semantic.signal_typetrace/log/metric/event信号类型
greptime.semantic.sourceopentelemetry/prometheus/influxdb/opentsdb/elasticsearch/loki/custom摄入生态
greptime.semantic.source_versionPrometheus remote write1.0/2.0源协议版本
greptime.semantic.pipelinegreptime_trace_v1内部摄入管线/数据模型,是引擎相关table_data_model的「信号无关」继任者

Trace 键

  • greptime.semantic.trace.conventions:行所符合的 OTelschema_url;不是单值时用mixed/unknown

Metric 键

v1 假设每张表一种 metric 类型——这与 Prometheus Remote Write 和 post-v0.16 的 OTel 摄入路径当前落数据的方式一致;混合类型表留作后续工作。OTL 在线上声明这些值随后又丢弃,所以为其加盖;Prometheus 的类型/单位在名字里,只给身份。

示例值说明
greptime.semantic.metric.typecounter/gauge/histogram/summary/updown_counter/gauge_histogram/info/stateset仪器类型
greptime.semantic.metric.unitUCUM,如sBy{request}单位。行编码器会丢弃它,摄入后不可恢复
greptime.semantic.metric.temporalitycumulative/delta/mixed仅 OTel;catalog 级描述
greptime.semantic.metric.metadata_qualitydeclared(OTLP / exposition)或inferred(Prom RW v1,名称后缀猜测)元数据质量
greptime.semantic.metric.original_name翻译前的 OTel 名表名被「Prometheus 化」时,消费方用它到 OTel semantic conventions 中查指标

metadata_quality = inferred承载置信度感知工具的关键字段:一个 inferred 的 counter 在押注rate()语义之前应被重新检查。

有意省略的键(及原因)

  • metric.monotonic——是type的函数;
  • trace.has_events/has_links——v1 模型下为常量,且可由span_events/span_links列推导;
  • log.severity_scheme/log.body_format——前者是常量,后者可通过采样推导(且会花掉一次 O(rows) 扫描);
  • resource.attributes_preserved/attributes_dropped/scope.preserved——保留集是在复述列,丢弃标记是无内容的布尔,血缘是采集器配置问题。

源码印证:词汇表的权威实现

完整词汇表在 src/table/src/requests/semantic.rs 中以常量形式权威定义,例如:

pub const SEMANTIC_PREFIX: &str = "greptime.semantic."; pub const SEMANTIC_SIGNAL_TYPE: &str = "greptime.semantic.signal_type"; pub const SEMANTIC_SOURCE: &str = "greptime.semantic.source"; pub const SEMANTIC_SOURCE_VERSION: &str = "greptime.semantic.source_version"; pub const SEMANTIC_PIPELINE: &str = "greptime.semantic.pipeline"; pub const SEMANTIC_TRACE_CONVENTIONS: &str = "greptime.semantic.trace.conventions"; pub const SEMANTIC_METRIC_TYPE: &str = "greptime.semantic.metric.type"; pub const SEMANTIC_METRIC_UNIT: &str = "greptime.semantic.metric.unit"; pub const SEMANTIC_METRIC_TEMPORALITY: &str = "greptime.semantic.metric.temporality"; pub const SEMANTIC_METRIC_METADATA_QUALITY: &str = "greptime.semantic.metric.metadata_quality"; pub const SEMANTIC_METRIC_ORIGINAL_NAME: &str = "greptime.semantic.metric.original_name";

SEMANTIC_OPTION_KEYS是一个封闭白名单greptime.semantic.前缀下未被列出的键会被拒绝(见 src/table/src/requests/semantic.rs),所以greptime.semantic.unknown_key之类不会静默落进表的选项;词汇表增键意味着在此追加。源码注释同时确认了greptime.semantic.pipeline是「signal-agnostic successor to the engine-specifictable_data_modeloption」(见 src/table/src/requests/semantic.rs),与 RFC 完全一致。

值域校验由validate_semantic_option()承担(见 src/table/src/requests/semantic.rs):封闭域键接受固定集合外加unknown哨兵、以及允许混合的键上的mixed;开放值键(unit、original_name、pipeline、conventions)接受任意非空字符串。源码还有一组针对性极强的测试(见 src/table/src/requests/semantic.rs),例如:signal_type拒绝"spans"、接受"metric"metric.type拒绝"bogus"、接受"mixed";从词汇表剔除的greptime.semantic.metric.monotonicgreptime.semantic.resource.attributes_dropped无论值如何都校验失败;semantic.signal_type(缺前缀)、greptime.semanticx(近匹配)等都不被认可。

冲突与更新语义:两个必须先行定死的设计决策

RFC 特别强调两个约束其余一切的决策:

  • 冲突。某些表级键(从schema_url提升的trace.conventionsmetric.temporality等)无法在一张长期存活的表看到多来源数据时表达真相。v1 记录mixedunknown,而不是虚构一个单值。下游消费方必须把任何单值语义键视为 best-effort,而非强证据。
  • 更新。语义选项在建表时盖章。v1 不规定更新路径:把metadata_qualityinferred提升为declared、刷新resource.attributes_preserved、修订trace.conventions都推迟。若真实使用表明需要更新,将以独立 RFC 落地。

delta 行标签:otlp_aggregation_temporality

OTLP delta 求和与显式直方图还会在每条生成的行上存储查询可见的 String 标签otlp_aggregation_temporality="delta"。要点:

  • 该名字固定,不遵循default_column_prefix
  • 该标签是series 身份的一部分,对每条 series 的浮点rate()/increase()行为具有权威性;表选项从不用作行级判别器;
  • Native histogram 保留其原生算法;
  • Prometheus 元数据对 catalog temporality 为deltamixed的 counter、histogram、up/down-counter 表上报unknown
  • 同一请求内的冲突可产生mixed;后续写入不会更新既有表选项,因此 catalog 值可能过期,而行标签始终保持权威。

具体混合 temporality 工作负载:同一指标名从 cumulative 到 delta 的滚动生产变更。新旧 exporter 在 rollout 期间可能重叠,重试与迟到点会延长重叠窗口,而 fleet 收敛后保留的 cumulative 历史必须仍可查询。在表级别拒绝不同 temporality 会阻止就地迁移;把 delta 行路由到另一张表要么向用户暴露不同指标/表,要么需要仍然需要 per-series temporality 判别器的逻辑 union。因此 v1 保留单表,并把判别器存在 series 上。

该标记会出现在 PromQL 结果中并遵循普通标签匹配与分组。状态集为:absent/NULL(cumulative)与delta。Metric Engine 的精确算术与比较匹配复用现有__tsid键,混合 temporality 支持在该路径不增加投影匹配列;__tsid不可用且标记参与匹配时,planner 只对齐otlp_aggregation_temporality,在 schema 缺少它的输入上投影可空 String。使用ignoring(otlp_aggregation_temporality)可让 temporality 不参与匹配;在会丢弃标记的聚合或子查询之前应用rate()/increase();v1 中irate()resets()不感知 temporality;delta()idelta()changes()保留现有原始采样语义。

otlp_aggregation_temporality是 OTLP metric 属性的保留存储键:已含该精确 String 标签且值为delta的既有 float 表在升级后自动选择加入此行为;非 OTLP 写入方可有意选择加入,而带命名空间的 OTLP scope 属性不会与存储键冲突。OTLPNoRecordedValue求和存储规范的 Prometheus stale marker;classic-histogram tombstone 标记该点提供的 bounds、隐式+Inf、以及(若提供)_count_sum——若可选sum缺失则不发出_sumstale marker,因此此前存储的_sum采样在 lookback 到期前仍可能对 instant selector 可见。

源码印证:保留标签的常量与测试

OTLP metric 测试 src/servers/src/otlp/metrics/tests/delta.rs 直接断言了标签常量与「delta 求和 + 保留标签 + stale marker」的行为:

#[test] fn test_raw_delta_sum_identity_and_stale_marker() { set_default_prefix(Some("custom")).unwrap(); assert_eq!( OTLP_AGGREGATION_TEMPORALITY_LABEL, "otlp_aggregation_temporality" ); // ...构造含 NoRecordedValue 标志的 NumberDataPoint 并验证输出行 }

information_schema.table_semantics:消费方的第一句 SQL

消费方连接后要执行的第一句 SQL:

SELECT table_catalog, table_schema, table_name, signal_type, source, pipeline FROM information_schema.table_semantics;

返回每个带语义标签的表一行。视图暴露稳定的核心列集合:

类型说明
table_catalog/table_schema/table_nameString表标识
table_idUInt32表 ID
signal_typeString(可空)提升为列的信号无关键之一
sourceString(可空)同上
source_versionString(可空)同上
pipelineString(可空)同上
metadata_qualityString(可空)提升为列的键
semantic_optionsString(JSON,可空)其余greptime.semantic.*键原样保留(前缀剥离、排序)
entity_declarationsString(JSON,可空)表对实体图贡献的实体列表,无论由选项声明还是约定推导

未来键出现在semantic_options内,不会强制视图 schema 变更;只有广泛使用的键才会被提升为一级列。

源码印证:视图的完整实现

视图实现在 src/catalog/src/system_schema/information_schema/table_semantics.rs。其模块注释完整复述了设计(「One row per table that is part of it, so a consumer can discover the observability concept a table stands for with a single SQL query instead of parsing every table'screate_options」),并解释了entity_declarations的语义:它展示结果而非推理——因命名了缺失列而被丢弃的声明只是缺席,只有日志会说明原因。

SemanticRow::extract()把表的extra_options投影到语义 schema:5 个键被提升为独立列,其余键以剥离greptime.semantic.前缀后的短名进入BTreeMap(保证 JSON 键排序、输出稳定),见 src/catalog/src/system_schema/information_schema/table_semantics.rs。单元测试给出了可读性极强的投影示例(见同文件测试模块):

// extract_promotes_core_keys_and_folds_the_rest assert_eq!(row.signal_type, Some("metric")); assert_eq!(row.source, Some("opentelemetry")); assert_eq!(row.source_version, Some("2.0")); assert_eq!(row.pipeline, Some("greptime_metric_v1")); assert_eq!(row.metadata_quality, Some("declared")); assert_eq!(row.options_json.as_deref(), Some(r#"{"metric.type":"counter","metric.unit":"By"}"#));
  • 未带任何语义选项的表返回Noneextract_skips_untagged_table);
  • 只有核心键时options_jsonNoneextract_omits_json_when_only_core_keys_present);
  • 实体键不提升为列,原样进入 JSON tail(extract_folds_entity_keys_into_json_tail)。

add_table()中还体现了一条 RFC 核心语义:if !carries_options && declarations_json.is_none() { return; }——视图的行集 = 携带语义选项的表 ∪ 约定推导出实体的表,后者即使没有任何语义选项也会得到一行(见 src/catalog/src/system_schema/information_schema/table_semantics.rs)。

源码印证:通过with_extra_table_factories()注册

视图通过既有扩展钩子接入 information_schema provider。在 src/catalog/src/kvbackend/builder.rs 中,KvBackendCatalogManagerBuilder::build()构造InformationSchemaProvider后立即调用.with_extra_table_factories(extra_information_table_factories.clone())注册附加信息表工厂;src/catalog/src/kvbackend/manager.rs 的运行时路径同样走这个钩子。这正是 RFC 中「registered through the existingwith_extra_table_factories()hook」的落地证据。

实现计划:四个可独立交付的阶段

  1. 身份(Identity):在每条自动建表路径上盖章signal_typesource。OTLP 路径已有天然注入点;Prometheus Remote Write 是唯一不平凡的路径——因为 metric engine 逻辑表共享物理存储(见开放问题 2)。
  2. Metric 细节(Metric specifics):在 OTel metric 与 Prom RW 摄入点添加 type / unit / temporality / monotonic / metadata_quality / original_name——数据在 OTel 翻译器内已经手到擒来。
  3. Resource / scope 血缘(Resource / scope lineage):记录 OTel→Prometheus 翻译保留与丢弃了什么。
  4. information_schema.table_semantics视图 + 文档:作为稳定用户面契约。

源码印证:实体键的自动盖章与推导

实现已部分落地。OTLP trace 表会自动盖章greptime.semantic.entity.service.id(值为service_name标签列),其常量在 src/table/src/requests/semantic.rs 定义为「The well-known entity-identity key auto-stamped on OTLP trace tables」,测试中还专门断言它是良构实体键(drift guard)。而实体图的读取端在 src/frontend/src/instance/entity_graph.rs —— 其模块注释表明:实体图实体表以引擎原生table_data_model选项为键,同时识别更新的greptime.semantic.*印章(见 src/frontend/src/instance/entity_graph.rs),这正对应 RFC 中「tables are keyed off the engine-nativetable_data_modeloption (same slot)」的表述,也是「选项声明 vs 约定推导」两条实体来源之一。实体子命名空间(greptime.semantic.entity.<type>.{id|descriptive|scope})的完整规则在 实体关系与图查询 RFC 中定义,语义层源码用「前缀 + 形状」而非成员表来校验开放式的实体类型(servicehostk8s.podprocessagent等)。

与 OpenTelemetry 标准化的关系

OTel 目前标准化的是生产者发出什么、以及数据采集器如何被管理;读侧——后端向客户端暴露什么——是厂商的自留地。OTLP 是单向的;OpAMP 是 Agent 管理;OTEP-0243(App Telemetry Schema)是生产者侧;schema_url是生产者声明的、没有反向通道。邻近先例——Prometheus/api/v1/metadata、Loki labels API、Tempo tags、Jaeger services、临时拼装的 MCP Server——全是厂商特定方案。

RFC 认为这是真实的空白。本地提议的形态(信号无关、schema_url感知、围绕小词汇表结构化)刻意贴近未来上游「backend-catalog read API」OTEP 可能的样子,Weaver 的Resolved Telemetry Schema是自然的数据模型。RFC 不承诺主导此类 OTEP,但承诺保持本地形态足够接近,使未来上游提案不会造成破坏性迁移。

备选方案回顾:为什么不用更「干净」的做法

备选方案否决理由
新 DDL 语法(如SEMANTIC trace WITH (...)看起来更干净但不标准,迫使每个客户端学习它;元数据不足以值得一个新关键字
专用_semantic系统表为每表静态 KV 翻倍存储路径,还引入生命周期问题(drop、backfill);对table_options建视图覆盖相同访问模式
只用列注释发现(WHERE signal_type = 'trace')变成全文搜索问题;注释适合列级补充,不适合身份
把一切编码进表名这正是今天的做法;每个新字段都会变成一个新的命名约定

开放问题与未来工作

开放问题

  1. 命名空间前缀greptime.semantic.*vs 裸semantic.*。v1 选择厂商前缀;若社区标准后来出现,再别名或迁移。
  2. Prom RW 注入点:metric engine 逻辑表共享物理存储,per-logical-table 选项需要一个不像 OTLP trace 分支那样干净的钩子。Phase 1 落地前需要一次短期 spike。
  3. 混合类型 metric 表:当出现把多种 metric 类型打进一张表的摄入模式时,metric.type从表级迁移到行级。v1 留下metric.type = 'mixed'标记并推迟。
  4. 稳定性面:顶层键(signal_typesource)稳定;子命名空间(metric.*等)在该层 v1.0 宣布前持续演进。

未来工作

  • 跨表关系:成对的 trace/services 表、metric/info 配对、JOIN 提示——独立 RFC;
  • 生产者 SDK/客户端身份:可选的greptime.semantic.source.sdk键记录发出客户端(如opentelemetry-goopentelemetry-javaopentelemetry-collector);共享 trace 表是常见情形,多 SDK 生产者坍缩为mixed,遵循与表级键相同的冲突规则;
  • 回填:为此功能发布前创建的表回填;
  • 上游提案:把该形态带入社区提案——很可能是 OTLP-Catalog 读 API 的 OTEP 加 MCP 绑定——以 Greptime 本地使用数据为依据。

阅读路径与延伸

  • RFC 原文:docs/rfcs/2026-05-28-table-semantic-layer.md
  • 词汇表与校验权威实现:src/table/src/requests/semantic.rs
  • 表选项槽位、AnnotationFamily与 ALTER 注解语义:src/table/src/requests.rs
  • information_schema.table_semantics视图实现:src/catalog/src/system_schema/information_schema/table_semantics.rs
  • 视图注册钩子:src/catalog/src/kvbackend/builder.rs、src/catalog/src/kvbackend/manager.rs
  • delta 求和保留标签测试:src/servers/src/otlp/metrics/tests/delta.rs
  • 实体图读取端(table_data_modelgreptime.semantic.*共同作为实体来源):src/frontend/src/instance/entity_graph.rs
  • 实体子命名空间的完整设计:docs/rfcs/2026-06-25-entity-relationships-and-graph-query.md

【免费下载链接】greptimedbThe open-source observability database. One columnar engine for metrics, logs, and traces, on object storage.项目地址: https://gitcode.com/GitHub_Trending/gr/greptimedb

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询