【Atlas】Atlas 中的 Relationship(关系)是如何建模的?
Apache Atlas Relationship 建模机制深度解析:血缘与依赖关系的图谱基石
用户问题原文:
18. Atlas 中的 Relationship(关系)是如何建模的?
本文将围绕上述问题,系统性剖析Apache Atlas 2.4.0中Relationship(关系)的建模原理、存储机制、API 使用与生产落地路径。我们将从电商用户行为宽表治理的真实场景切入,深入源码层级解释 Relationship 如何作为元数据图谱的“神经突触”,支撑起跨引擎(Hive → Spark → ClickHouse)的端到端血缘追踪、影响分析与合规审计能力。全文基于Atlas 2.4.0 + Hadoop 3.3 + Hive 3.1 + Spark 3.3 + OpenJDK 11 + Ubuntu 20.04环境验证。
一、问题引入:为什么无法追溯“用户画像标签”来源?
某电商平台的数据团队构建了一张名为user_behavior_ck_table的 ClickHouse 宽表,用于实时推荐。该表由 Spark 作业从 Hive 表ods_user_event加工而来。然而,当安全团队要求审计“是否包含未成年人行为数据”时,数据地图无法展示该宽表的上游血缘——点击“查看血缘”仅显示孤立节点,无任何输入/输出连接。
经排查发现,根本原因在于:Spark Hook 上报的 Process Entity 未正确建立与输入表(ods_user_event)和输出表(user_behavior_ck_table)之间的 Relationship,导致 Atlas 图数据库中缺失关键边(Edge)。
这一故障暴露了 Relationship 在元数据治理中的核心地位——它不仅是血缘的“连线”,更是影响分析、变更传播、权限联动的“逻辑通道”。
💡生活化类比:
Relationship 就像城市交通路网中的“道路连接”——每个路口(Entity)本身有价值,但只有通过道路(Relationship)连接,才能形成可导航的路径(血缘)。如果地图只记录路口坐标而不记录道路,就无法规划从 A 到 B 的路线。
技术本质差异:道路是物理存在,而 Atlas Relationship 是逻辑声明,需显式建模并持久化到图数据库。
二、Relationship 官方定义与设计动机
2.1 官方定义(源自 Apache Atlas GitHub 源码)
在 Apache Atlas 官方文档 中,Relationship 被定义为:
“Relationships in Atlas represent the connections between entities. They are first-class citizens in the type system and are used to model complex metadata topologies such as lineage, ownership, and containment.”
更精确地说,Relationship 是一种独立于 Entity 的 Type 定义,用于描述两个或多个 Entity 之间的语义连接。它由以下核心要素构成:
- RelationshipType:关系类型的声明(如
hive_table_columns,dataset_process_dataset) - EndDef:定义关系两端的实体类型、角色名与容器属性(
isContainer) - Cardinality:支持
SINGLE,LIST,SET等多重性
所有 Relationship 均通过REST API/api/atlas/v2/types/relationshipdef管理,并作为JanusGraph 图数据库中的边(Edge)存储。
2.2 设计动机:为何不直接用 Entity 属性?
早期版本(< 1.0)曾尝试将关系作为 Entity 的属性(如hive_table.columns = [col1, col2]),但面临三大问题:
- 双向查询困难:无法高效查询“某列属于哪些表”
- 事务一致性差:更新表结构需同时修改表和列,易出现中间态
- 图遍历性能低:嵌套属性无法利用图数据库的邻接索引
因此,Atlas 2.0+ 引入独立 Relationship 模型,将关系提升为一等公民,实现:
- 双向导航:
table → columns与column → table均 O(1) - 原子操作:增删列只需创建/删除 Relationship,无需修改表 Entity
- 图优化:JanusGraph 可对 Relationship 建立边索引(Edge Index)
🔍源码证据:
在org.apache.atlas.model.instance.AtlasRelationship中,Relationship 被定义为独立对象:publicclassAtlasRelationshipextendsAtlasStruct{privateStringguid;// 关系唯一IDprivateStringtypeName;// RelationshipType 名称privateAtlasObjectIdend1;// 一端实体引用privateAtlasObjectIdend2;// 另一端实体引用privatebooleanisCreatedByRelationship;// 是否由关系自动创建}
三、Relationship 核心模型与类型体系
3.1 RelationshipType 结构详解
一个完整的 RelationshipType 定义如下(以hive_table_columns为例):
{"name":"hive_table_columns","typeVersion":"1.0","endDef1":{"type":"hive_table","name":"columns","isContainer":true,"cardinality":"SET","isLegacyAttribute":false},"endDef2":{"type":"hive_column","name":"table","isContainer":false,"cardinality":"SINGLE","isLegacyAttribute":false}}关键字段解释:
| 字段 | 说明 | 生产影响 |
|---|---|---|
endDef1.type/endDef2.type | 关系两端的 Entity Type | 必须已注册,否则创建失败 |
isContainer | 是否为“容器-成员”关系 | 决定删除行为(见 3.3 节) |
cardinality | 多重性(SINGLE/LIST/SET) | 影响 API 调用方式 |
name | 关系在 Entity 中的属性名 | UI/API 通过此名导航 |
3.2 三大 Relationship 类型
Atlas 2.4.0 内置三类核心 Relationship:
| 类型 | 示例 | 用途 | 是否容器关系 |
|---|---|---|---|
| Containment(包含) | hive_table —[columns]→ hive_column | 描述“整体-部分”结构 | 是(isContainer=true) |
| Association(关联) | spark_process —[inputs]→ hive_table | 描述血缘、依赖 | 否 |
| Reference(引用) | kafka_topic —[schema]→ avro_schema | 描述外部引用 | 否 |
⚠️关键区别:
Containment 关系具有级联删除语义——删除hive_table会自动删除其所有hive_column(若atlas.graph.delete.container.entities=true,默认开启)。
3.3 容器关系(isContainer)的级联行为
当isContainer=true时,Atlas 会自动处理级联操作:
- 创建:先创建容器 Entity(如表),再创建成员 Entity(如列),最后建立 Relationship
- 删除:删除容器 Entity 时,自动删除所有成员 Entity 及其 Relationship
- 更新:替换成员列表时,自动计算增量(新增/删除 Relationship)
源码路径:org.apache.atlas.repository.store.graph.v2.EntityGraphMapperV2#mapRelationshipAttributes
// 简化逻辑:处理容器关系的级联删除if(relationshipDef.isContainer()){for(AtlasEntitymember:containerEntity.getRelatedEntities()){deleteEntity(member.getGuid());// 递归删除成员}}💡生活化类比:
isContainer=true就像“文件夹与文件”的关系——删除文件夹时,操作系统自动删除其中所有文件。
技术本质差异:文件系统删除是物理操作,而 Atlas 删除是逻辑标记(软删除),可通过hardDelete参数控制。
四、Relationship 在血缘建模中的实战应用
4.1 血缘三元组:inputs / outputs / process
Atlas 使用Process Entity + 两类 Association Relationship构建血缘:
process.inputs:指向输入数据集(如 Hive 表)process.outputs:指向输出数据集(如 ClickHouse 表)
其 RelationshipType 定义如下(源自0010-DataSetTypes.json):
{"name":"dataset_process_dataset","endDef1":{"type":"Process","name":"inputs","isContainer":false,"cardinality":"SET"},"endDef2":{"type":"DataSet","name":"outputs","isContainer":false,"cardinality":"SET"}}🔍注意:该 Relationship 是无向的,但通过
endDef1.name="inputs"和endDef2.name="outputs"赋予方向语义。
4.2 电商宽表血缘建模范例
我们以Spark 作业加工ods_user_event→user_behavior_ck_table为例:
步骤 1:定义 Process Entity
{"entity":{"typeName":"spark_process","attributes":{"qualifiedName":"spark_job_user_behavior@prod-cluster","name":"UserBehaviorAggregation","owner":"data-team","startTime":1713900000000,"endTime":1713900600000}}}步骤 2:建立 inputs Relationship
{"relationship":{"typeName":"dataset_process_dataset","end1":{"guid":"{{spark_process_guid}}","typeName":"spark_process"},"end2":{"guid":"{{ods_user_event_guid}}","typeName":"hive_table"}}}步骤 3:建立 outputs Relationship
⚠️重要:由于
dataset_process_dataset是无向关系,必须通过 API 指定哪一端是 input/output。Atlas 通过end1对应inputs,end2对应outputs实现。
# 创建 inputs 关系curl-uadmin:admin-XPOST\-H"Content-Type: application/json"\-d'{ "relationship": { "typeName": "dataset_process_dataset", "end1": { "guid": "PROCESS_GUID" }, "end2": { "guid": "INPUT_TABLE_GUID" } } }'\http://localhost:21000/api/atlas/v2/relationship# 创建 outputs 关系(交换 end1/end2)curl-uadmin:admin-XPOST\-H"Content-Type: application/json"\-d'{ "relationship": { "typeName": "dataset_process_dataset", "end1": { "guid": "OUTPUT_TABLE_GUID" }, "end2": { "guid": "PROCESS_GUID" } } }'\http://localhost:21000/api/atlas/v2/relationship✅验证点:
查询血缘:curl-uadmin:admin\"http://localhost:21000/api/atlas/v2/lineage/hive_table/inputs?depth=3&guid=OUTPUT_TABLE_GUID"应返回
ods_user_event表信息。
4.3 Mermaid 血缘关系图
五、Relationship 存储与查询机制
5.1 JanusGraph 存储格式
Relationship 在 JanusGraph 中以边(Edge)形式存储,其属性包括:
| JanusGraph Edge 属性 | Atlas 字段 | 说明 |
|---|---|---|
~label | RelationshipType 名称 | 如hive_table_columns |
end1_guid | end1.guid | 一端实体 GUID |
end2_guid | end2.guid | 另一端实体 GUID |
end1_type | end1.typeName | 一端类型 |
end2_type | end2.typeName | 另一端类型 |
🔍HBase RowKey 设计:
JanusGraph 默认使用shard + vertexId + edgeId作为 RowKey,确保 Relationship 查询局部性。
5.2 高效查询:避免 N+1 问题
错误做法(N+1 查询):
// 先查表AtlasEntitytable=client.getEntityByAttribute("hive_table","qualifiedName","default.user_table");// 再循环查每列for(StringcolGuid:table.getRelationshipAttribute("columns")){AtlasEntitycol=client.getEntityByGuid(colGuid);// 每次 RPC 调用!}正确做法(批量查询):
// 使用 AtlasClientV2 的批量接口List<String>colGuids=(List<String>)table.getAttribute("columns");List<AtlasEntity>columns=client.getEntitiesByGuids(colGuids);// 单次 RPC⚠️性能陷阱:
在 Web UI 中,若表有 1000 列,默认会发起 1001 次请求(1 次查表 + 1000 次查列)。生产环境需启用atlas.graph.storage.batch-loading=true优化。
六、自定义 Relationship 开发实战
6.1 场景:IoT 设备指标与 Hudi 表关联
假设我们有IoT 设备iot_device_001,其指标写入Hudi 表iot_device_metrics_hudi。需建立device_produces_tableRelationship。
步骤 1:定义 RelationshipType
{"relationshipDefs":[{"name":"device_produces_table","typeVersion":"1.0","endDef1":{"type":"iot_device","name":"producedTables","isContainer":false,"cardinality":"SET"},"endDef2":{"type":"hudi_table","name":"producingDevice","isContainer":false,"cardinality":"SINGLE"}}]}步骤 2:注册 Type
curl-uadmin:admin-XPOST\-H"Content-Type: application/json"\-d@iot_relationship.json\http://localhost:21000/api/atlas/v2/types/typedefs步骤 3:创建 Relationship
// Java Client 示例AtlasRelationshiprelationship=newAtlasRelationship();relationship.setTypeName("device_produces_table");relationship.setEnd1(newAtlasObjectId(deviceGuid,"iot_device"));relationship.setEnd2(newAtlasObjectId(hudiTableGuid,"hudi_table"));AtlasClientV2client=newAtlasClientV2(...);client.createRelationship(relationship);✅验证点:
查询设备关联的表:curl-uadmin:admin\"http://localhost:21000/api/atlas/v2/entity/guid/${deviceGuid}?excludeRelationshipAttributes=false"响应中
producedTables字段应包含 Hudi 表 GUID。
七、Relationship 生产调优与排障指南
7.1 常见故障与根因分析
| 故障现象 | 根因 | 诊断命令 |
|---|---|---|
| 血缘图显示“孤立节点” | Relationship 未创建或类型错误 | GET /api/atlas/v2/relationship/guid/{relGuid} |
| 删除表后列仍存在 | isContainer=true但配置atlas.graph.delete.container.entities=false | 检查application.properties |
| Relationship 创建慢 | JanusGraph 边索引未创建 | 查看janusgraph.log中Index not found |
| 双向导航失效 | RelationshipType 未正确定义endDef.name | GET /api/atlas/v2/types/relationshipdef/name/device_produces_table |
7.2 性能调优参数
在application.properties中调整:
# 启用 Relationship 批量加载(写入优化) atlas.graph.storage.batch-loading=true # Relationship 缓存大小 atlas.relationship.cache.size=10000 # 是否在创建 Entity 时自动创建 Relationship(默认 true) atlas.entity.relations.auto.create=true7.3 监控指标(Prometheus)
| 指标 | 说明 |
|---|---|
atlas_relationship_created_total | Relationship 创建总数 |
atlas_graph_edge_query_latency_ms | Relationship 查询延迟 |
janusgraph_edges_count | 图数据库边总数(反映血缘规模) |
八、FAQ:高频问题解答
Q1:Relationship 与 Entity 属性有何性能差异?
| 维度 | Relationship | Entity 属性 |
|---|---|---|
| 存储 | JanusGraph 边(独立 Row) | Entity JSON 内嵌 |
| 查询 | 图遍历 O(1) | 需反序列化整个 Entity |
| 更新 | 原子操作 | 需重写整个 Entity |
| 双向导航 | 原生支持 | 需手动维护反向引用 |
结论:超过 10 个关联对象时,必须使用 Relationship。
Q2:如何修复断裂的血缘关系?
- 通过
GET /api/atlas/v2/entity/guid/{guid}检查 Entity 的relationshipAttributes - 若缺失,手动重建 Relationship
- 若 GUID 错误,需先修复上游 Entity 注册
⚠️Atlas 2.4.0 限制:不支持 Relationship 的 PATCH 更新,只能删除重建。
Q3:Hive Hook 为何有时不创建 Relationship?
Hive Hook 仅在表结构变更(ALTER TABLE ADD COLUMN)时创建hive_table_columnsRelationship。若表已存在,首次上报不会补建。
解决方案:
在HiveMetaStoreBridge.registerTable()中强制重建关系:
// HiveMetaStoreBridge.javaif(tableExistsInAtlas){updateTableRelationships(table);// 补全列关系}Q4:能否跨集群建立 Relationship?
可以,但需确保:
- 两端 Entity 的
qualifiedName包含正确集群名(如@clusterA,@clusterB) - Atlas Server 能访问两个集群的元数据
示例:
hdfs://clusterA/path→ Spark →clickhouse://clusterB/table
Q5:Relationship 支持事务吗?
支持。Atlas 的EntityMutationResponse接口保证:
- 同一批次的 Entity 和 Relationship 创建具有原子性
- 失败时全部回滚
但跨批次操作无事务保证,需业务层处理幂等。
九、总结与最佳实践
9.1 适用场景
- 强血缘追踪:金融交易、医疗数据流
- 复杂依赖管理:多级宽表、特征工程 pipeline
- 动态影响分析:表结构变更影响评估
9.2 避坑指南
- ✅Always:使用
isContainer=true建模“整体-部分”关系(如表-列) - ✅Always:血缘 Relationship 使用内置
dataset_process_dataset - ❌Never:手动拼接 Relationship GUID(应通过 API 获取)
- ❌Never:在高并发场景关闭
batch-loading
9.3 扩展方向
- Relationship 版本化:记录关系变更历史
- 动态 Relationship:基于规则引擎自动推导(如“同名字段即血缘”)
- 跨 Atlas 实例同步:联邦 Relationship 管理
作者署名:九师兄
- 专题目录:【Apache Atlas】Apache Atlas 资深工程师到专家实战之路目录
- 总目录:【目录】技术体系目录
注意:本文由 AI 辅助生成,技术细节请以官方文档为准。生产环境使用前务必充分测试。