4. 金融数据模型设计:实体、关系、属性与图Schema定义
金融图谱的核心在于将复杂的金融业务抽象为实体(Vertex)、关系(Edge)和属性(Property)。本章定义批流一体链路中的标准数据模型,确保图结构在离线批处理和实时流处理中保持一致。
设计原则:实体必须具有唯一标识(ID),关系必须具有方向性和时间戳,属性需区分静态属性(如客户姓名)和动态属性(如账户余额)。
4.1 核心实体定义
金融图谱包含三大核心实体:客户、账户和交易。每个实体都包含一组关键属性,用于支撑风控、营销和监管场景。
| 实体类型 | 唯一标识 | 核心属性 | 属性说明 |
|---|---|---|---|
| 客户 (Customer) | customer_id | name, id_card, phone, risk_level, create_time | 静态身份信息 + 动态风险等级 |
| 账户 (Account) | account_id | account_type, balance, currency, open_date, status | 账户类型(储蓄/信用/投资)、余额、状态 |
| 交易 (Transaction) | txn_id | amount, direction, timestamp, channel, counterparty | 金额、方向(入/出)、时间戳、渠道 |
批流一致性要求:在批处理(Hive/Spark)和流处理(Flink/Kafka)中,实体的ID生成策略必须统一。建议使用UUID或雪花算法,避免ID冲突。
4.2 关系类型与语义
关系定义了实体之间的业务连接。在金融图谱中,关系必须携带时间窗口和权重,以支持时序分析和影响力计算。
- 转账 (TRANSFER):从账户A到账户B的资金转移。属性包括金额、时间、渠道、交易备注。方向:有向。
- 担保 (GUARANTEE):客户A为客户B提供担保。属性包括担保金额、起始日期、到期日期、担保类型。方向:有向。
- 持股 (SHAREHOLDING):客户持有账户或公司股份。属性包括持股比例、持股数量、持股起始时间。方向:有向。
- 亲属 (RELATIVE):客户之间的亲属关系。属性包括关系类型(配偶/子女/父母)。方向:无向。
- 设备关联 (DEVICE_LINK):客户与设备(手机/电脑)之间的关联。属性包括设备ID、首次关联时间、关联频次。方向:无向。
避坑提示:关系属性中的时间戳必须使用UTC时间,避免因时区不一致导致图分析结果偏差。在流处理中,事件时间(Event Time)优先于处理时间(Processing Time)。
4.3 属性设计规范
属性设计遵循扁平化和可扩展原则。避免嵌套结构,优先使用基本数据类型。
| 属性类别 | 数据类型 | 示例 | 备注 |
|---|---|---|---|
| 标识属性 | STRING | customer_id: "CUST_20241001_001" | 全局唯一,不可变 |
| 数值属性 | DOUBLE / LONG | balance: 150000.50 | 保留两位小数,单位统一 |
| 时间属性 | LONG (Unix Timestamp) | create_time: 1727712000000 | 毫秒级精度 |
| 枚举属性 | STRING (枚举值) | account_type: "SAVING" | 使用大写常量,避免歧义 |
| 标签属性 | ARRAY<STRING> | tags: ["高风险", "大额交易"] | 用于动态标注 |
4.4 图Schema定义
图Schema是图谱的元数据骨架。以下使用Apache TinkerPop / Gremlin风格的Schema定义,兼容JanusGraph、Neo4j等主流图数据库。
// 顶点标签定义
schema.vertexLabel('customer')
.partitionBy('customer_id', Text)
.property('name', Text)
.property('id_card', Text)
.property('phone', Text)
.property('risk_level', Text)
.property('create_time', Long)
.create();
schema.vertexLabel('account')
.partitionBy('account_id', Text)
.property('account_type', Text)
.property('balance', Double)
.property('currency', Text)
.property('open_date', Long)
.property('status', Text)
.create();
schema.vertexLabel('transaction')
.partitionBy('txn_id', Text)
.property('amount', Double)
.property('direction', Text)
.property('timestamp', Long)
.property('channel', Text)
.property('counterparty', Text)
.create();
// 边标签定义
schema.edgeLabel('transfer')
.from('account').to('account')
.property('amount', Double)
.property('timestamp', Long)
.property('channel', Text)
.create();
schema.edgeLabel('guarantee')
.from('customer').to('customer')
.property('guarantee_amount', Double)
.property('start_date', Long)
.property('end_date', Long)
.create();
schema.edgeLabel('shareholding')
.from('customer').to('account')
.property('ratio', Double)
.property('quantity', Long)
.property('start_time', Long)
.create();
批流Schema对齐:离线批处理(Spark GraphX)和实时流处理(Flink Gelly)必须使用同一份Schema定义。建议将Schema存储在Git仓库中,通过CI/CD自动同步到图数据库和计算引擎。
4.5 图结构可视化
下图展示了金融图谱的核心实体与关系结构,包含客户、账户、交易三大实体以及转账、担保、持股三种主要关系。
4.6 批流一体数据模型映射
在批流一体架构中,图数据模型需要同时适配离线存储(HDFS/Parquet)和实时存储(Kafka/Redis)。以下映射表展示了不同存储层的数据模型表示。
| 存储层 | 实体表示 | 关系表示 | 序列化格式 |
|---|---|---|---|
| 离线批处理 (Hive) | Parquet表,按customer_id分区 | Parquet表,包含from_id, to_id, edge_type | Avro / Parquet |
| 实时流处理 (Kafka) | JSON消息,key为实体ID | JSON消息,包含source, target, properties | JSON / Protobuf |
| 图数据库 (JanusGraph) | Vertex Label | Edge Label | Gremlin / GraphSON |
| 缓存层 (RedisGraph) | Graph Entity | Graph Edge | RedisGraph Query |
实践建议:在Kafka消息中,实体和关系使用统一的Schema Registry管理。建议使用Avro或Protobuf,确保批流两端的数据结构一致,避免反序列化失败。
4.7 属性演化与版本控制
金融业务模型会随时间变化,属性演化必须遵循向后兼容原则。推荐使用Schema版本号管理属性变更。
- 新增属性:设置默认值,允许为空。例如新增"customer_segment"字段,默认值为"UNKNOWN"。
- 删除属性:标记为废弃(deprecated),保留数据但不写入新值。避免直接删除导致历史数据断裂。
- 修改属性类型:创建新属性并迁移数据,旧属性保留为别名。例如将"balance"从FLOAT改为DOUBLE。
- 版本号管理:在实体和关系的属性中增加"schema_version"字段,用于区分不同版本的数据。
// 属性演化示例:客户实体增加schema_version
schema.vertexLabel('customer')
.property('schema_version', Int) // 当前版本: 2
.property('customer_segment', Text) // 新增字段,默认值: "UNKNOWN"
.property('risk_level', Text) // 原有字段,保持不变
.update(); // 使用update而非create,保留已有数据
避坑提示:在流处理中,属性演化可能导致反序列化异常。建议在Flink或Spark Streaming中使用Schema Registry的兼容性检查(Backward/Forward),确保新旧版本数据都能正常处理。