站内已经把行存(PostgreSQL /
InnoDB / SQLite)、写优化
LSM(RocksDB)、文档库默认引擎(WiredTiger)和分布式
KV(FoundationDB
/ TiKV)钉在各自路径上;向量引擎 回答
ANN,db-frontier / llm-infra 回答
GraphRAG
应用层。读者仍缺一角:边与邻接是一等公民时,一次
MATCH (a)-[:R]->(b) 如何落到页、指针与 dense
节点,计划又如何被存储布局卡住。
本文是「图数据库内核」系列第 1 篇,只做三件事:
- 画出属性图相对表 JOIN、向量检索、外置图层的生态位地图。
- 用官方 store formats 与邻接代价模型钉住后续章节共用的五条坐标系,并交代学术谱系与开放争论。
- 给出与站内系列的分工,以及 16 篇阅读路线。
本文是「图数据库内核」系列第 1 篇(共 16 篇)。→ 系列目录
篇目 核心内容 第 1 篇 · 图引擎全景 生态位、主线坐标系、系列路线 第 2 篇 · 邻接的代价模型 邻接表 / CSR / 边表 / 原生指针 第 3–4 篇 · Record / Block 布局 指针链 → block.x1.db与 dense store第 9–10 篇 · Expand / Cypher 计划 遍历执行与基数坑
版本锚定:Neo4j Operations Manual(Product Version 2026.07 / current)Database internals → Store formats。Enterprise 推荐
block(BLOCK_V1/block-block-1.1,5.14 引入、5.16 GA、5.22 起默认);Community 默认aligned;standard/high_limit自 5.23 弃用。本篇以定位与路线为主,不粘贴未执行的SHOW DATABASES/PROFILE输出。
一、生态位:图引擎补哪一角
关系库用边表 + JOIN(或递归 CTE)也能「算图」;向量库能找相似;GraphRAG 能把子图塞进上下文。它们优化的瓶颈不同——格子不是互斥,而是默认把什么当一等公民。
flowchart TB
subgraph stores ["Storage paradigms"]
PG["PostgreSQL / InnoDB<br/>row store + JOIN / CTE"]
RK["RocksDB<br/>LSM KV embed"]
VE["Vector engine<br/>ANN indexes"]
GE["Native property graph<br/>adjacency + expand"]
end
subgraph apps ["Application layers"]
GR["GraphRAG / KG apps<br/>db-frontier / llm-infra"]
end
PG -.->|"deep multi-hop locality"| GE
RK -.->|"graph layer on KV"| GE
VE -.->|"topology + embedding"| GR
GE --> GR
| 形态 | 一等公民 | 一次 hop 的典型代价中心 | 站内入口 |
|---|---|---|---|
| 服务器行存 | 行 / 索引 | 边表索引查找 + JOIN | postgresql-kernel、mysql-innodb |
| LSM KV | 有序键值 | 上层编码的邻接扫描 | rocksdb |
| 向量引擎 | 向量 + 图索引(HNSW 等) | 近似邻居,非语义边遍历 | vector-engine |
| 原生属性图 | 节点 / 关系 / 属性 | 指针追逐与邻接局部性 | 本系列 |
| GraphRAG 应用 | 检索编排 | 提示词与召回质量 | db-frontier |
一句话:本系列占住「属性图拓扑一等公民、原生或近原生存储、遍历 expand 吃布局」那一格——不替代 SQL 服务器,也不重写向量 ANN 或 GraphRAG 编排。
主线引擎取 Neo4j 5.x / current 的存储与执行边界:开源生态里它把 store format、page cache、Cypher 计划绑在同一产品路径上,适合做「从格式到 hop」的连续拆解。TinkerPop / JanusGraph(外置存储上的图层)与 TigerGraph 等放在第 13–14 篇作对照,避免第 1 篇变成产品目录。
二、属性图模型:点、边、属性,以及不是什么
2.1 工作定义
本系列默认的属性图(property graph):
- 节点(node / vertex):可带零个或多个标签(label),以及键值属性。
- 关系(relationship / edge):有向、有类型(type),两端锚定节点,自身也可带属性。
- 查询:模式匹配与路径扩展(Cypher / Gremlin / 未来 GQL 子集),而不是「先投影成宽表再 JOIN」。
这与 RDF 三元组(主谓宾 + 可选图名)共享「图」一词,但存储与查询代数不同:RDF 常以三元组 / 四元组索引为中心;属性图引擎把邻接与类型过滤放进核心路径。本系列不写 SPARQL 全书——只在选型篇用一句对照,避免读者把「知识图谱」四个字直接等同于 Neo4j。
2.2 学术谱系(本系列回指)
经典定义与 survey 给出「今天生产实现从哪分叉」:
- Angles & Gutierrez(ACM Computing Surveys, 2008)等图数据模型综述把图模型从关系模型旁路出来,区分「以图为接口」与「以图为存储不变量」。
- 属性图作为工业主路径:Neo4j 等把 label / type / property 固化进记录布局;后续 GQL 标准化(ISO/IEC 39075)试图统一声明式图查询表面——本系列只在计划篇碰到语言边界,不写标准逐条注释。
- 遍历与代数:从早期图数据库遍历 API,到 Cypher 的声明式模式匹配 + 代价优化;工程争论落在基数估计在幂律图上是否系统性失效(第 10、15 篇)。
谱系收束句:模型层争论「图是否需要一等类型」;引擎层争论「邻接是否需要一等布局」。 后者才是本系列主战场。
2.3 开放争论(先立靶,后各章拆)
| 争论 | A 侧 | B 侧 | 本系列落点 |
|---|---|---|---|
| 原生图 vs 关系库 | 多跳局部性、避免反复 JOIN | SQL 生态、事务与工具链成熟;递归 CTE / 物化路径够用 | 第 2、14–16 篇:用代价模型说话,不做延迟排行榜 |
| 外置图层 vs 原生 store | 复用 RocksDB/Cassandra 运维 | 每 hop 多一层编码与缓存语义 | 第 13 篇 |
| GraphRAG 是否需要强一致图库 | 审计 / 权限边要事务 | 检索场景最终一致 + 向量即可 | 第 16 篇:按边的语义分级,不一刀切 |
三、五条坐标系(后续章节回指)
后续每一篇都应能回指其中至少一条。
3.1 邻接的代价模型
同一逻辑图可以落成:
| 布局 | 一次「取邻居」 | 可选优点 | 典型坑 |
|---|---|---|---|
| 边表(src, dst, type, …)+ 二级索引 | 索引查找 / 范围扫 | 吃 SQL 工具链 | 多跳重复探测;优化器低估扇出 |
| CSR / 邻接数组 | 偏移数组一次切片 | 只读分析极快 | 可变点/边更新贵 |
| 原生指针链(record 系) | 跟随 nextRel 等指针 |
点查起步便宜 | 指针追逐打碎局部性;超节点链表巨长 |
| Block 内联 + 动态 / dense 溢出 | 先读节点主块,溢出再树查 | 小度数点减少追逐 | 空节点也占固定主块;超节点进 dense 树 |
第 2 篇把「大 O 相同、常数与局部性不同」写成可对照的代价表;本篇只钉:图引擎的性能故事几乎总是邻接局部性故事。
3.2 Neo4j store format:record 系与 block
Operations Manual Store formats(current)给出产品级不变量(A 级):
block:Enterprise 推荐格式;用更积极的数据局部性与 inlining;属性与节点/关系共置以减少 pointer chasing;实体上限最高一档(节点 \(2^{48}\);关系与属性无手册定义的上界;关系类型 \(2^{30}\) 等,见表)。aligned:Community 默认;基于 record 系改进对齐与内存效率;属性访问仍走「链表式」图数据布局叙述。standard/high_limit:自 5.23 弃用;计划在 vNext.LTS(手册写明目标约 2026-11)后仍支持一段时间,支持窗口延伸至约 2029-11;Enterprise 应尽早迁到block。high_limit在下一 LTS 之后将无法启动遗留 store——迁移窗口是运维硬约束,不是口味问题。
Block 主文件直觉(同页 Store
formats 对 block.x1.db
的描述):创建节点时在 offset \(=
128\,\mathrm{B} \times \mathrm{nodeId}\) 分配
128 B 主块,内含两个 64 B
记录——节点侧(标签与节点属性)与关系侧(该点上的关系数据)。手册给出数量级直觉:常见数据下大约可塞入量级为「约
10 个标签、6–7 个属性、最多约 5
条关系」——实际条数随类型与体积剧烈变化;无数据时块仍占
128 B。装不下则进动态 store;关系过多再进 dense
store(多根世代 B+
树,按类型与方向组织;同一类型关系不跨 store 拆散)。
record 系(aligned / legacy):node record → rel chain → property chain
(指针多,格式故事好讲,局部性受链长支配)
block: nodeId → 128B x1 主块(尽量内联)
→ node/rel 动态块
→ relationship dense 树(超节点)
第 3 篇必须仍讲清 record 指针链——线上大量库仍是
record-aligned-*,且 block
的「减少追逐」只有对照旧路径才有意义。第 4 篇专钉 block 与
dense。
版本标识示例(手册验证命令语义,非本机输出):SHOW DATABASES YIELD name, store
可能看到 block-block-1.1 与
record-aligned-1.1 并存;system
库在示例中可为 aligned。以你环境为准。
3.3 Page cache 与指针追逐
图查询很少是「一次索引探到一行结束」:expand 会沿着关系读出一批记录/块。于是:
- 热邻域落在少量页上时,page cache 把原生图的优势放大;
- 超节点(dense)把「一个点」变成「一棵关系树 / 一条长链」,缓存命中模型接近「扫索引」而非「追三条指针」;
- record 系的 property 链与 relationship 链会放大随机读;block 把中小度数点的属性/关系推进主块,换的就是更少的 page fault(手册对 block 的明确主张)。
第 5 篇把这条写成 I/O 与缓存计量口径;第 15 篇把它收成排障清单。
3.4 索引:起点过滤器,不是遍历引擎
标签扫描、关系类型、属性索引与约束决定从哪里开始走,很少能替代深度 expand:
- 索引解决「找到符合谓词的候选点/边」;
- 遍历解决「从候选出发沿拓扑扩张」;
- 二者交界处的经典事故:用属性索引取出超大起点集合,再做变长路径——计划每个算子都「合理」,乘积爆炸。
全文与向量属性(第 8 篇)只作边界:链到 search-engine / vector-engine,不在图系列内重写倒排或 HNSW。
3.5 Expand 与 Cypher 计划边界
执行骨架(细节在第 9–10 篇):
- 产生起点(label scan / index seek / 参数点)。
- Expand(按类型、方向、或许属性谓词过滤邻居)。
- 变长路径 / 最短路径等变体改变搜索策略与内存结构。
- 过滤器、投影、聚合接在拓扑算子前后。
计划层的开放问题不是「会不会选
IndexSeek」,而是:幂律度数下 cardinality
是否可信。低估扇出 → 选了 nested expand;高估 →
过度物化。第 10
篇用算子语义与(若有环境)PROFILE
钉案例;无环境则不写伪造行数。
四、与站内系列的分工
| 主题 | 站内已有 | 本系列 |
|---|---|---|
| MVCC / 行版本 | mvcc、InnoDB / WT / PG | 第 11 篇只写图特有锁与关系创建边界 |
| LSM / 外置存储 | rocksdb | 第 13 篇 JanusGraph 后端对照 |
| 向量 ANN | vector-engine | 第 8、16 篇接口句 |
| GraphRAG | db-frontier、llm-infra | 第 16 篇:哪些边要强一致图库 |
| SQL 计划 | query-engine、PG 系列 | 第 10 篇借用「基数 / 算子」词汇,不重写 CBO 全书 |
五、16 篇地图与阅读路径
| 部分 | 篇目 | 一句话 |
|---|---|---|
| 模型与存储 | 01–06 | 从生态位到邻接、record/block、cache、写入 |
| 索引查询事务 | 07–12 | 索引、全文/向量边界、expand、计划、锁、HA 边界 |
| 对照与收束 | 13–16 | TinkerPop/JanusGraph、TigerGraph 对照、排障、选型与 GraphRAG |
推荐路径见 系列 index 第二节。最小闭环:01 → 02 → 03 → 04 → 05 → 09 → 16。
六、来源、实验与研究台账(本篇)
6.1 来源台账
| 结论 | 等级 | 来源 |
|---|---|---|
block / aligned
默认与弃用时间线、实体上限 |
A | Neo4j Operations Manual Store formats(current / 2026.07) |
block.x1.db 128 B 主块、64 B×2、动态与
dense store 结构 |
A | 同上,block 文件布局节 |
| 属性图模型谱系 | A/B | Angles & Gutierrez, ACM CSUR 2008;后续 GQL 标准作语言边界 |
| GraphRAG 应用层不在本系列展开 | 分工 | 站内 db-frontier / llm-infra |
6.2 实验台账
| 实验 | 状态 |
|---|---|
本机 SHOW DATABASES YIELD name, store |
未跑;正文只引用手册示例形态 |
PROFILE 多跳查询 |
留第 10、15 篇;未跑不写行数 |
6.3 研究台账
| 字段 | 内容 |
|---|---|
| 核心问题 | 属性图是否需要原生邻接布局,才能把多跳代价从「反复 JOIN」变成「局部 expand」? |
| 奠基 | Angles & Gutierrez(CSUR 2008)图数据模型;Neo4j store formats 作为工业布局规范 |
| 争论 | 原生图 vs SQL/CTE;外置图层 vs 原生 store;GraphRAG 对一致性的真实需求 |
| 开放问题 | 幂律图上的基数估计;block 与开源版能力随版本变化的产品边界 |
| 未写 | GNN、RDF 全书、Aura 定价、跨引擎延迟榜 |
七、小结
- 生态位:本系列补「拓扑一等公民」的存储与遍历内核,上接 GraphRAG,旁路向量与行存,不吞掉它们的职责。
- 坐标系:邻接代价 → store format(record → block)→ page cache → 索引起点 → expand/计划。
- 版本事实:Enterprise 走向
block;standard/high_limit已弃用并有明确支持终点;Community 仍以aligned为默认——读盘前先看store字段。 - 下一篇:把邻接表、CSR、边表 JOIN 与原生指针写成同一套代价语言,为第 3–5 篇的指针与页故事垫底座。
→ 系列目录 · 下一篇:邻接的代价模型
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【图数据库内核】属性图 · Store Format · Expand · Cypher 计划边界
补齐站内缺失的图拓扑存储与遍历内核:属性图模型、Neo4j record/aligned/block 布局、page cache 与 dense 节点、标签索引、Expand 与 Cypher 计划边界,以及 TinkerPop/JanusGraph 对照与 GraphRAG 接口。
【图数据库内核】邻接的代价模型:边表 JOIN、CSR 与原生指针为何不是同一件事
把同一逻辑图落成边表+索引、CSR、原生关系链与 Neo4j block 内联四条路径,用统一代价语言比较一次 hop 与 k 跳扩张;钉住幂律超节点与局部性,为后续 record/block 布局篇垫底座。
【图数据库内核】Neo4j Block format:128 B 主块、动态溢出与 dense B+ 树
对照第 03 篇 record 指针链,钉住 Enterprise 推荐的 block 布局:block.x1.db 128 B 主块、node/relationship 动态 store、big_values、relationship dense 多根 B+ 树与实体上限;重写一次 hop 的读路径。
【图数据库内核】遍历与 Expand:从索引起点走到邻居的执行骨架
拆解 Cypher 遍历算子 Expand(All/Into)、OptionalExpand、VarLengthExpand(All/Into/Pruning)与最短路径双向 BFS;钉住路径爆炸、DISTINCT 剪枝、量化路径谓词,以及 expand 如何吃第 03–05 篇的 store 与 page cache。