知识库

如何诊断锁定问题

自 Neo4j 3.4 版本以来,可以更好地理解并发查询导致的锁定问题。本知识库文章不会详细介绍 Neo4j 中锁定的基础知识。我们假设您同时运行大量查询——可能是相同的参数化查询,也可能是不同的查询。这些查询的典型执行时间远高于您的预期——您会认为 Neo4j “很慢”。实际上,这种现象的原因可能是您的查询试图获取公共节点上的锁,因此它们需要等待直到锁持有者释放锁。等待公共锁的查询实际上是串行执行的。

在之前的 Neo4j 版本中,查询日志记录功能允许添加与查询处于等待状态(无论是等待锁还是等待 I/O)的时间长度相关的信息。这可以通过 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;

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

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

diagnose locking issues qbloQCO

从该输出中,我们可以看到 transaction-1381/query-1378 报告其状态为 Blocked by: [transaction-1380],并且 transaction-1380 也列在输出中,两个事务都描述了 currentQuery。

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

从上述输出中,我们看到 query-1377 在 id(n)=8432 的节点上有一个 EXCLUSIVE NODE 锁。

diagnose locking issues ElrX7jH

有了这两个过程,您就拥有了强大的工具来了解哪些节点/关系可能是锁竞争的来源。

© . All rights reserved.