インメモリデータベース VoltDBのインデックスの概要と作成方法

# VoltDBのインデックス

VoltDBのインデックスは基本的にOracleなどのRDBと変わりはないため、概要を理解するのは容易だと思います。他のRDBと異なる点を中心に、VoltDBのインデックスについて説明します。
※v8.2をベースに記載していますが、v8.3, v8.4, v9.0でも同様です。

VoltDBのインデックスは、ツリーインデックスとハッシュインデックスがあります。ただし、ハッシュインデックスは非推奨で将来無くなる可能性があるため、実質ツリーインデックスのみが利用できます。

# 一意制約

一意性を保証するため、UNIQUEキーワードをサポートします。
VoltDBでは一意制約が各パーティションで別々に適用されます。そのため、パーティションカラム以外のカラムはグローバルに一意であることは保証されません。
グローバルに一意であることが保証されるのは、レプリケートテーブル、パーティションテーブルのパーティションカラムを含むインデックスの場合となります。

ただし、パーティションカラム以外のカラムも完全ではありませんが一意制約を持たせることができます。
その場合、UNIQUEの代わりにASSUMEUNIQUEを使用します。
これは各パーティション内でだけ一意であるため、グローバルに一意であるかは使う側が保証しなければなりません。また、パーティション内でデータが移動されると一意制約違反が発生することがあります。

# 複合インデックス

複数のカラムからなるインデックスを複合インデックスと呼び、VoltDBは複合インデックスに対応しています。

# インデックスの作成方法

インデックスを作成する方法はいくつかあります。

- PRIMARY KEY（主キー）
- UNIQUEまたはASSUME UNIQUE制約
- CREATE INDEX文

以降で実際にインデックスを作成してみます。

## PRIMARY KEY（主キー）

PRIMARY KEY（主キー）を定義することで自動でインデックスが作成されます。
以下は、VOTES_TESTテーブルのPHONE_NUMBERを主キーにしています。

```
# sqlcmd
> CREATE TABLE VOTES_TEST (
   PHONE_NUMBER bigint NOT NULL,
   STATE varchar(2) NOT NULL,
   CONTESTANT_NUMBER integer NOT NULL,
   PRIMARY KEY (PHONE_NUMBER)
);
Command succeeded.
> PARTITION TABLE VOTES_TEST ON COLUMN PHONE_NUMBER;
Command succeeded.
```

Web管理コンソールでは以下のように、主キーが定義されていることが分かります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/179607/9e89b1ce-c07a-6bcc-59a5-bf693fb4d219.png)

次に、複数のカラムからなる主キーの例です。

```
# sqlcmd
> CREATE TABLE VOTES_TEST2 (
   PHONE_NUMBER bigint NOT NULL,
   STATE varchar(2) NOT NULL,
   CONTESTANT_NUMBER integer NOT NULL,
   PRIMARY KEY (PHONE_NUMBER, STATE)
);
Command succeeded.
> PARTITION TABLE VOTES_TEST2 ON COLUMN PHONE_NUMBER;
Command succeeded.
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/179607/7e766e70-4773-b189-b9be-9d15e00e9c60.png)


## UNIQUEまたはASSUME UNIQUE制約

カラムにUNIQUEまたはASSUME UNIQUE制約を付与することで、自動でインデックスが作成されます。
以下、UNIQUE制約の例です。

```
# sqlcmd
> CREATE TABLE VOTES_TEST4 (
   PHONE_NUMBER bigint NOT NULL,
   STATE varchar(2) NOT NULL,
   CONTESTANT_NUMBER integer NOT NULL,
   UNIQUE(PHONE_NUMBER)
);
Command succeeded.
> PARTITION TABLE VOTES_TEST4 ON COLUMN PHONE_NUMBER;
Command succeeded.
```
※PHONE_NUMBER bigint NOT NULL UNIQUEとしても同じです。

以下、ASSUME UNIQUE制約の例です。

```
# sqlcmd
> CREATE TABLE VOTES_TEST5 (
   PHONE_NUMBER bigint NOT NULL,
   STATE varchar(2) NOT NULL,
   CONTESTANT_NUMBER integer NOT NULL,
   ASSUMEUNIQUE(STATE)
);
Command succeeded.
> PARTITION TABLE VOTES_TEST5 ON COLUMN PHONE_NUMBER;
Command succeeded.
```

## CREATE INDEX

CREATE INDEXの構文は以下のようになります。

```
CREATE [UNIQUE|ASSUMEUNIQUE] INDEX index-name
ON {table-name | view-name} ( index-column [,...])
[WHERE [NOT] boolean-expression [ {AND | OR} [NOT] boolean-expression]...]
```

以下のVOTESテーブルを作成します。

```
CREATE TABLE VOTES (
   PHONE_NUMBER bigint NOT NULL,
   STATE varchar(2) NOT NULL,
   CONTESTANT_NUMBER integer NOT NULL
);
PARTITION TABLE VOTES ON COLUMN PHONE_NUMBER;
```

VOTESテーブルに対して、いくつかのパターンのインデックスを作成してみます。

```
# sqlcmd
STATEカラムはパーティションカラムではないため、UNIQUEではなく、ASSUMEUNIQUEインデックスしか作成できません。
> CREATE ASSUMEUNIQUE INDEX VOTES_IDX01 ON VOTES(STATE);
Command succeeded.
> CREATE ASSUMEUNIQUE INDEX VOTES_IDX02 ON VOTES(STATE, CONTESTANT_NUMBER);
Command succeeded.
> CREATE UNIQUE INDEX VOTES_IDX03 ON VOTES(PHONE_NUMBER);
Command succeeded.
> CREATE UNIQUE INDEX VOTES_IDX04 ON VOTES(PHONE_NUMBER, STATE);
Command succeeded.

```

Web管理コンソールでは以下のように、作成した4つのインデックスを見ることができます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/179607/79c57d0f-41b7-a769-3ecf-e31decdce20a.png)

なお、インデックスの作成は空のテーブルに対してのみ実行できます。テーブルにレコードが存在する場合は、実行すると以下のようにエラーとなります。

```
> CREATE ASSUMEUNIQUE INDEX VOTES_IDX01 ON VOTES(STATE);
VOLTDB ERROR:   Unable to make requested schema change:
 Unable to add unique index VOTES_IDX01 because table VOTES is not empty.
```

# インデックスを削除する

インデックスはDROP INDEX文で削除します。DROP INDEX文の構文は以下のとおりです。

```
DROP INDEX index-name [IF EXISTS]
```

IF EXISTSをつけると、インデックスが存在していなくてもエラーになりません。
なお、自動で生成された場合はインデックス名がないので削除することができません。UNIQUEまたはASSUME UNIQUE制約では自動でインデックス名が生成されるため、インデックスはCREATE INDEXで作成する方が良いということになります。

以下、DROP INDEXの実行例です。

```
> DROP INDEX VOTES_IDX01; 
Command succeeded.
```

# その他

ALTER TABLE文でUNIQUE、ASSUMEUNIQUE制約を追加することで、暗黙的にインデックスを追加することもできます。また、この場合もテーブルにデータが入っている場合は変更することはできません。

# 参考

VoltDBの公式ドキュメント

- Using VoltDB CREATE INDEX  
https://docs.voltdb.com/UsingVoltDB/ddlref_createindex.php
- Using VoltDB CREATE TABLE  
https://docs.voltdb.com/UsingVoltDB/ddlref_createtable.php
- Using VoltDB DROP INDEX  
https://docs.voltdb.com/UsingVoltDB/ddlref_dropindex.php
- Using VoltDB 4.6. Modifying the Schema  
https://docs.voltdb.com/UsingVoltDB/SchemaModify.php