如何诊断锁定问题
自 Neo4j 3.4 起,能够更好地了解并发查询导致的锁定问题。本文档不会详细介绍 Neo4j 中锁的基础概念。我们假设在并发运行大量查询的情形——可能是相同的参数化查询,也可能是不同的查询。这些查询的典型执行时间远高于预期,导致你觉得 Neo4j “很慢”。实际上,这种现象往往是因为查询尝试在同一个节点上获取锁,因而必须等待锁持有者释放锁。等待公共锁的查询实际上被序列化执行。
在早期版本的 Neo4j 中,查询日志功能允许记录查询处于等待状态的时长,无论是等待锁还是等待 I/O。可以通过 dbms.logs.query.time_logging_enabled=true 在 neo4j.conf 中开启。但这仅提供总等待时间的信息,无法洞察是哪一个竞争的节点或关系导致了等待。
从 Neo4j 3.4 开始,配合全新的存储过程 dbms.listTransactions(),可以更清晰地了解延迟的根本原因。要演示该过程,使用两个 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 将返回类似如下的输出:

从该输出中我们可以看到,transaction-1381/query-1378 的状态报告为 “Blocked by: [transaction-1380]”,而 transaction-1380 也出现在输出中,两者都描述了各自的 currentQuery。
此外,执行 call dbms.listActiveLocks('<currentQueryId>');(例如 call dbms.listActiveLocks('query-1377');)会返回类似如下的输出:
从上面的输出可以看到,query-1377 对 id 为 8432 的节点持有 EXCLUSIVE NODE 锁。

借助这两个过程,你即可拥有强大的工具来了解哪些节点/关系可能是锁竞争的根源。
此页面有帮助吗?