OneID实体识别实战,汽车行业图算法设计与避坑指南 | Entity Resolution | Spark GraphX | CDP | 数据治理
18年汽车数字化实战经验,从C企CDP项目拆解OneID完整设计方案,图算法匹配、置信度分级、离线+实时双轨架构、五大避坑要点。
目录
- 前言
- 一、OneID概念解析
- 二、汽车行业数据源全景
- 三、图算法匹配设计
- 四、置信度A/B分级
- 五、数据清洗规则
- 六、v_global_id虚拟ID机制
- 七、归并和解绑逻辑
- 八、离线+实时双轨架构
- 九、三级客户质量分类
- 十、四个验证指标
- 十一、五大避坑指南
- 十二、总结
前言
上一篇聊了主数据治理,这篇展开讲其中最核心的模块,OneID。这是C企CDP项目中花时间最多、踩坑最深的模块,也是数据治理从理论到落地最难啃的骨头之一。
一、OneID概念解析
DAMA体系里没有OneID这个词,对应的学术术语是Entity Resolution(实体识别),把分散在不同数据源中的、指向同一现实实体的多条记录,通过标识符匹配关联到统一ID。
OneID完成后,下一步是生成Golden Record(黄金记录),把匹配上的多条记录合并成一份最完整、最准确的客户档案。
OneID 流程
多源记录 → 标识符匹配 → 统一ID → 合并去重 → Golden Record
核心区别
OneID = 识别「是不是同一个人」 Golden Record = 合并后「最好的一份记录」
二、汽车行业数据源全景
C企CDP项目接入OneID计算的数据源最终覆盖了十几个系统,40多张表。
数据源 | 主要标识 | 典型场景 |
单点登录系统(SSO) | GlobalID + 手机号 | 认证用户的核心绑定关系 |
CRM系统 | 手机号 | 销售线索、会员数据 |
DCS系统(多业务线) | 手机号 + 证件号 + VIN | 线索、订单、实销、维修工单 |
DMP广告投放平台 | 设备ID(OAID/AndroidID/IMEI/IDFA) | 广告点击行为 |
统一埋点平台 | GlobalID/手机号/UnionID/OpenID/设备号 | App和小程序埋点数据 |
全媒体系统 | 手机号 | 服务请求、用户沟通记录 |
车联网平台 | 车机端用户数据 | 车机端用户行为 |
官方App | 手机号 | App注册用户 |
商城平台 | 手机号 | 电商会员 |
企微 | ExternalUserID | 企业微信客户联系人 |
直营线索平台 | 手机号 | 直营订单数据 |
涉及的用户ID类型有12种,GlobalID、手机号、证件号、邮箱、UnionID、OpenID、ExternalUserID、IMEI、AndroidID、OAID、IDFA、IDFV。
人和车的关系更复杂,一个人名下可能有多辆车,一辆车也可能被多人使用。OneID不仅合并人,还要建人-车关系图谱。
三、图算法匹配设计
核心思路,用图算法替代简单的分级匹配。
第一步,Snowflake全局唯一编号
给每一个「用户ID + ID类型」的组合生成一个全局唯一的数字编号,作为图计算的节点。
节点示例
手机号 13812345678 → Snowflake ID: 1234567890 GlobalID abc123 → Snowflake ID: 1234567891
第二步,构建关系对(图的边)
同一条记录里既有手机号又有GlobalID,两个ID之间就有一条边。关系对来自各个业务系统。
第三步,Spark GraphX连通图算法
// 核心逻辑示意 val graph = Graph(vertices, edges) val connectedComponents = graph.connectedComponents().vertices // 互相能连通的ID会被分到同一个图中 // 一个图 = 一个潜在用户
第四步,选OneID
在每个连通图里找优先级最高的ID类型,用它的Snowflake编号作为OneID。
ID优先级排序
- GlobalID(单点登录ID)
- 手机号
- 证件号
- 邮箱
- UnionID
- OpenID
- ExternalUserID(企微ID)
- IMEI
- AndroidID
- OAID
- IDFA
- IDFV
冲突处理,Dijkstra最短路径
一个连通图里出现多个最高优先级ID时,用Dijkstra算法计算路径权重,权重最大的路径获胜,另一个被拆分出去。这个过程叫「压制」,对应的反向操作叫「增益」。
四、置信度A/B分级
维度 | A类(高置信度) | B类(低置信度) |
权重 | 1.0 | 0.8 |
来源 | SSO account_info表、DCS实销客户表 | CRM线索表、全媒体、埋点平台、广告设备ID |
特点 | 用户自注册验证、实名认证 | 可能代填、未验证 |
冲突处理 | 优先采信 | 让位A类 |
冲突优先级规则,account_info表 > A类关系对 > 相同置信度下时间最新的关系对
无效关系对不直接删,存到脏数据表,记录来源系统和脏数据原因。
五、数据清洗规则
物理ID清洗
// 核心逻辑示意 val graph = Graph(vertices, edges) val connectedComponents = graph.connectedComponents().vertices // 互相能连通的ID会被分到同一个图中 // 一个图 = 一个潜在用户 # 手机号清洗规则 import re def clean_phone(phone): phone = re.sub(r'[^\d]', '', phone) # 去除所有非数字字符 if re.match(r'^1[3-9]\d{9}$', phone): return phone # 有效手机号 elif re.match(r'^\d{3,4}\d{7,8}$', phone): return phone # 座机号保留 else: return None # 脏数据,存脏数据表 # 证件号校验(身份证示例) def validate_id_card(id_card): if re.match(r'^\d{15}$', id_card): return True # 15位老版身份证 elif re.match(r'^\d{17}[\dXx]$', id_card): return True # 18位新版身份证 return False关系对清洗
异常类型 | 处理方式 |
同系统内一对多/多对多 | 只保留最新关系对,其他进脏数据表 |
跨系统冲突 | 按优先级处理,被丢弃的进脏数据表 |
手机号关联超20个身份证号 | 判定公共电话,从OneID中移除 |
关系对变更 | 清除旧关系对,保留最新 |
六、v_global_id虚拟ID机制
解决手机号单独出现时的OneID稳定性问题。
场景
- 手机号138先单独出现在CRM线索表 → 以手机号为OneID
- 后来客户注册App → 有了GlobalID
- OneID应该变成GlobalID → 但变了就要迁移所有历史数据
解决方案
- 手机号没有跟任何GlobalID同时出现时
- → 用Snowflake生成v_global_id(虚拟GlobalID)
- → 以v_global_id作为OneID
- → 将来GlobalID出现,归入同一连通图
- → OneID编号不变 = OneID不变性
七、归并和解绑逻辑
归并逻辑(四种场景)
归并逻辑(四种场景)
- 场景1: 手机号单独出现 → 以手机号为OneID
- 场景2: GlobalID + 新手机号 → 以GlobalID为OneID
- 场景3: GlobalID + 已有记录的手机号 → 以手机号已有OneID为准
- 场景4: GlobalID重新绑新手机号 → 沿用GlobalID当前OneID
解绑逻辑
GlobalID跟手机号解绑后,旧手机号废弃。如果后来被新客户办理,重新走归并逻辑。
踩过的坑
会员解绑手机号后,旧手机号在OneID里彻底消失,历史数据断链。
修复方案
- 清洗关系对前先判断
- → 被丢弃的ID是否还有其他关系对
- → 有 → 不做处理
- → 没有 → 生成v_global_id保留
效果: 覆盖率提升约5%
八、离线+实时双轨架构
离线(T+1全量)
源系统 → Hive → Spark GraphX → OneID结果,每天凌晨跑,最晚5点前完成
实时(增量)
源系统 → CDC → Kafka → Flink SQL清洗 → Kafka(关系对) → object-modeling服务 → PostgreSQL
对齐策略
每天凌晨离线跑完后 → 覆盖实时结果 → 数据对齐
实时OneID目前接了两个场景,线索汇聚和会员注册。
九、三级客户质量分类
级别 | 条件 | 说明 |
认证会员 | GlobalID + 手机号 + 证件号 | 身份信息完整且认证 |
普通会员 | GlobalID + 手机号 | 部分完整,无证件号或未验证 |
非会员 | 无GlobalID,只有手机号或设备ID | 待验证或匿名数据 |
三级互斥,可自动升级。非会员注册App变普通会员,买车提供证件号变认证会员,OneID不变。
十、四个验证指标
指标 | 说明 | 达标标准 |
ID归属唯一性 | 每个原始ID只能查到一个OneID | 100% |
重心稳定性 | OneID上可认证ID不应频繁变更 | 版本间变更率低 |
增益率 | 算法连通原本不连通的ID的效果 | 越高越好 |
压制率 | 算法打散不合理ID关联的效果 | 合理范围 |
增益率公式: (原始关系对数量 - OneID结果数量) / 原始关系对数量
十一、五大避坑指南
坑一,源系统主数据不规范就上OneID
手机号格式不统一,证件号缺失率超30%。先做Data Profiling(数据探查),评估覆盖率、唯一性和规范度,达标再进匹配流程。
# Data Profiling 检查项 data_profiling: phone: coverage_rate: ">= 85%" uniqueness_rate: ">= 95%" format_compliance: ">= 90%" id_card: coverage_rate: ">= 60%" uniqueness_rate: "100%"
坑二,关系对清洗太暴力导致物理ID消失
一对多清洗时直接删关系对,某些物理ID在OneID里消失。加了「保留孤岛ID」逻辑,被丢弃的ID如果没有其他关系对,生成v_global_id保留。约5%的数据受影响。
坑三,没有区分置信度等级
所有关系对一视同仁,低质量数据污染高质量数据。分A/B两类,A类权重1.0,B类权重0.8,图算法路径计算中自动体现差异。
坑四,忽略了人-车关系
汽车行业必须同时建人-车关系图谱。定义了24个融合对象,每个对象有自己的归并规则。
坑五,不可逆操作没有审计
合并操作不可逆。每次合并必须有审计日志,脏数据表是审计依据不是垃圾箱。
"dirty_record_id": "DRR_2024_00001", "source_system": "DCS", "source_table": "ustomer", "confidence_level": "A", "dirty_reason": "one_to_many_conflict", "business_update_time": "2024-08-15T10:30:00Z", "batch_time": "2024-08-16T02:00:00Z" }
十二、总结
OneID看着是技术模块,说到底是一套数据治理体系。前置工作(主数据规范、数据质量、清洗规则、审核流程)占70%精力,匹配算法只占30%。
C企CDP项目里OneID从V1到V3迭代了三个版本。V1接CRM和DCS核心数据,V2加埋点平台和车联网数据,V3补直营、官方App、电商数据。每个版本按「数据调研→策略定义→数据清洗→数据开发→数据验证」流程走。
很多人问我要OneID技术方案,我一般先反问,你们源系统手机号规范率是多少?回答不上来,就不是做OneID的时候。
TagsOneID EntityResolution CDP 数据治理 SparkGraphX 图算法 客户数据平台 GoldenRecord 数据质量 IdentityResolution 大数据 Flink Kafka Snowflake