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

【图数据库内核】生产排障:超节点、爆炸路径、内存三分与慢查询清单

文章导航

分类入口
databasegraph
标签入口
#graph-database#neo4j#troubleshooting#dense-node#cardinality#page-cache#memory#query-log#deadlock#profile

目录

前序篇章把机制拆开讲;线上症状往往缠在一起。本篇以 Neo4j 主线给出可执行的分流清单:先判定慢在哪一轴,再回指具体章节改查询、建模或配置。不粘贴本机未跑的 PROFILE / 指标截图;不写 Aura 工单话术。

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

篇目 核心内容
第 5 / 10 / 11 篇 I/O、计划、锁三条诊断轴
第 15 篇 · 生产排障 分流清单与回指
第 16 篇 · 选型与 GraphRAG 机制够用后的选型收束

版本锚定:Operations Manual(2026.07 / current)Memory configurationEssential metricsQuery loggingDisks, RAM and other tips(page cache warmup);Cypher Manual Understanding query plans;Concurrent data access(锁)。机制细节以第 03–12 篇为准。


一、先分流:三轴,不要一上来加内存

第 11 篇已钉慢查询三轴。排障第一步是互斥假设,而不是同时改 pagecache.size、加索引、加大事务内存。

典型信号 先读
A. 计划基数 PROFILE 某层 Rows 数量级跳变;Estimated ≪ Rows;事务被内存限制杀掉且 hit_ratio 仍高 第 09–10 篇
B. 存储 I/O Rows 正常或中等,DB Hits / page fault / hit_ratio 差;冷启动后第一次查询特别慢 第 03–05 篇
C. 锁 / 并发 查询挂起;listActiveLocksDeadlockDetected / GQLSTATUS 50N05;锁超时 第 11 篇
flowchart TD
  Slow["慢查询 / 超时 / OOM"] --> Q1{"挂起且几乎无 CPU?"}
  Q1 -->|是| Locks["轴 C:锁 / 死锁 / 超时"]
  Q1 -->|否| Q2{"PROFILE Rows 数量级爆炸?"}
  Q2 -->|是| Card["轴 A:基数 / 变长 / 行积"]
  Q2 -->|否| Q3{"hit_ratio 低或 DB Hits 相对 Rows 很高?"}
  Q3 -->|是| IO["轴 B:布局 / dense / 冷页"]
  Q3 -->|否| Mem["再查事务内存限制、GC、语义索引旁路"]

纪律

  1. 冷启动 / warmup 期的 fault 尖峰不要当成稳态病灶(手册:EE active warmup;fault 先高后降属预期)。
  2. 「用了 IndexSeek」≠ 健康——入口 \(|S|\) 可以合法地巨大(第 07、10 篇)。
  3. 集群上先排除「读到滞后 secondary / 等 bookmark」(第 12 篇),再按单机三轴查。

二、超节点(dense / 门户点)

2.1 如何确认

机制回顾:record 系过阈值进 dense group;block 系关系过多进类型向 dense B+ 树(第 03–04、06 篇)。业务上的「门户点」(全局账号、热门 SKU、公共标签点)即使未贴 dense 标签,也会表现得像超节点。

观察 含义
延迟与个别点 id 强相关,换点则正常 点局部度数问题,不是全局 cache 太小
全局 hit_ratio 尚可,单查询 pins/耗时尖峰 第 05 篇「超节点扫叶」形态
写边时锁等待集中在同一节点 / 类型 第 11 篇 dense/sparse 锁表;热点写
Expand 无类型过滤,从门户点出发 人为把 \(d_{\max}\) 整段扫进行流

2.2 处置顺序(先建模与查询,后硬件)

  1. 收窄类型与方向:禁止「无类型 * 从门户点出发」。
  2. 加界变长 / 改 Pruning 或只要终点(第 09 篇)。
  3. 拆点或边类型细分:把「一类超级边」拆成业务子类型,降低单次 range 宽度。
  4. 写路径:热点建边注意 sparse→dense 转换与锁集合变化(第 06、11 篇);应用层串行化或分片写入门户点。
  5. 扩 page cache:仅当该超节点工作集页数明确装不下且业务必须扫全邻接时——这是承认下界,不是根治。

引擎的 dense 优化(读上按类型树、写上减少整点互斥)不免除「门户点不该当遍历起点」的建模责任。


三、爆炸路径与基数事故

3.1 变长 / 最短误用

症状 检查
VarLengthExpand 层 Rows 指数涨 上界是否缺失/过大;是否该 DISTINCT→Pruning;量化路径内能否加谓词
「最短」却写成变长再应用层比长度 改用最短路径算子 / 选择器(第 09 篇)
OptionalExpand 后行数怪异 空扩展仍吐行——下游是否当存在性用错

3.2 计划层经典坑(第 10 篇压缩版)

  1. 高召回 IndexSeek × 多层 Expand:先砍 \(|S|\) 或跳数,不要先加更宽索引。
  2. 行积型第二次 MATCH:同一实体出现在多行后再匹配 → WITH DISTINCT / collect / pattern comprehension。
  3. USING INDEX 强迫错索引:以 PROFILE Rows 为准。
  4. EXPLAIN 好看、PROFILE 写爆堆:Estimated 全程乐观 → 伪 I/O,扩 page cache 无效。

3.3 操作顺序

EXPLAIN → 认叶子与 Expand 形状
PROFILE(仅调优窗口)→ 标出 Rows 第一处数量级跳变
改写查询 / 加界 / 压基数 → 再 PROFILE
仍 DB Hits 高且 Rows 已控 → 转轴 B

手册提醒:非调优时不要随便 PROFILE(成本高于 EXPLAIN)。


四、内存三分:page cache · heap · OS(Lucene/向量)

Memory configuration 把 RAM 切成 OS、JVM heap、native(含 page cache)、事务状态等。排障时至少分清三块:

区域 配置把手(常见) 图负载含义
Page cache server.memory.pagecache.size 缓存 store + native 索引页;治轴 B
Heap server.memory.heap.initial_size / max_size(建议相同) 算子状态、事务对象、计划;治轴 A 的物化爆炸与并发查询
OS 剩余 勿榨干;手册建议专用机可关 swap Lucene 全文/向量吃 OS 页缓存,不进 Neo4j page cache(第 08 篇)

4.1 指标口径(Essential metrics)

显式配置 page cache 与 heap;可用 neo4j-admin server memory-recommendation 看「data+native indexes」与 Lucene 体积,做分账而不是只看一个总数。

4.2 事务内存限制

手册警告:变长 / 最短路径 / 大 UNWIND 上,估算可能偏保守(偏高)而误杀;若怀疑误杀,可在受控环境临时关闭限制验证真实堆占用——这是诊断手段,不是生产长期关闭。

4.3 症状对照

症状 更像
hit_ratio 差、fault 持续高(非 warmup) page cache 偏小或工作集随机颠簸
hit_ratio 好,查询被 transaction memory 杀掉 行流/路径物化(轴 A)
加大 page cache 无效,GC 长暂停 heap 与并发查询;或计划爆炸
向量/全文慢,page cache 指标却健康 OS 内存被 Lucene 挤占;回第 08 篇

五、锁、死锁与「假慢查询」

现象 动作
查询长时间 runnable/waiting SHOW TRANSACTIONSdbms.listActiveLocks(queryId)
DeadlockDetected / 50N05 有限重试;固定多实体更新顺序;审查乱序 MERGE(第 11 篇)
锁超时 查超时配置与持锁事务;区分「慢计划」与「等锁」
只在写多时出现 dense 热点、同节点建边锁集合;考虑建模拆分

读已提交下遍历结果可被并发改写——「两次读不一致」可能是隔离语义而非 bug(第 11 篇)。


六、慢查询清单(建议固定流程)

把下面当作 runbook 骨架;每步记下证据再改配置。

  1. 确认范围:单条 Cypher / 一类模式 / 全局变慢?是否刚导入、是否刚重启、是否只打 secondary?
  2. 查 query logdb.logs.query.enabled;用 db.logs.query.threshold 只留慢查询;生产建议 obfuscate_literals=true(改设置后必要时 db.clearQueryCaches())。
  3. EXPLAIN 该模式:叶子是否合理?有无笛卡尔 / 无界变长?
  4. PROFILE(短窗口、限流):标出 Rows 爆炸层 vs DB Hits 主导层。
  5. 对号入座
    • Rows 爆 → 第三节;
    • Hits/miss → 第二节 + 第 05 篇;
    • 等待 → 第五节。
  6. 看内存分账:page cache / heap / OS;事务是否触顶;Lucene/向量是否该加 OS RAM。
  7. 看指标时段hit_ratiousage_ratiopage_faults、checkpoint 是否打满磁盘 IOPS(磁盘篇:checkpoint IOPS limit)。
  8. 改一处、复测一处:禁止「同时改计划、加缓存、加机器」导致无法归因。
  9. 建模债:若同一超节点反复出现,开改造票,而不是永久加大机器。
  10. 集群:写路径是否打到 writer;读是否需 bookmark;选举抖动是否与慢查询同时段(第 12 篇)。

七、症状 → 章节速查

症状 优先章节
不知道 hop 该贵在哪 02
怀疑链路过长 / 属性乱跳 03
EE 是否该迁 block、dense 树行为 04
hit_ratio、fault、伪 I/O 05
删除后空间、id、升 dense 06
起点选错索引、约束误解 07
全文/向量慢但拓扑查询正常 08
Expand / 变长 / 最短 09
Estimated vs Rows、计划事故 10
死锁、lost update、等锁 11
副本上看不见刚写的数据 12
宽表图层尾延迟(非 Neo4j) 13
选错引擎族 14 → 16

八、其它引擎:同构翻译,不要照抄指标名

第 14 篇地图上的系统,排障同构为:

Neo4j 轴 JanusGraph TigerGraph AGE / 边表 Neptune
page cache miss 远端行读 / 后端读延迟 GSE 驻留与跨分区消息 PG shared_buffers / 索引读 共享存储 + 实例缓存叙事
Rows 爆炸 Gremlin 步产出爆炸 ACCUM 前扇出过大 递归 CTE / JOIN 行积 查询模式未命中默认索引
锁等待 LOCK 争用 / 后端超时 引擎事务与分区热点 PG 锁与 vacuum 实例写主与限流

不要hit_ratio > 98% 直接贴到 Cassandra;要翻译成该系统的命中与尾延迟信号。


九、争论与开放问题

  1. 加机器 vs 改模型:A 侧短期扩容恢复 SLA;B 侧坚持超节点是图模型债。生产常需要「扩容止血 + 建模排期」双轨,单轨都会反复。
  2. 事务内存限制误杀:保守估计保护进程,也可能误杀合法大分析查询——分析闭包是否应迁 GDS / 批处理引擎,仍是开放分工。
  3. 开放问题:按节点度数直方图的自动告警是否进入官方可观测性;block dense 与锁指标的联合面板;查询日志与计划缓存在多数据库下的归因粒度。

谱系:OLTP 排障传统是「计划 → I/O → 锁」分层;图引擎把 I/O 特化成邻接局部性与超节点页集,把计划特化成路径行流——清单的价值是强制分层,而不是新造术语。


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

结论 等级 来源
内存区域、pagecache/heap、transaction total.max、memory-recommendation、误杀说明 A Ops Manual Memory configuration(2026.07/current)
hit_ratio / usage_ratio 等必备指标口径 A Essential metrics
query log threshold / obfuscate A Query logging
warmup 与 fault 尖峰预期 A Disks, RAM and other tips
EXPLAIN/PROFILE 用法 A Cypher Manual Understanding query plans
锁与死锁 A Concurrent data access;本系列第 11 篇
本机超节点构造与延迟数字 未跑 不写伪造耗时

十一、小结

  1. 先分轴:基数 / 页缓存 I/O / 锁;同时改三处等于放弃归因。
  2. 超节点:先收窄遍历与建模拆分;dense 是引擎缓解,不是业务通行证。
  3. 内存三分:page cache ≠ heap ≠ Lucene/向量的 OS 缓存;触事务上限先看行流再看加堆。
  4. 下一篇:收束「何时必须原生图」,并与 GraphRAG / 向量检索划清接口。

系列目录 · 上一篇:引擎对照 · 下一篇:选型与 GraphRAG

同主题继续阅读

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


By .