深度路径遍历优化:变长路径、遍历器与剪枝

系统讲解图数据库深度路径遍历的优化:多跳查询的挑战(组合爆炸与重复展开)、变长路径查询的语义与写法(*1..5 的展开、方向与约束)、遍历顺序与剪枝策略(先过滤后遍历、方向裁剪、深度限制)、图遍历器与策略(BFS/DFS、双向遍历、迭代式展开)、深度遍历的性能陷阱(路径笛卡尔积、关系属性过滤、无索引展开)、索引与存储优化(关系索引、邻接表缓存)、遍历的批处理与预计算(物化路径、动态规划中间结果)、以及资金链/社交网络深层遍历的实践与遍历监控调优,帮助在图上高效执行大跳数查询。

引言

两跳、三跳的查询很简单,但「找到 A 六跳之内能到达的所有节点」或「A 到 B 之间长度 3 到 8 的所有路径」就会暴露图查询的真实难点:遍历深度每加一跳,中间结果往往指数增长。深度路径遍历优化解决的就是「大跳数查询又快又稳」的问题。本文讲多跳遍历的优化:先厘清深度遍历的挑战(组合爆炸与重复展开),再讲变长路径查询的语义(*1..5 到底怎么展开、方向与约束怎么写),然后是遍历顺序与剪枝策略(先过滤后遍历、方向裁剪、深度限制)、图的遍历器与策略(BFS/DFS、双向遍历、迭代式展开)、深度遍历的性能陷阱(路径笛卡尔积、关系属性过滤陷阱、无索引展开)、索引与存储优化(关系索引、邻接表缓存)、遍历的批处理与预计算(物化路径、中间结果复用)、最后是资金链/社交网络深层遍历的实践与遍历监控调优。目标:你能写出「大跳数也不爆炸」的深度遍历查询,并定位深度遍历的性能瓶颈。

前置:/graphdb-data-model-basics/(属性图模型)、/graphdb-cypher-advanced/(高级路径查询)、/graphdb-graph-query-optimization/(执行计划与优化器)。


目录


1. 深度遍历的挑战:从两跳到大跳

深度遍历的本质:宽度相乘:

一跳:从 A 出发,平均展开 d 个邻居     → 结果 d
两跳:每个邻居再展开 d 个             → 结果 d²
三跳:                              → 结果 d³
六跳:                              → 结果 d⁶

若 d = 10:六跳 = 10⁶ = 100 万(中间结果)
若 d = 100:四跳 = 10⁸ = 1 亿(直接爆炸)
→ 遍历深度每加一跳,中间结果乘一个「平均度数」

为什么两跳简单、六跳难:

- 两跳:中间结果可控(d²),内存放得下
- 三跳以上:中间结果指数增长
- 高扇出节点(超节点):一跳就展开几百万邻居
- 无剪枝的遍历:中间结果全保留 → 内存/CPU 双爆
→ 「跳数」不是问题,「宽度相乘」才是

三个核心开销:

1. 展开开销:每个节点的邻居扫描(IO/CPU)
2. 存储开销:中间路径的保留(内存)
3. 去重开销:重复路径/重复节点的过滤
→ 优化的三条主线:少展开、少保留、少重复

优化的总体思路:

- 少展开:剪枝(先过滤、方向裁剪、深度限制)
- 少保留:物化中间结果、不保留全路径
- 少重复:去重策略(节点集 vs 路径集)
→ 深度遍历优化 = 「把指数增长压成多项式」

什么时候需要担心:

- 深度 ≤ 2:一般不用担心(d² 可控)
- 深度 3-4:看度数(高扇出要剪枝)
- 深度 5+:必须设计(剪枝 + 物化 + 算法)
- 全路径枚举(找所有路径):最容易爆炸
→ 越深、越要「全路径」、越要谨慎

心智:深度遍历的难点是宽度相乘——每加一跳中间结果乘平均度数(10⁶ 乃至 10⁸ 量级),不是「跳数」本身;三条优化主线是少展开(剪枝)、少保留(物化)、少重复(去重);深度 ≥ 5 或全路径枚举时必须设计剪枝与中间结果复用。


2. 变长路径查询:*1..5 的语义

变长路径的语法:

// 1 到 5 跳的任意关系类型
MATCH (a)-[*1..5]->(b)
RETURN a, b

// 指定关系类型 + 方向
MATCH (a)-[:TRANSFER*1..5]->(b)
RETURN a, b

// 无上界(谨慎!)
MATCH (a)-[:TRANSFER*]->(b)
RETURN a, b

*1..5 到底展开什么:

- 语义:所有长度 1、2、3、4、5 的路径
- 等价于:5 次展开的并集
  len=1:  a->x1
  len=2:  a->x1->x2
  ...
  len=5:  a->x1->...->x5
- 默认:路径可重复节点(走回原节点也算)
  → 会产生「来回走」的冗余路径
→ 变长路径 = 「多个固定长度展开的并集」

变长路径的返回内容:

- 返回节点集:RETURN DISTINCT b(只关心可达,去重)
- 返回路径:RETURN p(保留完整路径,量大)
- 返回边集:RETURN relationships(p)(可去重)
→ 能返回节点就别返回路径(省内存)

变长路径的陷阱:

1. 无上界 * 不加深度限制 → 全图扩散(危险)
2. 路径可重复 → 来回路径膨胀(A-B-A-B)
3. 返回路径 p → 每条路径一条记录(笛卡尔积)
4. 谓词在路径内 → 每个中间 hop 都要满足
→ 写变长路径:给上下界 + 明确返回粒度

限制路径的行为(路径谓词):

// 只保留长度 ≤ 5 且不经过 C 的路径
MATCH p = (a)-[:TRANSFER*1..5]->(b)
WHERE NONE(n IN nodes(p) WHERE n.id = 'C')
RETURN p

// 只保留无重复节点的简单路径
MATCH p = (a)-[:TRANSFER*1..5]->(b)
WHERE all(n IN nodes(p)
          WHERE size([m IN nodes(p) WHERE m = n]) = 1)
RETURN p

语义选择:可达性 vs 路径枚举:

- 可达性(是否连通):RETURN DISTINCT 端点
- 最短路径:shortestPath(专用算法,非展开)
- 全路径枚举:最贵(数量指数)
→ 先问「你要端点还是要路径」,再决定写法

心智:变长路径 *1..5 = 1~5 跳所有路径的并集(默认可重复节点、产生来回冗余);写法要点:给上下界、按需加路径谓词(不经过/简单路径)、能返回 DISTINCT 端点就别返回完整路径;无上界 * 全图扩散是最大陷阱。


3. 遍历顺序与剪枝

剪枝 = 在展开前/展开中砍掉分支:

- 前置过滤:只在满足条件的边上展开
  (先 WHERE 后 MATCH,缩小邻居集合)
- 深度限制:超过深度立即停止(*1..5 而非 *)
- 方向裁剪:只沿一个方向展开(单向)
- 节点排除:跳过特定标签/属性的节点
→ 剪枝发生在「展开前」,不是展开后过滤

先过滤后遍历(最重要的剪枝):

// 低效:先展开再过滤(中间结果已爆炸)
MATCH (a)-[:TRANSFER*1..5]->(b)
WHERE b.amount > 10000
RETURN DISTINCT b

// 高效:过滤下推 + 用索引锚定起点
MATCH (a {id: 'A'})-[:TRANSFER*1..5]->(b)
WHERE b.amount > 10000
RETURN DISTINCT b

按边属性剪枝:

// 只沿「金额 > 1000」的边展开
MATCH (a {id:'A'})
-[:TRANSFER*1..5 {amount: 1000}]->(b)
RETURN DISTINCT b
// 注意:关系属性谓词在每个 hop 都生效
// (不是只过滤第一条边)

按时间窗口剪枝(时态遍历):

- 时序资金链:只沿时间递增的边展开
  条件:下一跳的 at > 当前跳的 at
- 实现:路径谓词比较相邻边的时间
  或遍历时保持「当前时间」状态
→ 时间剪枝能把指数路径砍到「时序合理」子集

剪枝的权衡:

- 剪枝越多 → 结果越少 → 越快
- 剪枝可能「剪掉正确答案」(过度剪枝)
- 前置过滤依赖索引(无索引的过滤不省钱)
- 谓词在路径内 vs 只在终点:语义差异大
→ 剪枝要「保正确性」前提下尽可能早

验证剪枝效果:

- 对比剪枝前后的 PROFILE(第 9 节)
- 确认过滤是否「下推」到展开前
- 确认索引被使用(而非全扫)
→ 剪枝有效性要看执行计划,不靠直觉

心智:剪枝 = 在展开前砍分支(前置过滤、深度限制、方向裁剪、节点排除),最关键的是「先过滤后遍历 + 索引锚定起点」;边属性谓词每个 hop 都生效(既是剪枝也是约束);剪枝要保正确性前提下尽可能早,效果看 PROFILE 而非直觉。


4. 图的遍历器与策略

遍历策略:BFS vs DFS:

BFS(广度优先):
  - 一层层展开(按距离分层)
  - 特性:先找到的路径 = 最短路径
  - 内存:同层节点全部驻留(宽度大时吃内存)
  - 适用:可达性、最短距离、分层遍历

DFS(深度优先):
  - 沿一条路走到黑再回溯
  - 特性:内存小(只保留当前栈)
  - 可能先找到长路径(不是最短)
  - 适用:全路径枚举、约束求解
→ 要「最短」用 BFS,要「省内存/全枚举」用 DFS

双向遍历(Bidirectional):

- 从起点正向展开 d/2 跳
- 从终点反向展开 d/2 跳
- 在中间相遇(连接两端集合)
- 复杂度:2 × d^(k/2) ≪ d^k
  例:六跳单向 = d⁶,双向 = 2×d³
→ 双向遍历把指数指数「砍半」——最强优化之一

shortestPath 的算法:

// Neo4j 的 BFS 双向最短路径(默认)
MATCH (a {id:'A'}), (b {id:'B'})
MATCH p = shortestPath((a)-[*..10]-(b))
RETURN p

// allShortestPaths:所有最短路径
MATCH (a {id:'A'}), (b {id:'B'})
MATCH p = allShortestPaths((a)-[*..10]-(b))
RETURN p

迭代式展开(Iterative Deepening):

- 深度优先框架 + 深度上界递增
- 先限 1 跳搜,再 2 跳,再 3 跳……
- 特性:保留 DFS 的低内存,兼得 BFS 的最短性
- 代价:浅层路径被重复展开
→ 适用于「深度未知但有限」的搜索

GDS 的算法遍历器:

- BFS/DFS:Neo4j GDS 有原生实现(非 Cypher 展开)
- 双向 BFS:singleSourceShortestPath 等
- 复杂度在 C++ 层实现,比 Cypher 展开快几个量级
→ 深度遍历优先考虑 GDS 算法,而非手写 Cypher

心智:遍历策略四件套:BFS(分层、最短、吃内存)、DFS(省内存、全枚举)、双向遍历(两端各展开一半在中间相遇,把 d⁶ 变 2×d³,最强优化)、迭代加深(DFS 内存 + BFS 最短性);Neo4j shortestPath 是双向 BFS,GDS 算法遍历比 Cypher 展开快几个量级。


5. 深度遍历的性能陷阱

陷阱 1:路径笛卡尔积:

MATCH p = (a)-[*1..5]->(b) RETURN p
→ 每条路径一条记录:数量指数级
→ 只关心可达:RETURN DISTINCT b(收敛)
→ 只关心边:RETURN DISTINCT relationships(p)
→ 诊断:PROFILE 看 rows 是否远超预期

陷阱 2:关系属性过滤导致的展开:

// 低效:所有 TRANSFER 边都展开,最后才过滤
MATCH (a)-[:TRANSFER*1..5]->(b)
WHERE b.amount > 10000
RETURN DISTINCT b

// 关系属性作谓词:每个 hop 都过滤(变相剪枝)
MATCH (a)-[:TRANSFER*1..5 {amount: 1000}]->(b)
RETURN DISTINCT b

陷阱 3:无索引的锚点展开:

// 起点不是由索引定位,而是全扫匹配
MATCH (a:Account)-[:TRANSFER*1..5]->(b)
WHERE a.id = 'A'
→ 若 id 无唯一索引:先全扫 Account 再展开
→ 建唯一索引:直接定位起点,省全扫

陷阱 4:无上界展开:

MATCH (a {id:'A'})-[:TRANSFER*]->(b)
→ 在全图无限扩散,直到所有可达节点
→ 大图直接 OOM / 超时
→ 永远给上界:*1..N(N 据业务定)

陷阱 5:重复遍历同一子图:

- 多条路径到达同一节点 → 重复展开其后继
- 解法:去重当前层节点(BFS 的 visited 集)
- Cypher:用 DISTINCT / 记忆中间层
→ 防止「同节点反复展开」是去重的核心收益

陷阱 6:路径不可比导致的排序:

- 对路径排序/聚合 → 全部路径驻留内存
- 解法:先收敛(DISTINCT 端点)再排序
→ 排序/聚合对象越大越慢

心智:六大陷阱:路径笛卡尔积(返回路径 p)、关系属性过滤太晚(要作谓词而非终过滤)、无索引锚点(全扫起点)、无上界展开(全图扩散 OOM)、重复展开同子图(要 visited 去重)、对全路径排序聚合(先收敛再排序)——逐个对照 PROFILE 就能定位深度遍历瓶颈。


6. 索引与存储优化

深度遍历的性能基础:快速展开邻居:

- 展开一个节点的邻居 = 读它的邻接表
- 无索引邻接(存储层)已是最快(指针直达)
- Cypher 层还需要:锚点索引(定位起点)+ 属性索引
→ 展开本身靠存储,锚定和过滤靠索引

锚点索引(最重要):

// 起点用唯一属性锚定
CREATE CONSTRAINT unique_account_id
FOR (a:Account) REQUIRE a.id IS UNIQUE

// 终点属性过滤(如:只找 amount 高的终点)
CREATE INDEX account_amount_idx
FOR (a:Account) ON (a.amount)

关系属性索引:

// 按关系属性过滤/剪枝的索引
CREATE INDEX transfer_amount_idx
FOR ()-[r:TRANSFER]-() ON (r.amount)

// 关系类型 + 属性复合
CREATE INDEX transfer_ts_idx
FOR ()-[r:TRANSFER]-() ON (r.at)
→ 时态剪枝(时间窗口遍历)靠这个

存储优化的三个层次:

1. 邻接表缓存:page cache 命中 → 展开不走磁盘
2. 关系类型分离:只展开目标类型([:TRANSFER])
3. 属性字典化:节点属性不膨胀记录体
→ 展开路径 = 邻接表 + 类型过滤 + 缓存命中

page cache 与深度遍历:

- 深度遍历要反复读很多节点的邻接表
- page cache 越大 → 命中率越高 → 越快
- 超节点:它的邻接表是热点,必须常驻缓存
- 监控:db.pageCache.hitRatio 是否 > 0.9
→ 深度遍历吃 page cache,配大 + 保热点

查询写法的存储友好度:

- 指定关系类型:[:TRANSFER*1..5](不扫全类型)
- 指定方向:->(单向展开,省一半)
- 少取属性:RETURN 需要的字段
- 提前 LIMIT:不需要全部结果时 LIMIT
→ 存储友好 = 类型限定 + 方向 + 少取 + 提前 LIMIT

心智:深度遍历存储优化三层:锚点/属性/关系索引(定位与剪枝)、page cache(邻接表命中,展开不走磁盘,超节点邻接表必须常驻)、查询写法(类型限定 + 单向 + 少取 + LIMIT);展开本身靠无索引邻接指针直达,热点是超节点的邻接表。


7. 遍历的批处理与预计算

重复遍历同一批起点 → 批处理:

场景:100 个起点都要 4 跳邻居
做法 1(串行):每个起点单独展开(重复 100 次)
做法 2(批量):一次性多起点并行展开
  → GDS multi-source BFS(多源 BFS)
  → 一次遍历覆盖所有起点
→ 多源 BFS 把「每个起点一遍」变「一遍所有起点」

多源 BFS(GDS):

- breadthFirstSearch(sourceNodes: [...])
- 一次遍历同时记录「距离每个源的最近跳数」
- 结果:每个节点到最近的源的距离
- 适用:多中心可达性、多店覆盖分析
→ 比循环调用单源 BFS 快 N 倍(N=源数)

物化路径(预计算):

场景:高频查询「A 到 B 是否 5 跳内连通」
做法 1(实时展开):每次现算(慢)
做法 2(物化路径):预先计算并存储
  - 物化「对可达对」:(A,B) 到可达性布尔/最短距离
  - 或物化热门路径:(A)-[:REACHES {depth:3}]->(B)
  - 查询直接读物化结果(O(1) 查找)
→ 物化 = 空间换时间,适合固定高频查询

中间结果复用(动态规划):

- 从 A 到 X 的第 3 跳路径,是第 4 跳的子集
- 计算 1..5 跳时复用 1..3 跳的结果
- 等价于图的「传递闭包」增量构建
- 实践:分层缓存(每跳一层),上层用下层
→ 复用中间层 = 把「每次全展开」变「增量扩展」

物化的维护:

- 图变更后物化结果过期
- 策略:事件驱动重算(变更即触发)
- 或增量更新(只重算受影响路径)
- 或定期重建(离线批处理)
→ 物化要配套「失效与重建」机制

预计算的适用判断:

- 高频固定查询 → 物化(收益大)
- 低频随机查询 → 实时展开(不做物化)
- 图大且变更频繁 → 物化维护成本高(慎用)
→ 物化 = 查得快但维护贵,低频/易变慎用

心智:深度遍历批处理三件套:多源 BFS(GDS,一遍覆盖所有起点,比串行快 N 倍)、物化路径(预存可达对/距离,O(1) 读,需配失效与重建机制)、中间结果复用(每跳一层缓存、增量扩展,把全展开变动态规划);高频固定查询才值得物化,低频/易变慎用。


8. 实践:资金链与社交的深层遍历

案例 1:资金链多层穿透:

// 场景:从某账户出发,5 跳内的资金可达
// 目标:识别洗钱环形路径 / 最终受益人
MATCH (start:Account {id: $startId})
MATCH (start)-[:TRANSFER*1..5]->(suspect:Account)
WHERE suspect.id <> $startId
RETURN DISTINCT suspect.id
// 优化:TRANSFER 关系索引 + 锚点唯一索引

资金链的时序约束:

// 只沿时间递增的转账(时序资金链)
MATCH p = (start:Account {id: $startId})
-[:TRANSFER*1..5]->(suspect:Account)
WHERE all(i IN range(0, size(relationships(p))-2)
          WHERE relationships(p)[i].at
                <= relationships(p)[i+1].at)
RETURN DISTINCT suspect.id
// 时序剪枝大幅收窄展开空间

案例 2:社交网络 N 度人脉:

// 场景:找「3 度人脉」(朋友的朋友的朋友)
MATCH (me:Person {id: $meId})-[:FRIEND*1..3]->(candidate:Person)
WHERE NOT (me)-[:FRIEND]->(candidate)  // 排除一度
RETURN DISTINCT candidate
// 优化:双向遍历(FRIEND 无向边)+ DISTINCT 收敛

超节点(高扇出)的处理:

- 明星节点:几百万 FRIEND 边 → 一跳即爆炸
- 策略 1:反向剪枝(从目标侧展开)
- 策略 2:限定边属性(只看高权重/近期边)
- 策略 3:超节点拆桶(见建模篇)
- 策略 4:直接排除(业务上跳过大 V)
→ 深度遍历遇超节点:先剪枝再展开

案例 3:环检测(风险控制):

// 场景:资金回流环(A→B→A)
MATCH (a:Account)-[:TRANSFER*2..6]->(b:Account)
WHERE b.id = a.id
RETURN DISTINCT a
// 注意:环 = 起点回到起点,长度 2..6

案例 4:多层依赖/血缘:

- 场景:查一个构件被哪些上层构件依赖(4 层)
- 反向展开:从目标向上游展开(方向裁剪)
- 按层级聚合:GROUP BY 第几层依赖
→ 血缘/依赖是「反向深度遍历」的典型

实践要点汇总:

- 先确认「要端点还是要路径」→ 决定写法
- 锚点唯一索引 + 关系索引 + 时序剪枝
- 遇超节点:先剪枝/拆桶/方向裁剪
- 高扇出场景优先双向遍历 / GDS
→ 深度遍历实践 = 建模 + 索引 + 剪枝三管齐下

心智:深度遍历四大实践:资金链多层穿透(时序剪枝收窄展开)、社交 N 度人脉(双向遍历 + DISTINCT 收敛)、环检测(起终点同节点 2..6 跳)、多层血缘(反向展开 + 按层级聚合);超节点是最大雷区——先剪枝、拆桶、方向裁剪或直接排除。


9. 遍历监控与调优

用 PROFILE 定位深度遍历瓶颈:

- rows 是否指数增长(哪一跳开始爆炸)
- 是否有全扫(NodeByLabelScan)而非索引
- 是否展开后过滤(Filter 在 Expand 之后)
- 内存峰值(是否物化了巨大中间结果)
→ PROFILE 逐算子看 rows 和内存

关键算子解读:

- Expand(All):无方向展开(浪费一半)
- Expand(Into):已验证存在关系(省遍历)
- NodeByLabelScan:全标签扫描(缺索引)
- NodeUniqueIndexSeek:唯一索引命中(理想)
- Filter 位置:在展开前 = 剪枝,展开后 = 后过滤
→ 目标形态:索引定位 → 剪枝式展开 → 提前收敛

遍历深度预算:

- 预估中间结果:d^k(d=平均度数,k=深度)
- 设预算:超过阈值 → 报错/降级(LIMIT/深度减小)
- 生产保护:查询超时 + 最大深度限制
- 分而治之:深遍历拆成多层浅遍历 + 物化
→ 深度遍历要「预算意识」,防一条查询拖垮库

调优流程:

1. 写查询 → 2. PROFILE → 3. 看展开/扫描/内存
4. 加索引(锚点/关系属性) → 5. 加剪枝(前置过滤)
6. 改写法(双向/多源/物化) → 7. 再 PROFILE 对比
→ 一轮一轮:每轮确认 rows 是否下降

监控指标:

- 查询耗时/超时率(深度遍历高发)
- page cache 命中率(展开是否走磁盘)
- 内存峰值(中间结果是否过大)
- 高扇出节点热点(哪些节点总被展开)
→ 建立深度遍历的专项监控

防止「慢查询拖垮库」:

- 查询超时(statement timeout)
- 深度上界硬编码(不允许无限 *)
- 资源限制:内存/CPU 配额
- 走异步/批处理:大遍历不占同步查询通道
→ 深度遍历要「结构性保护」而非事后优化

心智:深度遍历调优 = PROFILE 看三件事:rows 哪跳爆炸、是否全扫、是否展开后才过滤;理想形态是「索引定位→剪枝式展开→提前收敛」;生产必须有深度预算保护(超时 + 上界 + LIMIT + 异步),大遍历拆浅遍历 + 物化。


10. 速查表

全篇速查:

主题结论
挑战本质宽度相乘(d^k),不是跳数本身
变长路径*1..5 = 1~5 跳并集,给上下界
剪枝先过滤后遍历 + 索引锚定起点
边属性剪枝关系谓词每个 hop 生效
BFS分层、最短、吃内存
DFS省内存、全枚举
双向遍历两端各半,d⁶→2×d³
六大陷阱笛卡尔积/过滤晚/无索引/无上界/重复展开/大排序
索引锚点唯一 + 关系属性索引
物化预存可达对,空间换时间
保护超时 + 深度上界 + LIMIT + 异步

一句话记忆:深度路径遍历优化解决「大跳数指数爆炸」——难点是宽度相乘(每跳乘平均度数,5+ 跳轻松百万级中间结果)而非跳数本身;变长路径 *1..5 是 1~5 跳展开的并集,必须给上下界、能返回 DISTINCT 端点就别返回完整路径(路径笛卡尔积是头号陷阱);剪枝三件套:先过滤后遍历 + 索引锚定起点 + 关系属性作谓词(每个 hop 生效);遍历策略按需选:BFS(最短/吃内存)、DFS(省内存/全枚举)、双向遍历(两端各半在中间相遇,把 d⁶ 压成 2×d³,最强优化)、迭代加深(DFS 内存 + BFS 最短性);存储层靠无索引邻接指针直达 + 锚点/关系索引 + page cache 保超节点热点;高频固定查询用多源 BFS / 物化路径(配失效重建)空间换时间;生产必须结构性保护——查询超时、深度上界、LIMIT、异步化,用 PROFILE 看 rows 哪跳爆炸、是否全扫、是否展开后过滤,逐轮加索引加剪枝直到中间结果收敛。


延伸阅读

  • /graphdb-cypher-advanced/ — 高级路径查询与子查询
  • /graphdb-graph-query-optimization/ — 执行计划与慢查询诊断
  • /graphdb-temporal-graphs/ — 时态路径与时序遍历
  • /graphdb-graph-database-internals/ — 无索引邻接与遍历引擎
  • AI/ML 专题 — 图算法与机器学习

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「graphdb」更多文章

  1. 自定义过程与 APOC:Neo4j 过程库与 Java 扩展
  2. 知识图谱推理:规则、OWL 与推理机
  3. 子图模式挖掘:Motif、子图同构与频繁模式