Neo4j 做关系推荐:Cypher 三度好友查询实战
社交产品要做"可能认识的人"推荐,第一版用关系库写的:好友的好友(二度)SQL 套两层 join 还能跑,产品要三度关系加共同好友数排序,SQL 变成了五层嵌套的怪物,跑了 40 秒。迁到 Neo4j 后同样的逻辑 Cypher 几行、200 毫秒,这次实战让我理解了图数据库的适用边界。
模型设计先想清楚。节点是 User(带 uid、city 属性),关系是 KNOWS(带 since、weight)。图建模的原则是"关系是一等公民":KNOWS 是物理存在的边(指针式的连通),不是关系库里要用 join 现场推导的外键。三度好友查询的 Cypher:
// 三度关系推荐:认识你的人认识的人再认识,但不是你已认识的
MATCH (me:User {uid: $uid})-[:KNOWS*2..3]-(friend:User)
WHERE friend.uid <> $uid
AND NOT (me)-[:KNOWS]-(friend) // 排除已是好友
AND friend.city = $city // 同城加权
WITH friend, count(DISTINCT friend) AS paths,
length(shortestPath((me)-[:KNOWS*]-(friend))) AS degree
RETURN friend.uid, friend.name,
paths * 1.0 / degree AS score // 路径多且近者优先
ORDER BY score DESC
LIMIT 20;
查询里两个设计点。可变长关系 [:KNOWS*2..3] 是图查询的灵魂,等价于 SQL 里不可想象的多层递归 join,Neo4j 沿边遍历是本地指针跳转,成本和度数相关而不是和全图规模相关——这是 200ms 对 40s 的来源。NOT (me)-[:KNOWS]-(friend) 的反模式匹配用来排除已认识的人,写法上是"模式即条件"的图思维,SQL 里对应 NOT EXISTS 子查询但执行代价完全不同。
工程化的配套也有讲究。属性索引 CREATE INDEX FOR (u:User) ON (u.uid) 保证起点定位 O(1);遍历深度要设上限(这里 3 度封顶),无界的可变长遍历在大图上可能失控;命中高频的推荐查询加了 5 分钟 TTL 的应用层缓存。数据导入用 neo4j-admin 的 CSV 批量导入,2000 万用户加 8 亿关系 40 分钟灌完,比逐条 CREATE 快两个数量级。
图运维的配套也提一句:Neo4j 的备份用 neo4j-admin dump 在线快照,千万节点级十分钟内完成;APOC 插件几乎必装,批量导入、路径扩展、图算法的瑞士军刀。图数据库的生态比关系库薄,周边工具的成熟度要在选型时一起评估,不然落地时处处补课。
最后说边界:图数据库不是关系库的替代,是补充。我们的订单、账目仍然在 MySQL,只有"关系密集"的数据(好友、权限继承、风控关联网络)值得进图——判断标准是 join 深度:业务查询常态化要两层以上 join 且以连通性为核心,图的收益才开始兑现。
- 点赞
- 收藏
- 关注作者
评论(0)