第 01 篇把「索引」钉成坐标系之一:决定从哪里开始走,很少替代深度 expand。 本篇把该句落到 Neo4j 5 的具体对象——token lookup、RANGE/TEXT/POINT、关系属性索引,以及唯一/存在/类型/KEY 约束——并接到第 06 篇的写放大。全文与向量索引只留接口,展开在第 08 篇。
本文是「图数据库内核」系列第 7 篇(共 16 篇)。→ 系列目录
篇目 核心内容 第 6 篇 · 写入路径 建边、dense、ID 复用 第 7 篇 · 索引与约束 LOOKUP / RANGE / TEXT / POINT;约束 第 8 篇 · 全文与向量边界 Lucene / vector;不重写 ANN 全书 第 9–10 篇 · Expand / 计划 索引起点之后发生什么
版本锚定:Cypher Manual 5(与站内 Neo4j 5.x / current 产品线对齐)Indexes → Search-performance indexes、Constraints。运维侧:Operations Manual Memory configuration(native 索引进 page cache;Lucene / 向量不在其中——第 05、08 篇)。不粘贴未执行的
PROFILE行数;算子名以手册示例为准。
一、先立边界:索引解决什么、不解决什么
| 解决 | 不解决 |
|---|---|
| 按标签/类型找到候选实体集合 | 把 \(k\) 跳拓扑扩张变成「一次索引查找」 |
| 按属性谓词缩小起点(或边集合) | 超节点某一类型上百万边的输出基数 |
| 唯一性等完整性(约束) | 基数估计在幂律图上天然可信(第 10 篇) |
一次典型模式匹配的代价仍接近:
[ {}() + {}^{k} + _{} ]
索引只硬砍第一项,并间接影响计划是否敢选某条路径。若 \(\mathrm{Cost}_{\mathrm{entry}}\) 产出的 \(|S|\) 仍然巨大,后面的 expand 照样爆炸——这是「计划每个算子都合理、乘积不合理」的常见入口。
二、Token lookup:数据库自带的标签/类型索引
Cypher Manual:创建库时默认存在两个 token lookup index——节点标签 lookup 与关系类型 lookup。它们只解决标签 / 关系类型谓词,不解决任何属性谓词。
| 有 lookup | 无 lookup(若被删) |
|---|---|
计划可用 NodeByLabelScan
等,只碰该标签节点 |
手册示例:退化为
AllNodesScan,先读全库节点再过滤 |
手册原话级警告:删除 token lookup 会导致严重性能退化;它们也帮助其它索引的填充。非平凡库上「只靠 lookup」不够——因为业务过滤几乎总在属性上——但没有 lookup 则连「按标签起步」都贵。
与第 03 篇对照:标签在节点记录里另有字段/动态记录;lookup
是为扫描准备的二级结构,不是「读节点时顺便得到的免费列表」。关系类型同理:store
里每条 Rel 带 type token,lookup 加速「找出所有
[:R]」类入口,不替代邻接 expand。
三、搜索性能索引四型
手册把加速精确检索的索引称为 search-performance indexes,四类:
| 类型 | 创建要点 | 主要解决 |
|---|---|---|
| RANGE | CREATE INDEX 默认即
range;可建在节点或关系上;支持复合属性 |
大多数谓词:等值、范围、STARTS WITH、排序借用等 |
| TEXT | CREATE TEXT INDEX;默认 provider
text-2.0(trigram,5.1+) |
STRING 上的 CONTAINS /
ENDS WITH;与 range 并存时这两类算子优先
text |
| POINT | CREATE POINT INDEX |
距离、bounding box 等空间谓词 |
| LOOKUP | 见上;一般勿删;可用 CREATE LOOKUP INDEX
重建 |
仅标签/类型 |
3.1 RANGE:默认主力
- 单属性与复合属性均可;复合索引要求查询谓词覆盖索引定义的属性集合(手册:须过滤复合索引中的全部属性才使用该复合索引);创建时属性顺序影响计划如何拆谓词。
- 节点与关系皆可:
FOR (n:Label) ON (n.prop)或FOR ()-[r:TYPE]-() ON (r.prop)。 - 适合高基数属性(唯一 ID、用户名、时间戳等)——手册 heuristics 原句方向。
3.2 TEXT 与 RANGE 分工
同一 STRING 属性上可同时存在 range 与
text。手册规则:
- 规划器对
CONTAINS/ENDS WITH倾向 text; - 其它情况倾向 range(精确匹配、前缀等 range 更擅长的形态);
- Text 仍是精确子串类匹配语义;模糊、打分、分词检索走 full-text(第 08 篇),不要把 TEXT INDEX 当成搜索引擎。
text-2.0 用 trigram 切分(手册举例
"developer" →
dev/eve/vel/…)——解释为何 CONTAINS
能走索引而不必全表扫字符串。
3.3 POINT
专用于 POINT 属性的距离与 bbox
类谓词。规划器需能推断属性为 point
类型才会用它;仅有松散谓词时可能退回 label
scan(手册有「text/point 需能排除非兼容类型」的讨论)。存在
property type
constraint(EE,5.9+)时可帮规划器确立类型。
3.4 自动选用与提示
多索引可用时,规划器选自认最便宜者;也可用
USING 提示强制(手册 Index
hints)。强制错索引会把「起点过滤器」变成「起点误伤」——第
10 篇再谈。
四、约束:完整性,兼带索引副作用
Cypher Manual 5 约束一览:
| 约束 | 作用 | Edition |
|---|---|---|
| Property uniqueness | 指定标签(或关系类型)上,属性(组合)唯一 | 节点与关系(关系唯一 5.7+) |
| Property existence | 属性必须存在(IS NOT NULL) |
Enterprise |
| Property type | 属性必须为给定类型 | Enterprise,5.9+ |
| KEY(NODE KEY / RELATIONSHIP KEY) | 存在 ∧ 唯一(组合) | Enterprise;关系 KEY 5.7+ |
4.1 唯一性细节
- 语法形态:
REQUIRE n.prop IS UNIQUE(及复合括号列表)。 - 未具备全部约束属性的实体不受唯一性约束——缺属性的节点/关系不参与该规则(手册原文)。这与「关系库 NOT NULL + UNIQUE」心智不同:Neo4j 唯一约束默认允许「没有这个属性」。
- 需要「必须有且唯一」时用 NODE KEY / RELATIONSHIP KEY(EE),或唯一 + 存在两条约束组合。
4.2 索引后备(写路径含义)
创建唯一性 / KEY 类约束会带上后备索引(同 schema 上通常不能再单独建同型重复索引——历史手册与创建冲突规则一致;以当前版本文档的冲突提示为准)。后果直接对接第 06 篇:
- 每次写入命中约束属性 → 维护索引;
- 过索引与过约束一样惩罚写入;
- 读侧则多了一个高选择率入口(唯一键查找)。
存在性约束不像唯一约束那样以同样方式「顺带当查找索引」——手册写明 existence 目前不按同一方式 leverage 索引使用;其价值在写入期拒绝坏数据。
4.3 Community / Enterprise 边界
存在、类型、KEY 在手册标为 EE。含这些约束的库不能用 Community 打开(4.x 手册已有同类警告;迁移时按 edition 规划)。本系列正文区分能力边界,不做版本营销。
五、和存储 / 缓存的接口
| 结构 | 落点(运维口径) |
|---|---|
| Token lookup、RANGE 等 native 索引 | 计入 data and native indexes;规划进 page cache(第 05 篇) |
| Full-text(Lucene) | 不进 Neo4j page cache;与剩余 OS/堆外内存谈判 |
| Vector index | 手册:加载在 OS 内存侧,不在 page cache(第 08 篇) |
因此「给图查询加索引」会:
- 增加 store 侧体积(手册 over-indexing:索引是主数据的二级拷贝,空间上近似再占一份被索引数据);
- 推高
server.memory.pagecache.size的合理下界; - 拖慢写路径(每写更新索引)。
SHOW INDEXES(5.8+
相关列)可用 lastRead / readCount
/ trackedSince 抓闲置索引,再
DROP INDEX——手册推荐的反过索引手段。
六、经典事故:索引起点 × 深度遍历
6.1 高召回入口
索引求出 |S| = 50万起点
→ 每点 expand 平均 f = 20
→ 一层就百万级中间行
索引「很成功」(避免了 AllNodesScan),查询仍可能跑爆。杠杆在:收紧谓词、限制变长路径、换建模,而不是再加一个更宽的索引。
6.2 低基数属性上的索引
手册 heuristics
偏向高基数属性。低基数(布尔、状态枚举)上的
range 可能选出巨大起点,计划看起来用了
NodeIndexSeek,实质接近标签扫描。此时索引的存储与写成本几乎白付。
6.3 复合索引属性顺序
创建 (a,b,c) 与查询只提供 c
时,复合索引可能根本用不上(须覆盖全部属性的规则)。错误顺序会导致「建了却不见
seek」。用 EXPLAIN/PROFILE
核对算子(有环境再贴;第 10 篇)。
6.4 与邻接布局的分工
| 组件 | 职责 |
|---|---|
| LOOKUP / 属性索引 | 选出 \(S\) |
| Record 链 / block 主块与 dense 树 | 从 \(S\) 走出邻居 |
| 计划器 | 猜测 \(|S|\) 与扇出——幂律下易错 |
索引再强,也要把第 03–05 篇的 hop 成本算进总账。
七、Heuristics(手册口径 + 本系列补一句)
手册 Heuristics: deciding what to index 要点:
- 频繁用于过滤/匹配的属性;
- 已确认的慢查询瓶颈属性;
- 高基数属性;
- 复杂多跳且多层过滤时,给过滤属性加索引;
- 用实验对比有无索引——不要凭感觉堆。
手册 Over-indexing:空间接近翻倍被索引数据;写变慢。本系列补一条图特有的:
- 先问入口选择性,再问跳数——选择性差的索引对多跳查询是负资产。
八、学术与工程谱系(短)
- 二级索引加速选择:与关系库 B-Tree/LSM 二级索引同构;图引擎的增量在于入口之后是拓扑算子而非更多 JOIN 节点(第 02、09 篇)。
- Token 与属性分离:标签/类型走 lookup、属性走 range/text/point——承认「图式过滤」与「键值过滤」不是同一访问路径。
- 争论:应用层「先建齐所有属性索引」vs「按
readCount裁剪」。写多读少的摄取管道与读多写少的查询服务答案相反;用第 06 篇写放大与本节 over-indexing 对齐,而不是品牌默认。 - 开放问题:幂律下入口基数估计(第 10 篇);约束与 graph type(Cypher 25 方向)如何改变「开放属性图」的工程默认——本篇不展开 25 全书。
九、来源与实验台账(本篇)
| 结论 | 等级 | 来源 |
|---|---|---|
| 四类 search-performance 索引职责;默认 LOOKUP;删 LOOKUP 的后果 | A | Cypher Manual 5 Search-performance indexes / using indexes |
| RANGE/TEXT 分工;trigram text-2.0;复合索引全属性与顺序 | A | 同上 Create indexes / using indexes |
| 约束类型与 EE 边界;唯一性对缺属性实体的豁免 | A | Cypher Manual 5 Constraints / Create constraints |
过索引空间与写代价;lastRead/readCount(5.8+) |
A | using indexes → Over-indexing |
| Native vs Lucene/向量内存分工 | A | Operations Manual Memory configuration(经第 05 篇) |
| 本机 PROFILE 对比有无索引 | 未跑 | 不贴伪造 DB hits |
十、小结
- LOOKUP 保标签/类型入口;RANGE/TEXT/POINT 保属性入口;都不是遍历引擎。
- 唯一/KEY 约束带索引后备,把完整性与写放大绑在同一条绳上;存在/类型约束偏 EE 的形状治理。
- 过索引有价:空间、写入、错误的低基数入口。
- 下一篇:全文(Lucene)与向量索引如何接在属性旁边,以及为何不应塞进 page cache 叙事。
→ 系列目录 · 上一篇:写入路径 · 下一篇:全文与向量边界
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【图数据库内核】图引擎全景:属性图作为一等公民的存储与遍历
定位属性图相对行存/LSM/向量/GraphRAG 的生态位;钉住邻接代价、Neo4j store format(record→block)、page cache、索引与 Expand、Cypher 计划五条坐标系,并以 Angles & Gutierrez 谱系与原生图争论收束系列路线。
【图数据库内核】遍历与 Expand:从索引起点走到邻居的执行骨架
拆解 Cypher 遍历算子 Expand(All/Into)、OptionalExpand、VarLengthExpand(All/Into/Pruning)与最短路径双向 BFS;钉住路径爆炸、DISTINCT 剪枝、量化路径谓词,以及 expand 如何吃第 03–05 篇的 store 与 page cache。
【图数据库内核】Cypher 计划入口:Estimated Rows、基数乘积与「看起来对但跑爆」
读懂 EXPLAIN/PROFILE:自下而上的算子树、Estimated Rows 与 Rows 的落差、DB Hits 与 page cache;钉住行流基数、幂律下选择性失真,以及索引起点 × Expand 的经典计划事故。
【图数据库内核】属性图 · Store Format · Expand · Cypher 计划边界
补齐站内缺失的图拓扑存储与遍历内核:属性图模型、Neo4j record/aligned/block 布局、page cache 与 dense 节点、标签索引、Expand 与 Cypher 计划边界,以及 TinkerPop/JanusGraph 对照与 GraphRAG 接口。