neo4j - Neo4J 上的锁定机制

标签 neo4j locking atomic

我在我的应用程序中发现了一个并发问题,其中有两个线程尝试在 Neo4J v2.2.5 中的同一节点或关系上同时执行写入操作。

我设法用简单的方法重现了这个问题:

  1. 下载并导入示例 Neo4j 电影数据库:http://example-data.neo4j.org/files/cineasts_12k_movies_50k_actors_2.1.6.zip 由于数据库较旧,因此需要在 conf/neo4j.properties 中添加 allow_store_upgrade=true 来启用数据库自​​动升级。

  2. 启动 neo4j 并在 neo4jshell 上运行此查询:

    match (a:Actor {name: "Claude Jade"}), (m:Movie)
    merge (a)-[:ACTS_IN]->(m);
    

    这将创建从“Claude Jade” Actor 到所有电影节点的 ACTS_IN 关系。这样做的原因是为了让删除“Claude Jade”Actor节点的过程(见下面的第4点)更长,并发问题发生的机会更大。

  3. 如果您还没有curl 命令行程序,请下载它。我们将使用curl向neo4j发送查询。

  4. 创建一个 bash 脚本文件(文件名由您决定),内容为:

    #!/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”Actor及其所有关系,第二个进程将尝试创建与“Claude Jade”Actor的新ACTS_IN关系。

  5. 在 bash 上运行脚本文件,例如$ ./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_IN 关系,它首先执行 MATCH 查询 (MATCH (n:Actor {name:\"Claude Jade\"}) OPTIONAL MATCH (n)-[r]-()) 但在执行 DELETE n, r 之前,第二个进程设法插入新的 ACTS_IN 关系到 Actor 节点,这就是删除失败的原因,因为当它尝试执行 DELETE 时,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

关于neo4j - Neo4J 上的锁定机制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33001468/

相关文章:

java - 如何在 Neo4j3 中调试非托管服务器扩展

java - 在 Java 中创建新的 neo4j 节点和现有节点之间的关系

sql-server-2008 - 防止多行出现竞争条件

MySQL:在一个表中插入记录,从另一个表读取,不锁定

c - OpenCL - 双重原子操作 - 工作到极限

java - Neo4J Java API - 特定节点标签或排除特定关系的最短路径

c# - Neo4j .NET 客户端执行字符串密码查询

MySQL 慢查询 Lock_time = 年?

c++ - 多线程环境中的延迟加载数据

postgresql - 如何在PostgreSQL中实现 `BEGIN ATOMIC`