JanusGraph的事务

处理数据库时,为了保持数据的一致性,必须关注事务。由于JanusGraph也支持事务,所以需要确认如何处理。

确认是否支持交易支持。

可以通过Graph.features()获取由TinkerPop规范支持的图数据库功能列表。可以在Gremlin控制台中输入并尝试。

gremlin> graph = JanusGraphFactory.open("conf/janusgraph-berkeleyje.properties")
==>standardjanusgraph[berkeleyje:C:\******\janusgraph-0.5.1\conf\../db/berkeley]
==>FEATURES
> GraphFeatures
>-- IoRead: true
>-- Computer: true
>-- IoWrite: true
>-- Transactions: true  // ココ!
>-- Persistence: true
>-- ConcurrentAccess: true
>-- ThreadedTransactions: true  // あとココ!
()

如果Transactions的项为true,则可以使用事务。例如,使用TinkerGraph.open()生成的图不支持事务。

gremlin> graph = TinkerGraph.open()
==>tinkergraph[vertices:0 edges:0]
gremlin> graph.features()
==>FEATURES
> GraphFeatures
>-- IoRead: true
>-- Computer: true
>-- IoWrite: true
>-- Transactions: false  // ダメ!
>-- Persistence: true
>-- ConcurrentAccess: false
>-- ThreadedTransactions: false  // ダメ!

如果ThreadedTransactions为true,则可以使用在多个线程之间共享一个事务的功能(具体用途虽然不确定,但可能会在分散重负载处理时使用)。

如果认为使用features()时日志的显示不清晰,可以使用以下代码逐个确认。

gremlin> graph.features().graph().supportsTransactions()
==>true
gremlin> graph.features().graph().supportsThreadedTransactions()
==>true

访问服务器时的交易限制

在之前的文章《JanusGraph的两种用法》中已经提到,如果要访问图形数据库服务器并进行查询(即远程情况),事务的使用会受到限制。

JanusGraph-article01(1).png

发送的查询将自动包装在一个事务中并执行。因此,无法将两个查询组合成一个事务。但是,在Gremlin中,可以编写复杂的查询,一次获取、添加、编辑或删除多个元素(顶点/边/属性),所以应该没有太大的不便。上面图中的两个查询可以像 g.addV(“person”).V().count() 这样合并。

如果你确实想要完全控制事务的话,建议你尝试上述文章中使用Cluster和Client的连接方法。虽然不是特别推荐这样做。

TinkerPop事务的基础

以下的描述是基于连接到嵌入式数据库(而非远程数据库)的前提。

交易接口

处理事务时,请使用graph.tx()或g.tx()。由于g.tx()被明确表示为graph.tx()的代理,所以这两者执行完全相同的操作。您可以使用任何一个喜欢的方式。返回一个Transaction作为结果,可以根据需要进行操作。

交易功能方法列表(部分)

メソッド説明open明示的にトランザクションを開始するisOpenトランザクションが開始されているかどうかを取得するclose明示的にトランザクションを閉じる。commitされるかrollbackされるか、あるいはエラーになるかはonCloseの設定によるrollbackトランザクションの内容をロールバックするcommitトランザクションの内容をコミットするonReadWriteグラフのデータに対して読み書きが行われた場合、自動的にトランザクションを開始するかどうか設定するonClosecloseが呼ばれた際の挙動を設定するcreateThreadedTxマルチスレッドに対応したトランザクションを開始し、専用のGraphインターフェースを返す

开始交易

默认情况下,事务会自动开始。其触发时间是在首次对图进行操作(查询)的瞬间。例如,在执行g.V()、g.E()、graph.addVertex()、graph.addEdge()、graph.vertices()、graph.edges()等函数时。

可以通过graph.tx().isOpen来检查事务是否已经开始,可以尝试一下。

gremlin> graph = JanusGraphFactory.open("conf/janusgraph-berkeleyje.properties")
==>standardjanusgraph[berkeleyje:C:\******\janusgraph-0.5.1\conf\../db/berkeley]
gremlin> graph.tx().isOpen()
==>false  // まだ開始されていない
gremlin> graph.vertices()  // ここで開始される
gremlin> graph.tx().isOpen()
==>true

肯定已经开始了。

提交和回滚

如果只是进行只读查询,那么就没有必要特别担心,但如果对图进行了更改(写入),那么这些更改尚未确定。要确定这些更改,需要进行提交。或者在事务中出现了错误或其他不便时,需要进行回滚以取消所有已进行的更改。

回滚操作可以通过graph.tx().rollback()来执行。

gremlin> g.V().count()
==>0
gremlin> g.addV("person").iterate()  // 頂点を追加した
gremlin> g.V().count()
==>1  // 現時点では反映されている(が、確定ではない)
gremlin> g.tx().rollback()  // 取り消す
==>null
gremlin> g.V().count()
==>0  // ロールバックされた

提交由graph.tx().commit()完成。

gremlin> g.V().count()
==>0
gremlin> g.addV("person").iterate()  // 頂点を追加した
gremlin> g.V().count()
==>1 // 現時点では反映されている(が、確定ではない)
gremlin> g.tx().commit()  // 確定させる
==>null
gremlin> g.V().count()
==>1  // コミットされた

在编写程序时,如果发生错误,最后应执行回滚操作;如果没有错误,则应执行提交操作。

执行提交或回滚操作时,事务将被关闭。

明确执行交易的开始

也许有些人觉得自动开始事务有点不舒服。在这种情况下,可以使用onReadWrite来强制显式启动事务。

gremlin> graph.tx().onReadWrite(Transaction.READ_WRITE_BEHAVIOR.MANUAL)
==>org.janusgraph.graphdb.tinkerpop.JanusGraphBlueprintsGraph$GraphTransaction@e886caf
gremlin> g.addV("person").iterate()
Open a transaction before attempting to read/write the transaction
Type ':help' or ':h' for help.
Display stack trace? [yN]
gremlin> graph.tx().open()
==>null
gremlin> g.addV("person").iterate()

通过将onReadWrite设置为MANUAL来防止自动启动。默认情况下,为AUTO。

多线程事务

在多个线程中共享一个事务

使用createThreadedTx()方法。在主线程中生成事务,并将其引用传递给各个线程。等所有线程的处理完成后执行commit或rollback操作。

用Java的程序来举例子。

class AddVertexThread extends Thread
{
    private Graph gx;
    private String name;

    public AddVertexThread(Graph gx, String name)
    {
        this.gx = gx;
        this.name = name;
    }

    @Override
    public void run()
    {
        GraphTraversalSource g = this.gx.traversal();
        g.addV("person").property("name", this.name).iterate();        
    }
}

public class JanusExample {
    public static void main(String args[])
    {
        Graph graph = JanusGraphFactory.open("conf/embedded.properties");
        GraphTraversalSource g = graph.traversal();
        // グラフを空にする
        g.V().drop().iterate();
        // コミット
        g.tx().commit();

        // 名前1つに対し、それぞれ別のスレッドで頂点を登録する
        String names[] = {"bob", "alice", "ellie"};
        List<Thread> threads = new ArrayList<Thread>();
        Graph gx = graph.tx().createThreadedTx();  // 共有されるトランザクション
        for(String name : names){
            Thread t = new AddVertexThread(gx, name);
            threads.add(t);
        }
        for(Thread t : threads) t.start();  // 全スレッド開始
        for(Thread t : threads) t.join();  // 全スレッド完了待ち
        gx.tx().commit();  // まとめてコミット

        // 結果を確認
        List<Object> result_names = g.V().values("name").toList();
        for(Object obj_name : result_names){
            String name = (String)obj_name;
            System.out.println(name);
        }

        graph.close();
    }
}

The result is…

bob
alice
ellie

在中文中拆分:「となる(登録の順番は保証されない)」。

可能的翻译:「并且将是这样(注册顺序不保证)」。

在多个线程中处理多个事务。

在我调查的情况下,没有找到这种方法。实际尝试也不行。

Could not commit transaction due to exception during persistence

由于出现了这个错误,所以无法正常工作。使用类似ConcurrentLinkedQueue的队列,在一个线程中依次处理事务似乎是一个可行的选择。

如果有任何能够成功的方法,请告诉我。

只能进行读取的交易

如果你想要防止意外地覆写数据或者破坏数据,有时候你可能需要进行限制。在这种情况下,你可以使用 JanusGraph.buildTransaction()。

由于实际上无法直接实例化JanusGraph,因此需要通过执行JanusGraphFactory.open()后获取的图(StandardJanusGraph)进行使用。由于StandardJanusGraph是JanusGraph的子类,所以可以使用buildTransaction方法。在Java中,需要将open方法的返回值进行向下转型后再使用。

gremlin> graph = JanusGraphFactory.open("conf/janusgraph-berkeleyje.properties")
==>standardjanusgraph[berkeleyje:C:\******\janusgraph-0.5.1\conf\../db/berkeley]
gremlin> readonly_graph = graph.buildTransaction().readOnly().start()
==>standardjanusgraphtx[0x1e12a5a6]  // 読み取り専用グラフ
gremlin> readonly_graph.addVertex("person")
Cannot create new entities in read-only transaction
Type ':help' or ':h' for help.
Display stack trace? [yN]
gremlin> ro_g = ro_tx.traversal()  // 読み取り専用トラバーサル
==>graphtraversalsource[standardjanusgraphtx[0x1e12a5a6], standard]
gremlin> ro_g.addV("person")
Cannot create new entities in read-only transaction
Type ':help' or ':h' for help.
Display stack trace? [yN]

当前正在进行的交易列表

使用getOpenTransactions()方法。如果由于某种原因事务仍然处于打开状态,可以使用closeTransaction()方法强制关闭它。

gremlin> tx = graph.tx().createThreadedTx()
==>standardjanusgraphtx[0x13213f26]
gremlin> graph.getOpenTransactions()
==>standardjanusgraphtx[0x13213f26]

// 1つだけ閉じたい場合は
gremlin> graph.closeTransaction(graph.getOpenTransactions().get(0))  // 0番目を閉じる
// 全部閉じたい場合は
gremlin> graph.getOpenTransactions().forEach { tx -> graph.closeTransaction(tx) }

关于酸的问题

根据官方文件,可以了解到事务在ACID方面的支持程度。

JanusGraph 的事务并不总是符合 ACID。尽管 BerkeleyDB 中是这样设计的,但是在底层存储系统 Cassandra 或 HBase 这样不提供可串行化隔离性和多行原子写入的情况下,并不满足 ACID。

所以,BerkeleyDB是否兼容?实际上我不知道,如果担心的话最好自行验证一下。

广告
将在 10 秒后关闭
bannerAds