唯一节点

本文描述了在创建节点时如何确保属性的唯一性。

创建唯一节点

在许多使用场景中,实体之间需要一定程度的唯一性。例如,系统中只能存在一个具有特定电子邮件地址的用户。如果多个并发线程天真地尝试创建该用户,就会产生重复记录。

以下是确保唯一性的主要策略,它们在集群和单实例部署中均可工作。

单线程

通过使用单线程,任何两个线程都不会同时尝试创建同一实体。在集群环境中,可以使用外部的单线程客户端来执行这些操作。

获取或创建

定义唯一约束并使用 Cypher MERGE 子句是 获取或创建 唯一节点的最高效方式。

示例的源代码可在以下位置找到:GetOrCreateDocIT.java

创建唯一约束

try ( Transaction tx = graphdb.beginTx() )
{
    tx.schema()
        .constraintFor( Label.label( "User" ) )
        .assertPropertyIsUnique( "name" )
        .withName( "usernames" )
        .create();
    tx.commit();
}

使用 MERGE 创建唯一节点

Node result = null;
ResourceIterator<Node> resultIterator = null;
try ( Transaction tx = graphDb.beginTx() )
{
    String queryString = "MERGE (n:User {name: $name}) RETURN n";
    Map<String, Object> parameters = new HashMap<>();
    parameters.put( "name", username );
    resultIterator = tx.execute( queryString, parameters ).columnAs( "n" );
    result = resultIterator.next();
    tx.commit();
    return result;
}

MERGE 子句会对已存在的匹配节点获取读取锁,或对新节点获取写入锁,从而确保当前事务是唯一创建该节点的事务。

技术上可以使用 锁节点锁属性,但应尽可能避免。使用 锁节点 模式很难正确实现,并且由于始终涉及写锁,其性能表现更差。

你可能也会想使用 Java 同步来进行悲观锁定,但这很危险。将 Neo4j 的锁与 Java 运行时的锁混用可能导致 Neo4j 检测不到的死锁。只要所有锁都由 Neo4j 管理,所有死锁都会被检测并避免。

有关锁和死锁的更多信息,请参阅 操作手册 → 锁与死锁