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

【图数据库内核】事务、锁与隔离:读已提交、建边锁与 dense 并发

文章导航

分类入口
databasegraph
标签入口
#graph-database#neo4j#transaction#isolation#read-committed#locks#deadlock#dense-node#acid

目录

10 篇假定「计划健康仍慢」时要换诊断轴。本篇换到并发:默认隔离允许多少异常、写锁落在节点还是关系、sparse/dense 建边为何锁集合不同,以及死锁为何是可重试的瞬时错误。高可用复制留给第 12 篇。

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

篇目 核心内容
第 6 篇 · 写入路径 挂链、升 dense、逻辑删除
第 11 篇 · 事务与锁 隔离异常、锁表、死锁
第 12 篇 · HA 边界 集群与只读副本

版本锚定:Operations Manual(2026.07 / current)Database internals(ACID 总述)与 Concurrent data access(隔离、异常、锁、dense、死锁、删除语义)。死锁错误码含 5.25+ 的 GQLSTATUS 50N05 注记。不粘贴本机锁转储。


一、ACID 总述与默认隔离

手册 Database internals:Neo4j 事务具备 ACID,持久化靠 write-ahead transaction log。行为要点:

相对站内 MVCC / PG / InnoDB 叙事:Neo4j 默认路径不是「读也持长期快照锁」的 Serializable 故事,而是写互斥 + 读已提交。图工作负载大量是遍历——手册把「遍历结果可被并发改掉」写成一等事实,而不是脚注。

1.1 两档隔离心智

级别 手册含义 典型用法
read-committed(默认) 读到某节点/关系后,不阻塞他人在本事务结束前改写它 绝大多数查询
显式写锁 → serializable 效果 对公共节点/关系拿写锁,相关事务在该锁上串行 需要确定性读改写(如计数器)时

Cypher 没有完整的 SET TRANSACTION ISOLATION LEVEL 菜单式 API;升高隔离常靠锁技巧(见第三节 lost updates)。


二、read-committed 下的图特有异常

手册 Anomalies 列出的异常仅在 read-committed 下讨论(摘要):

2.1 Lost updates

并发 SET n.prop = … 时,若读与写之间无写锁保护,最终值可不决定。

Cypher 有时自动加写锁:当 SET 右侧直接依赖正在读的同一属性(如 SET n.prop = n.prop + 1,或 map literal 中对 n.prop 的依赖)时,会在读前拿节点/关系写锁。

若先 WITH n.prop AS p 再基于 p 计算后 SET,或形成属性间循环依赖,则不会自动加锁——百客户端自增可能远到不了 100。workaround(手册):先对 dummy 属性 SET/REMOVE 逼出写锁,再读真属性。

2.2 Non-repeatable reads

同一事务内两次读同一属性,中间可被并发提交改掉,两次结果不一致。缓解:只读一次并在查询内保留变量。

2.3 Missing / double reads(索引扫描)

对索引做 scan(如 NodeIndexScan、范围类 RelationshipIndexSeek)时,并发改属性可能使实体在扫描游标「前方/后方」移动 → 重复出现或完全跳过。手册强调:即使索引 backing 唯一性约束也可能发生。Seek 单点与 scan 路径不同——调优时区分算子(第 10 篇)。

2.4 与 Expand 的合读

第 09 篇的多跳 Expand 在默认隔离下,路径上后半段看到的图可以与前半段开始时不同。需要「整条路径冻结」时,不能指望默认隔离;要收窄写并发、显式锁,或接受应用层重试/校验。


三、锁落在哪:默认表与 dense 分叉

3.1 默认行为(手册条目)

锁加入事务,提交或回滚时释放(回滚立即释放)。

操作 锁(简化叙述)
改节点/关系属性 该实体写锁
创建/删除节点 该节点写锁
创建/删除关系 该关系 + 两端节点(见下表细分)

更细的 Table 2:

修改 获得的锁
创建节点 无锁
更新标签 / 节点属性 / 删除节点 NODE
创建关系 sparse:NODE;dense:NODE DELETE prevention(防删点)
更新关系属性 RELATIONSHIP
删除关系 节点侧同创建关系规则 + RELATIONSHIP(sparse/dense 皆然)

索引与内部结构还会加额外锁——手册声明不作保证哪些一定出现。

观测:管理员可 SHOW TRANSACTIONScurrentQueryId,再 CALL dbms.listActiveLocks(queryId)

3.2 Dense:为何超节点写入更能并

接第 06 篇:

建/删关系时:

一句话:升 dense 不只是读路径改 group/树,也是写路径的锁粒度迁移。 热点门户点上的边写入,在越过阈值后争用形态会变——不等于「永远不堵」,但不再与 sparse 链头更新同构。

3.3 内部锁类型(仅作心智,版本可改)

手册列出 LABEL/RELATIONSHIP_TYPE、SCHEMA_NAME、NODE_RELATIONSHIP_GROUP_DELETE、NODE、DEGREES、RELATIONSHIP_DELETE、RELATIONSHIP_GROUP、RELATIONSHIP 等,并警告版本间可无通知变更。读排障日志时认名字即可;不要把内部类型写进跨版本 SLA。

应用侧排序建议:对属性更新等引擎不代为排序的操作,固定锁顺序(如总是先 A 后 B);引擎内部排序主要协助建/删关系类锁。


四、死锁:瞬时错误与 MERGE 陷阱

4.1 检测与语义

两事务互相等待对方持有的节点/关系锁 → 死锁。引擎终止其一:

被终止事务持有的锁在事务结束时释放,等待方继续;应用应有限次重试(可加退避),并覆盖其它瞬时错误。频繁死锁说明写模式在隔离目标下不可规模化并发——要改顺序或分区写,而不是无限重试。

4.2 避免

  1. 多实体更新固定顺序(先 A 后 B);
  2. 避免无必要的交叉热点写;
  3. 注意:MERGE 为保唯一可能乱序取锁,削弱引擎内部排序;可能时用 CREATE(手册鼓励)。唯一性需求用约束 + 明确创建路径,而不是到处 MERGE「图方便」。

4.3 锁等待超时

db.lock.acquisition.timeout(默认 0 = 关闭):正时长(如 10s)内拿不到锁则失败事务。动态设置不可用(手册)。用于防止事务无限卡住,与死锁检测互补。


五、删除语义(提交约束)

手册 Delete semantics

与第 06 篇逻辑删除/.id 复用衔接:并发删除与建边抢同一热点时,锁表(RELATIONSHIP_DELETE、NODE_RELATIONSHIP_GROUP_DELETE 等)比「文件是否缩小」更先成为症状。


六、和计划、缓存的分工诊断

症状 先查
PROFILE Rows 爆炸、Estimated 离谱 第 10 篇
DB Hits / page miss 高、Rows 正常 第 05 篇
查询挂起、listActiveLocks 显示等待 本篇
DeadlockDetected / 50N05 本篇(重试 + 改写序)
自增丢更新、路径两次读不一致 本篇隔离

慢查询三轴:计划基数 × 存储 I/O × 锁等待——不要用单一 pagecache.size 或单一 IndexSeek 解释一切。


七、争论与开放问题

  1. 默认 RC vs 更强隔离:A 侧要遍历吞吐;B 侧要多跳读稳定。图产品多数押 A,把强一致留给显式锁与窄写集。
  2. dense 锁优化 vs 建模:引擎让超节点边写入更能并,不免除「门户点」成为业务热点;建模拆点与锁优化应并行,而不是互相替代。
  3. 开放问题:block 类型级 dense 与内部锁表的可观测性;多数据库/Fabric 下的锁语义(第 12 篇边界);与因果集群提交可见性的交叠。

谱系:隔离异常词汇接 Berenson et al.(SIGMOD 1995,站内 MVCC 文常用);图引擎的增量是边两端锁 + 邻接结构锁,不是行锁词典的简单翻译。


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

结论 等级 来源
ACID、WAL、默认 RC、遍历不受护、非重复读、死锁检测总述 A Operations Manual Database internals
Lost update / 索引 scan 异常 / 锁表 / dense 锁 / 死锁码 / MERGE 警告 / 删除语义 / 锁超时 A Concurrent data access(current)
本机死锁复现、listActiveLocks 未跑 不贴伪造输出

九、小结

  1. 默认 read-committed:写互斥,读与遍历不冻结图。 要确定性读改写,靠直接依赖的 SET 或 dummy 写锁。
  2. 建边锁在 sparse/dense 上分叉;dense 是读布局与写并发的共同转折点(接第 06 篇)。
  3. 死锁是设计内的瞬时错误——有限重试,并固定更新顺序;慎用乱序取锁的 MERGE
  4. 下一篇:因果集群 / 只读副本如何改变「提交可见」与本篇单机锁故事的边界。

系列目录 · 上一篇:Cypher 计划 · 下一篇:HA 边界

同主题继续阅读

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


By .