LoginSignup
0
0

More than 5 years have passed since last update.

インメモリデータベース VoltDBのトランザクション処理を試してみる

Last updated at Posted at 2018-10-06

VoltDBのトランザクション処理

インメモリデータベースであるVoltDBはNoSQLなみに高速(というか超えている)でありながら、NoSQLでは実現が難しいトランザクション処理(ACID)を実現できます。

VoltDBではDML(insert/update/delete/select)をJavaで実装されたストアドプロシージャで実現します。ストアドプロシージャを開始すると、VoltDBは自動的にトランザクションを開始し、ストアドプロシージャが完了すると自動でコミットされます。ストアドプロシージャがエラーなどで完了しなかった場合は自動的にロールバックされます(アトミック)。
明示的にVoltAbortException例外をスローすることによって、強制的にロールバックすることもできます。

プロシージャ作成

実際にトランザクション処理を確認してみます。
以下の処理を実行するプロシージャを作成します。

(1)トランザクションを開始する。(自動)
(2)Aテーブルにインサートする。
(3)Bテーブルにインサートする。
(4)コミットする。(自動)

エラー時にロールバックすることを確認するため、(2)と(3)の間で例外を発生させます。

まず、テスト用のテーブル(TEST_A、TEST_B)を作成します。

$ sqlcmd
CREATE TABLE TEST_A (
   ID varchar(50) NOT NULL,
   DATA smallint,
   PRIMARY KEY (ID)
);
PARTITION TABLE TEST_A ON COLUMN ID;

CREATE TABLE TEST_B (
   ID varchar(50) NOT NULL,
   DATA smallint,
   PRIMARY KEY (ID)
);
PARTITION TABLE TEST_B ON COLUMN ID;

次に以下の2つのストアドプロシージャを作成します。

・InsertDataToAAndB: テーブルAとテーブルBにインサートする。
・InsertDataToAAndBFail: InsertDataToAAndBを流用し例外を追加。

InsertDataToAAndB.java
package test;

import org.voltdb.SQLStmt;
import org.voltdb.VoltProcedure;

public class InsertDataToAAndB extends VoltProcedure {

    public final SQLStmt insertTestA = new SQLStmt("insert into test_a values(?, ?)");
    public final SQLStmt insertTestB = new SQLStmt("insert into test_b values(?, ?)");

    public long run(String idA, int dataA, String idB, int dataB) throws VoltAbortException {

        voltQueueSQL(insertTestA, idA, dataA);
        voltExecuteSQL();
//InsertDataToAAndBFailクラスの場合はここで例外をスローしている。
        voltQueueSQL(insertTestB, idB, dataB);
        voltExecuteSQL();
        return 2L;
    }
}

作成したプロシージャはコンパイルし、catalog.jarを作成します。

VoltDBサーバに先ほど作成したcatalog.jarをアップロードし、以下のコマンドでプロシージャをロードします。

$ sqlcmd                                               3
1> load classes catalog.jar;
2> show classes;
--- Potential Procedure Classes ------------------------------
 test.InsertDataToAAndB    
 test.InsertDataToAAndBFail

「Potential Procedure Classes」でプロシージャがロードされていることが分かります。
次にプロシージャを作成(定義)するため、以下のコマンドを実行します。
2つのプロシージャが「Active Procedure Classes」になります。

$ sqlcmd
1> CREATE PROCEDURE FROM CLASS test.InsertDataToAAndB;
2> CREATE PROCEDURE FROM CLASS test.InsertDataToAAndBFail;
--- Active Procedure Classes ---------------------------------
 test.InsertDataToAAndB    
 test.InsertDataToAAndBFail

作成したプロシージャを実行し、トランザクション処理を確認します。

InsertDataToAAndBを呼び出し、TEST_AとTEST_Bテーブルに1レコードずつインサートします。

$ sqlcmd
> exec InsertDataToAAndB "1" 100 "1" 200;
(Returned 2 rows in 0.00s)

TEST_A, TEST_Bテーブルに1レコードずつ挿入されていることを確認します。

> select * from test_a;
ID   DATA 
---- -----
"1"    100

(Returned 1 rows in 0.01s)
> select * from test_b;
ID   DATA 
---- -----
"1"    200

(Returned 1 rows in 0.00s)

次にTEST_Aに挿入後、例外を発生させてロールバックさせた場合を確認します。

> exec InsertDataToAAndBFail "2" 100 "2" 200;
VOLTDB ERROR: USER ABORT
  test exception
    at test.InsertDataToAAndBFail.run(InsertDataToAAndBFail.java:16)

例外発生でロールバックされ、TEST_A, TEST_Bテーブルには元から存在した1レコードずつしかないことが確認できます。

> select * from test_a;
ID   DATA 
---- -----
"1"    100

(Returned 1 rows in 0.00s)

> select * from test_b;
ID   DATA 
---- -----
"1"    200

(Returned 1 rows in 0.00s)

終わりに

VoltDBでトランザクション処理ができることを確認しました。
トランザクション処理はできたのですが、複数テーブルを更新する際にパーティションキーを指定する方法が分からなかった。今回のやり方だとマルチパーティションになってしまい性能が落ちてしまう(と思われる)。それが仕様なのか設定・コーディングが悪いのかは調べてみる必要あり。

なお、ストアドプロシージャの実行計画は以下のようになっており、「RECEIVE FROM ALL PARTITIONS SEND PARTITION RESULTS TO COORDINATOR」から、マルチパーティションで処理されているので間違いないようです。

image.png

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0