3、关系型数据库中的图建模:邻接表、关系表、SQL表示图归纳总结
咱们做反欺诈图谱衍生变量,第一步就是把图数据塞进关系型数据库里。这不难,但选错建模方式,后面写递归查询时就等着哭吧。我这些年踩过的坑,全是在“存图”阶段埋下的。
邻接表模型——最直接的父子关系
邻接表,说白了就是在表里加一列 parent_id 指向上一级。比如给用户表加个 invited_by 字段,就能表示推荐关系链。这种模型直观,新手一看就懂。
CREATE TABLE user (
id INT PRIMARY KEY,
name VARCHAR(50),
invited_by INT REFERENCES user(id) -- 邻接关系
);
但注意了,邻接表只适合“每个节点最多一个父节点”的树形结构。反欺诈场景里,一个设备关联多个账号、一个账号关联多个IP,这全是多对多网络。用邻接表硬做,你会写出一堆自连接,性能还极差。我曾经在一个项目里用邻接表存储设备指纹图,跑一个两层深度遍历就要十几秒……后来全改了。
WITH RECURSIVE 的 CYCLE 子句处理,但早期 MySQL 不支持,踩过这个坑的人不只我一个。
关系表模型——显式的边存储
真正的图建模,我们得把“边”单独拎出来。一张节点表存实体,一张边表存关系。这才是关系型数据库里最通用的图模型。
CREATE TABLE node (
id INT PRIMARY KEY,
label VARCHAR(20) -- 'USER','DEVICE','IP'等
);
CREATE TABLE edge (
from_node INT REFERENCES node(id),
to_node INT REFERENCES node(id),
rel_type VARCHAR(20), -- 'LOGIN','SHARE_DEVICE'
weight FLOAT,
created_at TIMESTAMP,
PRIMARY KEY (from_node, to_node, rel_type)
);
关系表的好处是:
- 支持任意度数的边(多对多、子环、自环)
- 可以给边加属性(时间、权重、类型)——做衍生变量的核心
- 索引优化后,两跳遍历秒级响应
我建议在生产环境里,用关系表作为反欺诈图数据库的底层存储。邻接表只适合做“管理层级”这种固定树结构,别乱用。
SQL中的图遍历——递归CTE
图存进去了,怎么查?关系型数据库没有内置图遍历,但SQL标准提供了递归CTE。这玩意儿就像一把瑞士军刀,能处理大部分图查询需求。
WITH RECURSIVE reachable AS (
-- 起点
SELECT from_node, to_node, 1 AS depth
FROM edge
WHERE from_node = 1001
UNION ALL
-- 递归步
SELECT e.from_node, e.to_node, r.depth + 1
FROM edge e
INNER JOIN reachable r ON r.to_node = e.from_node
WHERE r.depth < 5
)
SELECT to_node, depth FROM reachable;
你想想看,仅用SQL就能查出某个账号所有间接关联的节点,最多5层。这在反欺诈里太常用了——比如查一个异常IP扩散到了多少设备。
但注意了,递归CTE在处理超大图(千万级边)时,如果深度超过10层,性能会断崖式下跌。原因在于关系型数据库是基于行的迭代,不是向量化计算。我一般会加两个限制:深度不超过6层,且每条查询都要用索引覆盖。
(from_node, to_node, rel_type) 和 (to_node, from_node),让递归CTE的内连接走索引。不加索引时,两次迭代后全表扫描,CPU直接打满。
三种方式对比归纳
光说理论不如一张表直观。我把实际项目经验整理成下面的对比:
| 模型 | 适用场景 | 遍历能力 | 边属性支持 | 索引效率 | 我的推荐 |
|---|---|---|---|---|---|
| 邻接表 | 树形层级(组织架构、推荐链) | 弱(深度遍历性能差) | 无 | 中等 | 仅用于固定树结构 |
| 关系表 | 多对多网络(设备-账号-IP) | 强(配合递归CTE) | 丰富(时间、权重、类型) | 高(双表索引分开) | 首选方案 |
| SQL递归CTE | 图遍历查询 | 中等(深度≤6层性能可接受) | 依赖边表 | 依赖索引设计 | 必须掌握的技能 |
下面这张图概括了三种建模方式的关系和选择路径,你存图时可以参考这个逻辑:
核心经验:反欺诈图谱一定是多对多网络,直接用关系表。别被邻接表的简单迷惑了——我曾经在早期项目里犯过这错误,后来重构时改边表花了三天,连带影响了上层全部查询SQL。一次选对,省半年。
这三种方式不是互斥的。你可以在同一个数据库里混用:管理层级用邻接表,设备关联网络用关系表,然后统一通过递归CTE对外提供图查询接口。但要记住,不管用哪种,索引设计和深度限制是永远逃不掉的对立面。