土法炼钢兴趣小组的算法知识备份

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

文章导航

分类入口
databasegraph
标签入口
#graph-database#tinkerpop#gremlin#janusgraph#adjacency-list#bigtable#cassandra#hbase#vertex-centric-index#eventual-consistency

目录

前 12 篇把 Neo4j 原生 store 的 hop、页缓存、锁与集群可见性钉成一条链。本篇换坐标:Apache TinkerPop 提供可移植的属性图接口与 Gremlin 遍历机;JanusGraph 把图落在支持 Bigtable 模型的外置存储上。要回答的不是「Gremlin 怎么写」,而是:邻接如何编码进宽行、一次 hop 付什么后端代价、事务与超节点故事和原生图差在哪。

本文是「图数据库内核」系列第 13 篇(共 16 篇)。→ 系列目录

篇目 核心内容
第 2 篇 · 邻接代价 边表 / CSR / 指针 / block
第 13 篇 · TinkerPop / JanusGraph 接口层 + 外置邻接表
第 14 篇 · 引擎对照 TigerGraph 等选型句

版本锚定:Apache TinkerPop Reference(current)structure / process 分工与 Gremlin Server;JanusGraph 文档 IntroductionData modelTransactionsEventually-Consistent Storage BackendsIndexing for Better Performance(以 docs.janusgraph.org 现行页为准)。不粘贴本机 Gremlin Console / Cassandra 集群输出,不做跨引擎延迟排名。


一、先分清两层:TinkerPop ≠ 某一种磁盘格式

TinkerPop 文档把自己的角色写成:为图提供者与用户提供 structure / process 接口;实现了这些接口的系统称为 TinkerPop-enabled,彼此差异主要在时空复杂度,而不是另一套查询语言方言。

组件 谁用
Structure API GraphVertexEdgeVertexPropertyPropertyTransaction 主要给图系统实现者;终端应用应少直接依赖
Process API Gremlin / Gremlin Traversal Machine 终端用户的遍历入口
部署形态 嵌入式 JVM、Gremlin Server、Remote Gremlin Provider 决定元素是「完整对象」还是远程 reference(常仅 id+label

属性图形式定义与本系列一致:有向、带属性的 multi-graph;顶点可有 multi-propertiesmeta-propertiesVertexProperty 既是 Property 又是 Element)。Gremlin 是函数式数据流:map / filter / sideEffect 步组合;同一套步可走 OLTP 遍历引擎,也可在支持 GraphComputer 的实现上走 OLAP(如 Spark 路径)——本篇只关心 OLTP 存储代价,不写 VertexProgram 全书。

JanusGraph 在栈中的位置:原生支持 TinkerPop 属性图模型与 Gremlin;持久化交给可插拔 storage backend(文档列举 BerkeleyDB JE、Cassandra/CQL、HBase 等),索引可另挂 Elasticsearch / Solr / Lucene。它不是「再实现一套 Neo4j block 文件」,而是图层 + 外置宽行存储

flowchart TB
  App["Application / Gremlin"]
  TP["TinkerPop process API"]
  JG["JanusGraph transaction + cache"]
  Store["Storage backend<br/>Cassandra / HBase / BerkeleyJE"]
  Idx["Optional index backend<br/>ES / Solr / Lucene"]
  App --> TP --> JG
  JG --> Store
  JG --> Idx

与第 01 篇争论表对齐:外置图层复用分布式 KV/宽表运维;每 hop 多一层编码、缓存与一致性语义。


二、JanusGraph 落盘:邻接表进 Bigtable 宽行

2.1 布局不变量(文档 Data model

  1. 图以 adjacency list 存储:每个顶点带一份邻接表,内含该点的关联边与属性
  2. 每条边存两次——两端顶点的邻接表各一份;换局部遍历,付双写与双删。
  3. 邻接表按边标签的 sort key / sort order 排序,以便用 vertex-centric index 做子集检索。
  4. 后端须支持 Bigtable 数据模型:行由 key 标识;行内大量 cell(column + value);cell 按 column 有序,且能高效取 column range。

物理映射:

边 cell 的列字节序编码标签 id(末位区分入/出边)、sort key、邻接点 id 的差分、边 id;值侧放 signature 属性与其余属性序列化。这是压缩后的有序邻接切片,不是 Neo4j 的 nextRel 指针链,也不是全局 CSR 数组。

2.2 一次 hop 的代价语言(接第 02 篇)

从已知顶点 \(v\)、标签/方向 \(t\) 取邻居,直觉路径是:

  1. 用 vertex id 定位宽行(可能跨网络到 Cassandra/HBase);
  2. 在有序 cell 上按 column range(或 vertex-centric index)取出 \(E(v,t)\)
  3. 解码对端 id,必要时再取对端行上的属性。

[ {}(v,t) C{}(v) + C_{}|E(v,t)| + C_{}_{} ]

与本系列其它布局对照(方法模型,非实测):

布局 邻接落点 边是否双写 超节点压力
Neo4j record 链 指针链 / group 单份关系记录 + 双向链指针 链长 → dense 树(第 03–06 篇)
Neo4j block x1 内联 → 动态 → dense 原生关系存储 类型级 dense B+ 树
JanusGraph 顶点宽行内有序 cell 显式双写 行宽 / cell 上限 + 需 vertex-centric 索引
边表 + SQL 索引 二级索引范围 通常单行一边 索引扇出与 JOIN

\(C_{\mathrm{row}}\) 在嵌入式 BerkeleyJE 上接近本地 JE 读;在 CQL/HBase 上含 RPC 与副本一致性级别。「邻接表」名字相同,常数项可以差一个数量级以上——选型时看的是这一项,不是大 O。

文档对 BerkeleyJE 的定位值得单独记下:与 JanusGraph 同 JVM、本地盘;频繁访问元素宜进内存;实用上限量级写在「商品硬件上约千万至亿级顶点」——适合测试与中小图,不是分布式主路径。生产扩展叙事落在 Cassandra / HBase(及配置里的 Scylla 等 shorthand)。


三、事务:TinkerPop 壳 + 后端决定的「是否真 ACID」

JanusGraph Transactions 开宗明义:

JanusGraph transactions are not necessarily ACID. They can be so configured on BerkeleyDB, but they are not generally so on Cassandra or HBase, where the underlying storage system does not provide serializable isolation or multi-row atomic writes and the cost of simulating those properties would be substantial.

对照第 11 篇 Neo4j:默认 read-committed + 写锁 + WAL,仍是单库引擎内的 ACID 故事。JanusGraph 把「事务」接到 TinkerPop 语义(首操作开事务、commit/rollback 显式结束;元素绑定事务作用域;边默认不自动跨事务迁移),但隔离与原子性随 backend 降级

最终一致后端上的增量机制(Eventually-Consistent Storage Backends):

机制 作用 代价 / 风险
ConsistencyModifier.LOCK 对有一致性约束的 schema 元素在提交时加锁、重读校验 默认可关;锁贵、易争用;文档警告非覆盖所有故障(如 Cassandra 低于 quorum)
ConsistencyModifier.FORK(MULTI 边) 改边时删旧加新,冲突变多副本,读时消解 写便宜;读侧要能处理分叉
临时不一致 事务部分可见、陈旧索引、half-edges(只持久化一边方向) 固有于异步传播;建议后端支持 batch write atomicity,且单事务修改量 \(<\) buffer-size
Ghost vertices 删点与并发修改交错后顶点「幽灵复活」 存在性双检、定期修复作业、软删除等缓解

CAP 叙述(JanusGraph Introduction):同一图层下,Cassandra 偏可用性(答案完整度可能受损),HBase 偏一致性(完成请求的概率可能受损),BerkeleyJE 非分布式、多用于探索。这与第 12 篇 Neo4j primary Raft 多数 + secondary 异步是另一条产品契约——不要把 bookmark 因果链和 Cassandra 最终一致 pen 混成一种「集群」。


四、索引:全局找起点 vs 顶点内切超节点

文档 Indexing for Better Performance 区分两类:

4.1 Graph index(全局)

无索引时全局属性查找会退化为全图扫描;生产可开 force-index 禁止扫图。这与第 07 篇 Neo4j「标签/属性索引是起点过滤器」同构,但 mixed 索引把 Lucene/ES 旁路抬成一等配置——心智接近第 08 篇的「语义索引旁路」,只是 JanusGraph 把它写进常规索引故事而非例外。

4.2 Vertex-centric index(关系索引)

针对高度数顶点上的局部遍历:在已定位的顶点邻接表内,按标签 + 属性条件切子集,缓解 infamous super node。邻接表的全局有序 + sort key,正是为这类索引服务。

对照 Neo4j dense(第 04–06 篇):

Neo4j dense JanusGraph vertex-centric
触发 关系数过阈值,布局升级为类型向 B+ 树 Schema 上显式建关系索引;遍历时走索引而非扫整行 cell
存储 原生 store 内 仍在该顶点宽行(及索引结构)语义下
运维含义 引擎自动升/降路径(见写入篇) 需要建模时预判哪些标签/属性会打爆度数

两边都承认:超节点不是靠「换个查询语言」消失的;要么改布局/索引,要么改图模型(拆点、边类型细分)。


五、和 Neo4j 主线并排放时,看什么

用五条坐标系快速对照(不写选型结论——留给第 14–16 篇):

  1. 邻接局部性:Neo4j 争 page cache 内指针/块共置;JanusGraph 争「一行内有序 cell + 尽量少的远端行读」。分布式下后者的 \(C_{\mathrm{remote}}\) 常主导。
  2. 写入放大:JanusGraph 边双写;最终一致下还有锁协议或 FORK 副本。Neo4j 关系记录单份,但两端链/块维护与 dense 转换有自己的放大(第 06 篇)。
  3. 隔离默认值:Neo4j 文档化的 RC + 锁表;JanusGraph 明确「不必然 ACID」,一致性是按 schema 元素配置的加价项
  4. 查询表面:Cypher 计划/基数(第 09–10 篇)vs Gremlin 步流 + 后端 multiQuery/batch 等调优旋钮;可移植性是 TinkerPop 的卖点,也意味着更少绑定某一 store 的深度计划叙事
  5. 运维边界:JanusGraph 把 Cassandra/HBase/ES 的运维面暴露给图平台;Neo4j 集群是第 12 篇的库拓扑。选 JanusGraph 常常是「已有宽表集群,要图层」,不是「只要一个更省事的图安装包」。

六、刻意不展开

话题 立场
Gremlin 步库教程、GLV 语法 官方 Reference;本系列不重写
Cassandra/HBase 集群安装与调优 各后端手册
Hadoop-Gremlin / SparkGraphComputer 分析全书 只承认 OLAP 入口存在
Neo4j 的 Gremlin 插件或其它 TinkerPop 实现逐一评测 不测不排名
TigerGraph、PG 递归 CTE 对照句 第 14、16 篇

七、争论与开放问题

  1. 可移植图层 vs 原生 store 深度:A 侧用 TinkerPop 避免绑定;B 侧用原生布局与计划器吃尽局部性。JanusGraph 证明「邻接表 + 宽行」能扩到很大图,但不自动复制 Neo4j block 的页内共置故事。
  2. 最终一致上的图不变量:双写边 + half-edge + ghost vertex 迫使应用承认修复路径(定期作业、软删、存在性检查)。这与「图 = 强一致关系网」的产品直觉冲突——文档把它写进运维前提,而不是脚注。
  3. 开放问题:vertex-centric 索引与混合索引在幂律负载下的维护成本;multiQuery 批量化能在多大程度上抹平逐 hop RPC;图层缓存(transaction / db cache)与后端行缓存如何叠加才不致读到系统性陈旧——公开基准很少按「跳数 × 后端一致性级别」正交报告。

谱系:属性图工业接口经 TinkerPop 标准化(Gremlin 遍历机);存储层谱系接 Bigtable / 宽行有序 cell(Chang et al. 一脉的工程后代),与 Neo4j 从记录记录引擎长出的原生图文件是分叉而非父子。Angles & Gutierrez 的模型层争论在此落地为:同一属性图接口下,邻接是否一等布局——以及一等布局是否必须独占磁盘格式。


八、来源与实验台账(本篇)

结论 等级 来源
Structure/process 分工、属性图与 VertexProperty、Gremlin Server / 远程 reference A TinkerPop Reference(current)
大规模、TinkerPop 原生、Cassandra/HBase/BerkeleyJE 与 CAP 简述 A JanusGraph Introduction
邻接表、边双写、Bigtable 行/cell、边编码 A Data model
事务非必然 ACID;作用域与配置项 A Transactions
LOCK/FORK、临时不一致、half-edge、ghost vertex A Eventually-Consistent Storage Backends
Graph index vs vertex-centric;composite/mixed A Indexing for Better Performance
跨引擎 hop 延迟对比 未跑 不写排名数字

九、小结

  1. TinkerPop 是接口与遍历机;JanusGraph 是外置宽行上的邻接表实现——不要当成另一种 Neo4j store format。
  2. 边双写 + 有序 cell 换局部遍历;分布式下 \(C_{\mathrm{row}}\) / RPC 常主导 hop 常数。
  3. 事务与一致性跟 backend 走;最终一致路径要显式 LOCK/FORK,并准备 half-edge / ghost 类修复。
  4. Vertex-centric index 是超节点主武器,对位 Neo4j dense,但是建模期配置而非同一套自动升格叙事。
  5. 下一篇:把 TigerGraph 等引擎收成对照句,服务选型,不做延迟排行榜。

系列目录 · 上一篇:HA 边界 · 下一篇:引擎对照(待发布)

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。


By .