概述
在 UModel 系统中,实体(Entity)是 EntitySet 的具体实例,代表系统中的各种资源对象,如服务、主机、Pod 等。本文档详细介绍实体的数据格式、写入方法和最佳实践。
关于 UModel、EntitySet、Entity 的详细概念,请参见EntityStore 简介。
应用案例
当前应用案例相关素材,请参考:umodel.zip
实体数据格式
实体数据通过 SLS Log 协议写入到 EntityStore的${workspace}__entity
日志库中。每个实体记录包含系统字段和自定义字段两部分。
系统字段
字段名 | 类型 | 是否必须 | 示例 | 说明 |
| string | 是 | apm, k8s, acs | 实体所属的域,用于数据隔离和分类 |
| string | 是 | apm.service, k8s.pod | 实体类型,定义实体的分类 |
| string | 是 | 391274C9A1D369FB5222E273D02B81B8 | 实体唯一标识符,128位hex值 |
| string | 否(默认Update) | Create, Update, Expire, Delete, Revise | 操作类型,控制实体的生命周期 |
| int | 否 | 1700000000 | 首次观察时间,秒级时间戳 |
| int | 是 | 1700000001 | 最近观察时间,秒级时间戳 |
| int | 否(默认3600) | 3600 | 存活时间,秒为单位 |
字段说明
__entity_id__
生成规则
标准格式:128位十六进制字符串。
建议生成方法:对主键使用 MD5 或双重 xxhash 函数。
兜底处理(不建议):如不符合128位hex格式,内部自动进行 xxhash 转换。
时间字段管理
__first_observed_time__
:实体首次被发现的时间,一旦设置通常不变。__last_observed_time__
:实体最近一次被观察的时间,每次更新时更新。实体可见时间范围:
[FirstObservedTime, LastObservedTime + KeepAliveSeconds]
。说明:若不确定实体的首次被发现时间,可以不填
__first_observed_time__
此字段。EntityStore 在写入时会自动使用第一次接收的__last_observed_time__
的值作为__first_observed_time__
的值。
自定义字段
除系统字段外,可以添加 EntitySet 中定义的各类 Field 字段:
{
"__domain__": "apm",
"__entity_type__": "apm.service",
"__entity_id__": "391274C9A1D369FB5222E273D02B81B8",
"__method__": "Update",
"__last_observed_time__": 1700000001,
"__keep_alive_seconds__": 3600,
"service_name": "user-service",
"version": "v1.2.3",
"cluster": "production",
"labels": {
"env": "prod",
"team": "backend"
}
}
JSON 字段注意事项
⚠️ 重要:对于JSON格式的字段(如labels、annotations),必须确保JSON对象中的键按字母顺序序列化,避免频繁重建同一实体的索引。
实体时间区间与查询覆盖机制
时间区间基础概念
实体在 EntityStore 中具有明确的时间可见范围,查询引擎只有在查询时间窗口与实体可见时间产生交集时,才会返回该实体。
实体可见时间计算
实体的可见时间范围由以下公式确定:
注意:实体可见时间范围、和查询时间范围遵循左闭右开原则。
实体状态 | 可见时间范围 | 说明 |
正常状态 |
| 包含KeepAlive延长时间 |
已过期状态(显示调用Expire后) |
| 不包含KeepAlive延长时间 |
已删除状态 | 无可见时间 | 任何查询都不返回 |
时间交集判断规则
查询引擎使用以下规则判断实体是否应该被返回:
查询返回实体 = 查询时间窗口 ∩ 实体可见时间
具体判断逻辑:
if (QueryEndTime >= EntityFirstObservedTime &&
QueryStartTime <= EntityVisibleEndTime) {
返回实体
} else {
不返回实体
}
写入方法详解
Create - 创建实体
使用场景:明确创建新实体时使用,如监听到Pod创建事件。
行为逻辑:
若实体已存在:不做任何操作。
若实体不存在:创建新实体,覆盖所有字段。
示例:
{
"__domain__": "k8s",
"__entity_type__": "k8s.pod",
"__entity_id__": "abc123...",
"__method__": "Create",
"__first_observed_time__": 1700000000,
"__last_observed_time__": 1700000000,
"pod_name": "web-app-123",
"namespace": "default"
}
Update - 更新实体
使用场景:大部分场景使用,包括定期全量更新、增量更新等。
行为逻辑:
覆盖除
__first_observed_time__
外的所有字段。对于
__first_observed_time__
:若实体已存在:不修改原值。
若实体不存在且事件包含该字段:使用事件中的值。
若实体不存在且事件不包含该字段:使用
__last_observed_time__
的值。
示例:
{
"__domain__": "apm",
"__entity_type__": "apm.service",
"__entity_id__": "def456...",
"__method__": "Update",
"__last_observed_time__": 1700000100,
"service_name": "payment-service",
"status": "running",
"instance_count": 3
}
Expire - 实体过期
使用场景:实体明确被删除时使用,如监听到Pod删除事件。
行为逻辑:
标记实体的
__deleted__
为true。更新
__last_observed_time__
。查询时不再附加计算KeepAlive时间。
时间范围变化:
过期前:
[FirstObservedTime, LastObservedTime + KeepAliveSeconds]
。过期后:
[FirstObservedTime, LastObservedTime]
。
示例:
{
"__domain__": "k8s",
"__entity_type__": "k8s.pod",
"__entity_id__": "abc123...",
"__method__": "Expire",
"__last_observed_time__": 1700000200
}
Delete - 删除实体(谨慎使用)
使用场景:功能被禁用或需要实体在全周期都不可见的场景。
行为逻辑:
直接从底层数据库删除实体记录。
任何时间段的查询都无法找到该实体。
⚠️ 注意:这是不可逆操作,请谨慎使用。
Revise - 订正实体(谨慎使用)
使用场景:数据写错需要强制订正时使用,通常与Delete组合使用。
行为逻辑:
直接覆盖所有字段,包括
__first_observed_time__
。不考虑原有数据状。
写入最佳实践
推荐方式:增量事件 + 定期全量
核心思路是以增量事件为主,定期全量写入作为数据校对和容错机制。
配置建议:
增量事件:实时或近实时写入。
定期全量:频率建议1小时及以上。
KeepAlive时间:根据业务需求设置,通常10分钟到1小时。
说明:KeepAlive时间根据上报周期进行调整,建议不低于1个上报周期。此外定期上报周期建议不低于5分钟,避免频繁更新影响查询性能。
实际场景应用
场景1:云产品资源同步
使用RMC提供的增量+全量同步机制:
增量事件处理:
监听资源创建事件 -> 使用Create方法
监听资源更新事件 -> 使用Update方法
监听资源删除事件 -> 使用Delete方法
全量事件处理:
定期全量同步 -> 统一使用Update方法
KeepAlive 根据业务需求灵活配置
// 云资源创建事件
{
"__domain__": "acs",
"__entity_type__": "acs.ecs.instance",
"__entity_id__": "i-bp1234567890",
"__method__": "Create",
"__first_observed_time__": 1700000000,
"__last_observed_time__": 1700000000,
"__keep_alive_seconds__": 86400,
"instance_id": "i-bp1234567890",
"instance_name": "web-server-01",
"instance_type": "ecs.c6.large",
"status": "Running"
}
场景2:Agent采集资产数据
使用LoongCollector采集K8s资产数据的模式:
启动时全量采集:
进程启动时采集一次全量数据
后续每1小时采集一次全量数据
使用Update方法写入
增量事件订阅:
订阅K8s API事件
根据事件类型转换为对应的写入方法
实时写入增量变化
// K8s Pod全量采集
{
"__domain__": "k8s",
"__entity_type__": "k8s.pod",
"__entity_id__": "pod-web-app-123",
"__method__": "Update",
"__last_observed_time__": 1700000400,
"__keep_alive_seconds__": 3600,
"pod_name": "web-app-123",
"namespace": "production",
"node_name": "worker-01",
"pod_ip": "10.0.1.100",
"labels": {
"app": "web-app",
"version": "v1.0"
}
}
不推荐的写入方式
纯事件管理
问题:需要查询所有时间段数据,性能较差。
场景:仅在实体生命周期完全由事件驱动时考虑。
纯全量写入
问题:数据量大,实体发现延迟高。
建议:如无法识别增量,控制写入频率为10分钟或1小时。
写入接口
实体写入通过SLS Log协议进行,写入到对应workspace的${workspace}__entity
日志库即可。后台会自动同步到 EntityStore 存储引擎。
支持的写入方式:
ETL转换写入:通过数据加工任务转换和写入。
ScheduledSQL计算写入:通过定时SQL查询计算并写入。
API直接上报:通过SDK或API直接写入日志库。
注意事项
数据一致性:确保同一实体的
__domain__
、__entity_type__
、__entity_id__
组合在整个生命周期内保持一致。时间戳管理:所有时间字段使用秒级 Unix 时间戳,确保时区一致性。
方法选择:根据实际业务场景选择合适的 Method,避免不必要的Delete操作。
性能考虑:合理设置 KeepAlive 时间,平衡数据保留需求和查询性能。
JSON格式:确保 JSON 字段的键值按字母顺序序列化,提高索引效率。
实体数量:建议单个 EntitySet 的实体数量不超过1万,系统中整体实体数量不超过100万,避免影响查询性能和写入。