暂无图片
暂无图片
1
暂无图片
暂无图片
暂无图片

NebulaGraph如何重构人际关系的存储逻辑

原创 多明戈教你玩狼人杀 2025-07-01
470

前情提要《你这不是关系型数据库吗,怎么没法显示“关系”》一文曾经提到,关系型数据库与现实中的关系往往存在一个鸿沟,那么我们今天再次展开,从理论出发,来聊聊这个“关系”悖论。

为何“关系”成为数据库的阿克琉斯之踵?

如果大家做过面向对象编程,那么对于关系型数据库的表以及类之间的异曲同工会有心得。本质上,这两者都是对于现实世界各种属性的抽象和建模。它们在软件开发中常常被一起使用,也能够印证其中的关联。
然而,如果我想要表达两个实体之间的“关系”又该怎么办?比如客户经理A服务于客户A,客户经理助理A又服务于客户经理A?那么我们往往会创建三张表,分别代表三个不同的角色,再使用外键或者维护一张关系表来做关系的对应。

customer_id customer_name customer_contact customer_region
1001 客户 A contact_a@company.com 华东
1002 客户 B contact_b@company.com 华南
1003 客户 C contact_c@company.com 华北
1004 客户 D contact_d@company.com 西南
manager_id manager_name manager_email managed_region
2001 张经理 manager_zhang@company.com 华东
2002 李经理 manager_li@company.com 华南
2003 王经理 manager_wang@company.com 华北
2004 刘经理 manager_liu@company.com 西南
assistant_id assistant_name assistant_email associated_manager_id
3001 小张助理 assistant_xiaozhang@company.com 2001
3002 小李助理 assistant_xiaoli@company.com 2002
3003 小王助理 assistant_xiaowang@company.com 2003
3004 小刘助理 assistant_xiaoliu@company.com 2004
relationship_id customer_id manager_id
4001 1001 2001
4002 1002 2002
4003 1003 2003
4004 1004 2004

那么,我想要获取某个客户是哪个客户经理服务,这个客户经理又有哪些客户经理助理,就需要一个多表join:

SELECT c.customer_name AS 客户名称, m.manager_name AS 客户经理姓名, ma.assistant_name AS 助理姓名, m.manager_email AS 客户经理邮箱, ma.assistant_email AS 助理邮箱 FROM customers c JOIN customer_manager_relationship cmr ON c.customer_id = cmr.customer_id JOIN managers m ON cmr.manager_id = m.manager_id JOIN manager_assistants ma ON m.manager_id = ma.associated_manager_id WHERE c.customer_name = '客户 A';

三张表的join,就意味着随着数据量的提升,有可能出现查询性能的断崖式下滑。而过往很多互联网公司的XX军规,就有一条不允许超过几张表的join就是来自于此。这背后的本质在于,外键无语义描述能力,无法表达关系的强度、类型等属性,难以满足复杂关系的存储需求。即便是如今关系型数据库已经如此强大,仍然有着它们无法有效覆盖到的场景。

然而,人际关系的复杂之处就在于此,我们现实中的人际关系是动态的网状结构,非静态层级,随时可能发生变化,难以用传统的表结构来准确表述——我遇见谁会有怎样的对白,我等的人她在多远的未来。就比如Jack Ma背后有哪些企业和关联人,这些关联的任何企业具体和他又是什么关系,用管系统数据库可以表述,但是在查询时带来的复杂度以及性能开销,都是远超我们想象的。其中任何一环有了变化,都会引起滚雪球一样的修改。
而且,人人之间的关系具有多维度属性,如关系强度、类型、时效性等,传统表结构难以完整存储这些信息,导致信息丢失。比如我和某个人,以前是同学,现在是同事,未来可能是朋友,然后我和他可能还有债务关系,用关系型数据库可不可以?可以,但是多维度属性就需要维护更多的表或者字段。
image.png

图数据库:存储逻辑的重构哲学

我们仍然从理论出发,图数据库表达“关系”时,有哪些先天优势。
首先就是图数据库的三要素:
顶点:人/物(携带属性),如姓名、年龄等,是关系的主体。一个顶点有时候更像关系型数据库中的一条记录,包含了属性,同时代表一个确定的实体。
:关系(可携带权重、类型),如亲密度、时间等,是关系的连接。边在关系型数据库里怎么直接描述?外键或者其他方式的引用,但是图数据库中,一条记录足以,甚至表达更加简练精确。
属性:为顶点和边提供详细信息,丰富关系的语义。顶点和边都可以带属性,比如顶点里人有年龄有身高,边的属性里有关系的走向以及关系的具体定义等等。

那么与关系型数据库相比,图数据库的差异就很明显:
关系表达:关系型数据库采用隐式表达(外键约束)简介存储引用,图数据库采用显式(一等公民)直接存储关系。
查询模式:关系型数据库集合操作,图数据库图遍历。

既然关系表达和查询模式的差异,那么必然就会带来插叙语言的不同,SQL查询作为结构化查询,有着自己先天的优势,但是在面对图数据库时,就会有自己的局限性。比如说,我要查询黄晓明和李晨的关系,以及他们有没有共同的朋友或者间接合作的企业,SQL语言的局限性就出来了。需要显式指定Join路径,并且路径固化,难以应对动态关系查询。
那么如果我用图数据库语言来查询,不但可以便利,还可以通过查询关系深度挖掘到更多不同的信息。
还是拿黄晓明和李晨关系为例子,深度1度,可以获取他们直接的商业关联,二度还可以发现共同的朋友以及互相的商业关系。这是SQL语言所不擅长的。
image.pngimage.png

NebulaGraph 如何重构关系存储

首先我们还是拿客户、客户经理、客户经理助理这个关系来规划一下图数据库的模型,按照图数据库,在规划出顶点(tag)代表实体,边(edge)代表关系,而两者各有各的属性,图数据库的三要素就都全了。
image.png
那么我想查询某个客户关联的关系,该怎么查?

-- 查询客户A的客户经理及其助理信息 MATCH (c:Customer {customer_name: '客户A'})-[:Managed_By]->(m:Manager)-[:Assisted_By]->(a:Assistant) RETURN c.customer_name AS 客户名称, m.manager_name AS 客户经理姓名, a.assistant_name AS 助理姓名, m.manager_email AS 客户经理邮箱, a.assistant_email AS 助理邮箱;

不需要考虑多表Join的逻辑,只需要找到客户姓名是客户A的客户,对应关系是Managed_By的客户经理以及关系是Assisted_By的客户经理助理即可,而因为是遍历,性能方面比起多表join也有了很强的性能提升。在NebulaGraph上万条的记录中,实现毫秒级秒的性能。

image.png
为了实现这些,其实NebulaGraph还是做了很多的努力:
架构方面,实现了存算分离,这样有利于水平扩展,以及水多了加面,面多了加水的灵活。
数据模型方面,实现了属性图的灵活建模,点、边、属性都可以按照实际需求添加或者修改。
查询模式,用我们之前所说的遍历模式查询,免索引邻接,邻居访问的方式,数据量越大与关系型数据库的性能优势越明显。

在过去的一段时间里,我做了如下探索和测试:
社交网络分析
合规检测:识别洗钱或黑产团伙,通过关联的设备或者账号,在反洗钱方面的应用。
人际关系分析:关键节点挖掘,例如高净值客户识别,通过股权穿透、周围强人际关系等等。

金融风控
实时反欺诈:闭环交易检测,在关系型数据库中不能直观反映的A→B→C→A路径分析。
知识图谱构建:比如企业股权穿透,让多层持股关系可视化。

Graph+AI(规划中)
GraphRAG:知识图谱增强大模型推理,这部分具体怎么做还想看看其他同行的探索。
Text2GQL:自然语言转查询语句,目的是让业务部门自助完成各类数据查询工作。


最后依旧要强调,每一种数据库有着自己的擅长的领域与场景,我们去研究关系型数据库、文档数据库、图数据库,最终目的不是为了谁替代谁,而是在不同场景里综合选择当下最合适的方案。

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论