【图数据库内核】写入路径:建边、升 dense、逻辑删除与 ID 复用
内容提要
本文介绍图数据库Neo4j的写入路径:创建关系时挂链或升dense,删除采用逻辑删除并复用ID,空间不立即归还OS。写放大影响读局部性,逼近阈值触发结构迁移,复用旧ID可能破坏缓存友好性。压实需用database copy工具。
延伸解读
写放大与读局部性的权衡
本文揭示写路径与读路径的紧密耦合:建边时插入链头、升dense重写整链、删除后复用旧ID,这些写操作都会影响后续读取的局部性。例如,删除后复用旧ID可能导致新边落在远离高水位的页,破坏时间局部性,加剧随机fault。理解这一权衡有助于在运维中权衡写优化与读性能。
逻辑删除与空间管理
Neo4j采用逻辑删除,删除的记录不立即归还OS,而是通过.id文件记录可复用ID,空间在后续创建时复用。这导致删除大量数据后磁盘占用不降,但可通过database copy工具压实。运维时需注意,仅看磁盘用量下降可能误判,应结合store稀疏度和hit_ratio评估。
dense节点阈值与结构迁移
当节点关系数达到阈值(默认50)时,会触发升dense操作,将sparse链重挂为group结构,这是一次读改写放大,可能重写该点所有关系记录的链接字段。逼近阈值的写入更贵、锁更重,因此建模时需预知超节点,合理配置阈值,避免频繁触发结构迁移。
Q&A
Neo4j中创建一条关系时,写入路径涉及哪些主要步骤?
创建关系时,首先对两端节点检查是否需要升为dense节点,然后分配并写入一条RelationshipRecord,最后通过connectSparse或connectDense将新边挂到两端的邻接结构中。对于sparse节点,新边插入链头;对于dense节点,则插入对应的relationship group。
什么是dense节点?Neo4j中dense节点阈值是多少?升dense的过程是怎样的?
dense节点是指关系数达到或超过阈值的节点,阈值由配置db.relationship_grouping_threshold控制,默认值为50。一旦节点达到阈值,即使之后关系数减少,仍被视为dense。升dense的过程是:当节点关系数达到阈值时,将其标记为dense,并遍历原有sparse链,将已有关系按类型/方向重新挂到relationship group结构上,之后新关系直接插入对应group。
Neo4j中删除关系或节点后,空间会立即归还给操作系统吗?为什么?
不会。Neo4j采用逻辑删除,即相关记录被标记为deleted,但占用的空间不会立即归还给操作系统。删除操作会写入事务日志,可复用的ID记录在.id文件中,空间通过后续创建操作复用,或通过database copy工具进行物理压实。
Neo4j中ID复用如何影响读局部性?
ID复用可能导致新创建的边或节点获得旧ID,从而在文件中位于远离当前高水位的位置,破坏时间局部性。这会导致page cache工作集从尾部热带变成全文件洒点,增加随机指针颠簸,影响读性能。
如何将Neo4j数据库文件真正压缩并归还空间给文件系统?
使用neo4j-admin database copy工具进行碎片整理式拷贝,可以生成新库,跳过未使用记录,使高水位回到紧凑区间。这是离线运维操作,不是在线DELETE的副作用。
Neo4j中block存储格式的写入路径是怎样的?
对于block格式,小度数节点更新block.x1.db中的128字节记录,尽量内联数据;装不下时分配或扩容block.node.xd或block.relationship.xd,并更新x1引用;关系再多时,该类型关系进入block.relationship.dense B+树。胖属性则存入big_values,极端情况进入huge。
Neo4j中删除大量数据时,为什么磁盘占用可能不降反升?
删除操作会写入事务日志,大批量删除(如DETACH DELETE)会撑大事务内存状态和日志,导致磁盘占用增加。同时,逻辑删除不会立即释放空间,store文件不因删除缩小,所以磁盘占用可能不降反升。