Neo4J的锁紧机构
我在我的应用程序中发现了一个并发问题,在Neo4J v2.2.5中,有两个线程试图在同一时间对同一节点或关系执行写操作 我用简单的方法重现了这个问题:Neo4J的锁紧机构,neo4j,locking,atomicity,Neo4j,Locking,Atomicity,我在我的应用程序中发现了一个并发问题,在Neo4J v2.2.5中,有两个线程试图在同一时间对同一节点或关系执行写操作 我用简单的方法重现了这个问题: 下载并导入示例neo4j电影数据库: 由于数据库很旧,因此您必须在conf/neo4j.properties中添加allow_store_upgrade=true,以启用自动数据库升级 启动neo4j并在neo4jshell上运行此查询: match (a:Actor {name: "Claude Jade"}), (m:Movie) merge
conf/neo4j.properties
中添加allow_store_upgrade=true
,以启用自动数据库升级match (a:Actor {name: "Claude Jade"}), (m:Movie)
merge (a)-[:ACTS_IN]->(m);
这将创建从“Claude Jade”演员到所有电影节点的角色关系。这样做的原因是为了使删除“Claude Jade”参与者节点过程(参见下面的第4条)更长,因此并发问题发生的可能性更大#!/bin/bash
curl -XPOST http://localhost:7474/db/data/transaction/commit -H "Content-Type: application/json" -d '{"statements" : [ {"statement" : "MATCH (n:Actor {name: \"Claude Jade\"}) OPTIONAL MATCH (n)-[r]-() DELETE n, r"} ]}' &
curl -XPOST http://localhost:7474/db/data/transaction/commit -H "Content-Type: application/json" -d '{"statements" : [ {"statement" : "MATCH (n:Actor {name: \"Claude Jade\"}) CREATE (n)-[:ACTS_IN]->(m:Movie {title: \"Hello World\"}) RETURN m"} ]}' &
wait
这将并行运行两个curl进程,其中第一个进程将尝试删除“Claude Jade”演员及其所有关系,第二个进程将尝试创建与“Claude Jade”演员相关的新动作$./test.sh
{
"results" : [{
"columns" : ["m"],
"data" : [{
"row" : [{
"title" : "Hello World"
}
]
}
]
}
],
"errors" : []
} {
"results" : [{
"columns" : [],
"data" : []
}
],
"errors" : [{
"code" : "Neo.DatabaseError.Transaction.CouldNotCommit",
"message" : "org.neo4j.kernel.api.exceptions.TransactionFailureException: Node record Node[3150,used=false,group=55,prop=-1,labels=Inline(0x0:[]),light] still has relationships",
"stackTrace" : "java.lang.RuntimeException: org.neo4j.kernel.api.exceptions.TransactionFailureException: Node record Node[3150,used=false,group=55,prop=-1,labels=Inline(0x0:[]),light] still has relationships\r\n\tat org.neo4j.server.rest.transactional.TransitionalTxManagementKernelTransaction.commit(TransitionalTxManagementKernelTransaction.java:87)\r\n\tat org.neo4j.server.rest.transactional.TransactionHandle.closeContextAndCollectErrors(TransactionHandle.java:278)\r\n\tat
...
注意:如果没有看到任何事务错误,则必须重新导入电影数据库并重新运行上述步骤
因此,从我所看到的情况来看,删除失败是因为它试图删除Actor节点及其在关系中的所有ACTS\u,它首先进行匹配查询(MATCH(n:Actor{name:\'Claude Jade\\”})可选匹配(n)-[r]-()
),但在执行删除n,r
)之前,第二个进程设法将新的ACTS_插入到Actor节点,这就是删除失败的原因,因为当它尝试执行删除时,Actor已经添加了一个新的关系
我想知道Neo4J上是否有可以用来防止此问题的锁定机制?您正在观察一个经典的竞速条件。为了防止出现这种情况,您需要在
Claude
节点上获取一个锁。Cypher没有显式抓取锁的语法,所以我们只是将一个伪属性设置为第一个操作,并在最后删除该属性-这有抓取锁的副作用。因此,改变你的两种说法:
MATCH (n:Actor {name: "Claude Jade"})
SET n._fake = 1 // grabs the lock as first action
WITH n
OPTIONAL MATCH (n)-[r]-()
DELETE n, r
MATCH (n:Actor {name: "Claude Jade"})
SET n._fake = 1 // grab the lock early
CREATE (n)-[:ACTS_IN]->(m:Movie {title: "Hello World"})
REMOVE n._fake // get rid of fake property
RETURN m
您好,感谢您的解决方案,我已经尝试过了,有时它可以工作,但有时我在第二个curl进程(在关系中创建新的ACTS_)中出错:
{“results”:[],“errors”:[{“code”:“Neo.ClientError.Statement.EntityNotFound”,“message”:“无法加载id为3150的节点”。}]}
使用fake属性可以有效地序列化这两条语句,因为另一条语句必须等待第一条语句提交。由于两条语句同时启动,因此无法确定哪一条语句首先获取锁。如果delete语句首先选择,它将删除node和rels,第二个匹配将失败(因为节点已消失)。如果delete语句首先运行,第二个匹配过程确实将失败,但它不应返回错误,而应返回空数据。我想发生的事情是当第二个进程遇到SET n._fake=1
行时,它停止了查询执行,直到第一个进程释放了锁。然后,当它被释放时,它试图再次将查询执行恢复到下一行:CREATE(n)-[:ACTS_IN]->(m:Movie{title:\'Hello World\'})
,但是节点n
已经被第一个进程删除,这就是为什么错误无法加载id为的节点…
。您是对的。在这种情况下,您应该在另一个不受更改影响的节点上获取一个锁。可以创建一个特定的Technical lock节点:create(:lock)
,并在该节点上设置/删除假属性。每个需要锁的语句都将以MATCH(l:lock)SET l开头。_fake=1和l。。。。。。移除l.(u fake
我还遇到了需要手动锁定的问题。Neo4j流行的APOC插件有一系列可通过Cypher调用的显式锁定过程: