知识库

如何诊断锁定问题

从 Neo4j 3.4 开始,可以更好地理解由并发查询引起的锁定问题。本文档不会详细介绍 Neo4j 中锁定的基础知识。我们假设一种情况,您并发运行大量查询 - 可能是相同的参数化查询或不同的查询。这些查询的典型执行时间远高于预期 - 您基本上认为 Neo4j “很慢”。事实上,这种观察结果的来源可能是您的查询试图获取公共节点上的锁,因此它们需要等待锁持有者释放锁。等待公共锁的查询实际上被串行化了。

在早期版本的 Neo4j 中,查询日志记录功能 允许添加与查询处于等待状态多长时间相关的信息,无论是在等待锁还是在等待 IO。这可以通过 neo4j.conf 参数 dbms.logs.query.time_logging_enabled=true 打开。但是,这仅向我们提供有关总等待时间的信息,而没有深入了解可能导致等待的节点或关系。

从 Neo4j 3.4 开始,以及新的存储过程 dbms.listTransactions(),可以更好地理解延迟的原因。为了演示使用 2 个 cypher-shell 连接的过程,请运行以下 Cypher 代码

session1:    merge (n:Lock {id:1}) set n.age=20 with n call apoc.util.sleep(200000) return n;
session2:    Match (n:Lock {id:1}) set n.age=85 return n;

第一个会话将把 id=1 的 :Lock 标签节点的 age 属性设置为 20,然后休眠 200 秒(从而保持对该节点的锁定)。第二个会话将尝试更新相同的节点并将其 age 属性设置为 85,但将在这些 200 秒内被阻塞。

在这 200 秒内,通过 Neo4j 浏览器运行 call dbms.listTransactions() yield transactionId, startTime, currentQueryId, currentQuery, status 将返回类似于以下内容的输出

diagnose locking issues qbloQCO

从中我们可以看到,事务 1381/查询 1378 报告其状态为“被以下事务阻塞:[事务 1380]”,并且事务 1380 也列在输出中,并且两个事务都描述了当前查询。

此外,运行 call dbms.listActiveLocks('<currentQueryId>');,例如 call dbms.listActiveLocks('query-1377'); 将返回类似于以下内容的输出

从上面的输出中,我们看到查询 1377 对 id(n)=8432 的节点拥有排他节点锁。

diagnose locking issues ElrX7jH

通过这两个过程,您掌握了强大的工具,可以了解哪些节点/关系可能是锁争用的来源。