Image
Top
Navigation

トランザクション分離レベルについての確認 Phantom Read

ファジーリードの次はファンタムリード(Phantom Read)を確認します。

OrientDBは1.7.8の段階ではレベルをIsolation Level を REPEATABLE READとしています。

実際に確認していきたいと思います。
手順としては前回と一緒のリモート接続にてJavaのAPIを使用して実施します。

Phantom Read

まずはファンタムリードとはの別のトランザクションで挿入されたデータが見えることにより、一貫性がなくなる現象です。

今回の確認の手順としては、

1.トランザクションAでレコードをカウントする(Class指定)。該当レコードがないことを確認(0件)
2.トランザクションBでレコードをINSERTし、COMMITする。
3.トランザクションAでレコードをSELECTする(今回はカウント)。2でINSERTとしたレコードがカウントされる。
4.ファジーリードとよく似ていますがINSERTとUPDATEという点が異なります。

今回も2つの非常にシンプルなJavaプログラムを使用します。
一つは、トランザクションA用のプログラム.

package transaction.phantomread;

import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx;
import com.orientechnologies.orient.core.id.ORecordId;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.core.tx.OTransaction.TXTYPE;

public class Transaction_lock5Stop {

    /**
     * @param args
     */
    public static void main(String[] args) {
        ODatabaseDocumentTx database = null;
        try {

            System.out.println(">>>> トランザクションA");
            // remoteでデータベースへ接続します。
            database = new ODatabaseDocumentTx("remote:172.16.251.205/DocumentTest");
            database.open("root", "root");

            database.getMetadata().getSchema().getOrCreateClass("PhantomTest");

            database.begin(TXTYPE.OPTIMISTIC);

            long firstCount = database.countClass("PhantomTest");

            // save前に値を確認
            System.out.println("> トランザクションの中で取得件数 : " + firstCount);

            System.out.println("---------- 10秒Sleepする ----------");
            Thread.sleep(10000); // 10秒Sleepする

            long secondCount = database.countClass("PhantomTest");

            System.out.println("> トランザクションの中で取得件数 : " + secondCount);

            database.rollback();

            database.close();

        } catch (Exception e) {
            e.printStackTrace();
            database.rollback();
        } finally {
            database.close();
        }

    }
}

内容としては、 PhantomTest クラスを作成しトランザクションを開始します。
ちなみにですが、トランザクション内でOrientDBないでクラスの作成(スキーマへの変更)はできません。
そしてトランザクション内にて、 PhantomTest のレコードの件数をカウントします。
(レコードと表現してよいのか微妙ですが)
その後、10秒ほどスリープします。その間にトランザクション2を実行するためです。
トランザクション2によるインサートが完了したあとに同一トランザクション内で再度PhantomTestのレコード件数を取得し、トランザクション2からの影響が発生していないか確認します。

次に、トランザクションB用のプログラム.

package transaction.phantomread;

import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.core.tx.OTransaction.TXTYPE;

public class Transaction_lock5 {

    /**
     * @param args
     */
    public static void main(String[] args) {
        ODatabaseDocumentTx database = null;
        try {
            System.out.println(">>>> トランザクションB");
            // remoteでデータベースへ接続します。

            database = new ODatabaseDocumentTx("remote:172.16.251.205/DocumentTest");
            database.open("root", "root");

            database.begin(TXTYPE.OPTIMISTIC);

            ODocument doc = new ODocument("PhantomTest");
            doc.field("name", "phantomTest");

            doc.save();

            System.out.println("> コミット前の値を確認");
            for (ODocument od : database.browseClass("PhantomTest")) {
                System.out.println("RID : " + od.getIdentity());
            }

            database.commit();

            // save 後の値を確認
            System.out.println("> コミット 後の値を確認");
            for (ODocument od : database.browseClass("PhantomTest")) {
                System.out.println("RID : " + od.getIdentity());
            }

            database.close();

        } catch (Exception e) {
            e.printStackTrace();
            database.rollback();

        } finally {

            database.close();
        }

    }
}

こちらの内容としては、単純にトランザクションを開始してレコードを登録しコミットしているだけです。
確認のために、レコード取得後、更新後に値を表示しています。

さて、こちらがAの結果です。

>>>> トランザクションA
> トランザクションの中で取得件数 : 0
---------- 10秒Sleepする ----------
> トランザクションの中で取得件数 : 1

こちらはBの結果です。

>>>>トランザクションB
> commit 前の値を確認
RID : #27:-2
---------- 10秒Sleepする ----------
> commit 後の値を確認
RID : #27:1

トランザクションA内にて、トランザクションBのコミット前後の値に影響を受け、レコードの件数が変わったしまいました。
つまりファンタムリードは発生してしまいます。

次にグラフデータベースで確認をしてみましょう。