【图数据库内核】TinkerPop / JanusGraph:外置邻接表图层,不是第二套原生 store

💡 原文中文,约8000字,阅读约需20分钟。
📝

内容提要

本文介绍JanusGraph基于TinkerPop接口,将图数据存储于Cassandra等宽行后端,采用邻接表布局,边双写,事务非必然ACID,依赖后端一致性。对比Neo4j原生存储,JanusGraph牺牲局部性换取可扩展性,用vertex-centric索引应对超节点,适合已有宽表集群的场景。

🔎

延伸解读

邻接表布局的代价差异

JanusGraph 将图数据以邻接表形式存入宽行后端,每个顶点对应一个宽行,边双写以支持双向遍历。一次 hop 的代价包括定位宽行、按列范围取边、解码对端 id,分布式下 RPC 和一致性级别主导常数项。与 Neo4j 原生存储相比,JanusGraph 牺牲局部性换取可扩展性,适合已有宽表集群的场景。

事务与一致性的后端依赖

JanusGraph 事务并非必然 ACID,在 Cassandra/HBase 上依赖后端一致性,最终一致后端需使用 LOCK 或 FORK 机制处理冲突。LOCK 机制在提交时加锁并重读校验,但锁贵且易争用;FORK 机制通过删旧加新产生多副本,读时消解,但可能产生临时不一致。应用需接受 half-edge 和 ghost vertex 等修复路径。

超节点应对策略

JanusGraph 提供 Graph index(全局)和 Vertex-centric index(局部)两类索引,后者用于缓解超节点问题。Vertex-centric index 在顶点邻接表内按标签和属性条件切子集,避免扫描整行 cell。与 Neo4j 的 dense 节点自动升级不同,JanusGraph 需要建模时显式配置,预判哪些标签或属性可能打爆度数。

Q&A

JanusGraph 是如何存储图数据的?

JanusGraph 使用邻接表布局,将图数据存储在支持 Bigtable 模型的宽行后端(如 Cassandra、HBase)中。每个顶点对应一个宽行,行内每个边或属性存储为一个 cell,按列有序排列。每条边会存储两次,分别放在两个端点的邻接表中,以支持双向遍历。

JanusGraph 的事务是 ACID 的吗?

JanusGraph 的事务不一定是 ACID 的。在 BerkeleyDB 上可以配置为 ACID,但在 Cassandra 或 HBase 上通常不是,因为底层存储系统不提供可串行化隔离或多行原子写。事务的隔离和原子性取决于后端,在最终一致后端上需要使用 LOCK 或 FORK 机制来处理冲突。

JanusGraph 如何解决超节点问题?

JanusGraph 通过 vertex-centric index(关系索引)来缓解超节点问题。这种索引在顶点邻接表内按标签和属性条件进行子集检索,避免扫描整个宽行。它需要在建模时显式创建,与 Neo4j 的自动 dense 节点处理不同。

JanusGraph 与 Neo4j 在存储和性能上有什么主要区别?

JanusGraph 将图数据存储在外置宽行后端,采用邻接表布局,边双写,牺牲局部性换取可扩展性;Neo4j 使用原生存储,将关系记录和属性存储在页缓存中,强调局部性。JanusGraph 的 hop 代价受分布式 RPC 和一致性级别影响,而 Neo4j 更依赖页缓存。JanusGraph 适合已有宽表集群的场景,Neo4j 适合需要强一致性和高性能单机遍历的场景。

JanusGraph 支持哪些索引类型?

JanusGraph 支持两类索引:Graph index(全局索引)和 Vertex-centric index(局部索引)。Graph index 又分为 Composite 和 Mixed 两种,Composite 用于等值查询,Mixed 需要外部索引后端(如 Elasticsearch)支持更灵活的谓词。Vertex-centric index 用于优化高度数顶点上的局部遍历。

JanusGraph 在最终一致后端上如何处理数据一致性?

在最终一致后端(如 Cassandra)上,JanusGraph 提供两种机制:ConsistencyModifier.LOCK 在提交时加锁并重读校验,但成本高且易争用;ConsistencyModifier.FORK 在修改边时创建新副本,读时消解冲突。此外,可能出现临时不一致、half-edges 和 ghost vertices,需要应用层处理。

TinkerPop 在 JanusGraph 中扮演什么角色?

TinkerPop 是 Apache 提供的属性图接口和 Gremlin 遍历机。JanusGraph 原生支持 TinkerPop 的 Structure 和 Process API,通过 Gremlin 进行图遍历。TinkerPop 提供了可移植的接口,使得 JanusGraph 可以适配不同的存储后端,但 JanusGraph 的存储实现是独立的,不是基于 Neo4j 的。

🏷️

标签

➡️

继续阅读