インメモリデータベース　VoltDBの最新バージョン（v8.3.1）をインストールする（シングル構成）

# はじめに

VoltDBは2018年10月時点で最新バージョンがv8.3.1です。
最新版のv8.3.1をシングル構成で構築します。

[VoltDB 最新版のリリースノート](https://docs.voltdb.com/ReleaseNotes/)

v9.0もリリースされていますが、インストール手順はv8.3.xと同じです。

クラスタ構成での構築手順は以下の記事になります。

[インメモリデータベース　VoltDBの最新バージョン（v8.3.1）をインストールする（クラスタ構成）](https://qiita.com/drafts/1a1f8c9ce7f91293a5b1/edit)

# VoltDBとは

VoltDBはインメモリRDBで、PostgreSQLに携わったマイケル・ストーンブレーカーにより設定されています。
NoSQLより高速でありながら、SQLを利用できトランザクションもサポートされています。

最近はあまり話題にならない（？）ですが、「2011年の国民的アイドルグループ選抜総選挙」で利用されたときに話題になりました。
私も購入したのですが、舞台裏は以下の本に書いています（技術的に深い内容ではありませんが）。

[過負荷に耐えるWebの作り方 ~国民的アイドルグループ選抜総選挙の舞台裏 (Software Design plus)](http://www.amazon.co.jp/exec/obidos/ASIN/4774162051/hatena-blog-22/)

興味がある方は以下も参考になるかと思います。

VoltDBを使ってみる
https://qiita.com/ytake/items/4da625997079716404ad
VoltDB(Wikipedia)
https://ja.wikipedia.org/wiki/VoltDB

# 環境と構築手順の概要

VirtualBox上にCentOS 7.4を1台起動し、VoltDBをシングル構成で構築します。
ローカル環境なので、firewalldとSELinuxは無効化しています。

サーバは以下の1台です。
・192.168.10.171 volt1


構築では以下の順番で実施します。

1. OpenJDKのインストール
1. NTPの設定
1. VoltDBのインストール
1. VoltDB起動
1. テーブルを作成

# 構築手順

## OpenJDKのインストール

OpenJDK 8かOracleJDK 8をインストールします。
今回はOpenJDKをインストールしましたが、OracleJDKが推奨されています。
[2018/10/4追記]
OracleJDKが推奨されていますが、OpenJDKもサポートされています。

```
# yum install java-1.8.0-openjdk-devel

# java -version
openjdk version "1.8.0_191"
OpenJDK Runtime Environment (build 1.8.0_191-b12)
OpenJDK 64-Bit Server VM (build 25.191-b12, mixed mode)
```

JAVA_HOMEを設定し、PATHにJavaのパスを追加しています。

```terminal
# echo "export JAVA_HOME=$(readlink -e $(which java)|sed 's:/bin/java::')" > /etc/profile.d/java.sh
# echo "PATH=\$PATH:\$JAVA_HOME/bin" >> /etc/profile.d/java.sh
# source /etc/profile
```

## NTPの設定

クラスタ構成の場合はVoltDBの各サーバ間での時刻のずれが100ms未満である必要があるため、NTPで同期させます。今回はシングル構成なので省略します。


## firewalldの設定

VoltDBがデフォルトで使用するポートの一覧は以下のとおりです。

Client Port: 21212
Admin Port: 21211
Web Interface Port (httpd): 8080
Web Interface Port (with TSL/SSL enabled): 8443
Internal Server Port: 3021
Replication Port: 5555
Zookeeper port: 7181

この内、「Web Interface Port (with TSL/SSL enabled)」と「Replication Port」は使用する場合だけポートを解放すればよいです。
※3021, 5555, 7181はシングル構成では不要かもしれません。

```
firewall-cmd --add-port=21212/tcp --permanent
firewall-cmd --add-port=21211/tcp --permanent
firewall-cmd --add-port=8080/tcp --permanent
firewall-cmd --add-port=3021/tcp --permanent
firewall-cmd --add-port=5555/tcp --permanent
firewall-cmd --add-port=7181/tcp --permanent
firewall-cmd --add-port=123/udp --permanent
firewall-cmd --add-port=22/tcp --permanent
firewall-cmd --reload
```

以下のコマンドで設定を確認します。

```
firewall-cmd --list-ports --zone=public
21212/tcp 21211/tcp 8080/tcp 3021/tcp 5555/tcp 7181/tcp 22/tcp
```


## VoltDBのインストール

VoltDBのモジュールは以下の公式サイトから「COMMUNITY EDITION」をダウンロードします。
一部使用できない機能もありますが、プロダクション環境でも十分な機能が網羅されている（と思います）。

https://www.voltdb.com/try-voltdb/

![image.png](https://qiita-image-store.s3.amazonaws.com/0/179607/53c9ab85-ed7f-006e-ff84-42fc43ccbb98.png)

まず、voltdb-community-8.3.1.tar.gzを/opt以下に展開し、シンボリックリンクを作成します。

```
# tar xvzf voltdb-community-8.3.1.tar.gz -C /opt/
# ln -s /opt/voltdb-community-8.3.1/ /opt/voltdb
```

VoltDBでは透過的なHugePages（Transparent HugePage）を無効化する必要があります。これを実行しないとVoltDBが起動しません。
これだとOS起動後に毎回実行しないといけない。

```
# echo never > /sys/kernel/mm/transparent_hugepage/enabled
# echo never > /sys/kernel/mm/transparent_hugepage/defrag
```

OS起動時に自動実行するにはrc.localを編集します。
/etc/rc.localに以下を追記します。（CentOS 7.4の場合）

```
if test -f /sys/kernel/mm/transparent_hugepage/enabled; then
   echo never > /sys/kernel/mm/transparent_hugepage/enabled
fi
if test -f /sys/kernel/mm/transparent_hugepage/defrag; then
   echo never > /sys/kernel/mm/transparent_hugepage/defrag
fi
```

また、rc.localに実行権限も付与してください。

```
# chmod u+x /etc/rc.d/rc.local
```

最後にOSを再起動し、以下のコマンドを実行して両方ともneverになっていることを確認します。

```
# cat /sys/kernel/mm/transparent_hugepage/enabled
always madvise [never]
# cat /sys/kernel/mm/transparent_hugepage/defrag
always madvise [never]
```

今回使用するサーバ1台をhostsに入れます。

```
vi /etc/hosts
```

「/etc/hosts」に追加する内容は以下のとおりです。

```
192.168.10.171	volt1
```

次に「/opt/voltdb/bin」をパスに追加します。

```
vi /etc/profile.d/voltdb.sh
```

追加する内容は以下のとおりです。

```
export PATH="$PATH:/opt/voltdb/bin"
```

すぐに反映させるため、以下のコマンドを入力します。

```
source /etc/profile
```

## スキーマ・プロシージャの作成

テーブルやプロシージャは、インストール後に作成することもできるので、必ずしもインストール前に準備する必要はありません。


## voltdbユーザの作成

VoltDBの操作はvoltdbユーザを使用しますので、まずはユーザを作成します。

```console
# useradd -s /bin/bash -m voltdb -p voltdb
# chown -R voltdb:voltdb /opt/voltdb-community-8.3.1/
```

以降の操作はvoltdbユーザを使用して実行します。


## VoltDBの初期化と起動

標準設定でVoltDBを初期化します。
※-fをつけると強制的に初期化します。今回は初回なので必要ないですが既に初期化済みの場合は-fをつけます。

```
# cd /opt/voltdb
# voltdb init -D /opt/voltdb -f
```

VoltDBを起動します。

```
# cd /opt/voltdb
# voltdb start -D /opt/voltdb -B
```

起動時のオプションは以下のとおりです。今回はシングル構成なので、-Hと-cは省略できます。

-D：　VoltDBのルートディレクトリ（init時と同じディレクトリを指定）
-H：　接続先のクラスタのサーバ
-c：　クラスタ中のサーバ台数
-B：　バックグラウンドで実行



# VoltDBのWeb画面を開く

VoltDBでは標準でSQLを実行したり、パフォーマンスを見ることができるWebアプリケーションが起動します。
各サーバで実行されており、8080ポートでアクセスできます。

http://volt1:8080/

画面は以下のようになります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/179607/175d8d80-4345-30df-f3eb-6c7927751271.png)


# テーブルを作成する

VoltDBのコンソール（sqlcmd）から、テスト用のテーブルを作成します。

```bash
$ sqlcmd
1> CREATE TABLE TEST_A (
  2>    ID varchar(50) NOT NULL,
  3>    DATA smallint,
  4>    PRIMARY KEY (ID)
  5> );
Command succeeded.
6> PARTITION TABLE TEST_A ON COLUMN ID;
Command succeeded.
```


# クライアントプログラムを実行する

先ほど作成したテーブルへアクセスするクライアントプログラムを作成します。

```java
package sample;

import java.io.IOException;

import org.voltdb.VoltTable;
import org.voltdb.VoltType;
import org.voltdb.client.Client;
import org.voltdb.client.ClientFactory;
import org.voltdb.client.ClientResponse;
import org.voltdb.client.NoConnectionsException;
import org.voltdb.client.ProcCallException;

public class SingleTest {

	public static void main(String[] args) throws NoConnectionsException, IOException, ProcCallException {
		Client client = ClientFactory.createClient();
		client.createConnection("volt1");

		String msgId = "test1";
    	VoltTable[] results;
    	ClientResponse res;

    	res = client.callProcedure("TEST_A.insert", msgId, 1);

    	if (res.getStatus() == ClientResponse.SUCCESS) {
    		System.out.println("Insertに成功");
    	}

    	res = client.callProcedure("TEST_A.select", msgId);
    	results = res.getResults();

    	while (results[0].advanceRow()) {
    		Short value = (Short) results[0].get(1, VoltType.SMALLINT);
    		System.out.println("value = " + value);
    	}
	}
}
```

以下でVoltDBのサーバへ接続しています。

```java
		Client client = ClientFactory.createClient();
		client.createConnection("volt1");
```

VoltDBでDMLを実行する場合はストアドプロシージャを呼び出します。
テーブルを作成すると、insert, select, update, deleteのデフォルトストアドプロシージャが自動生成されます。
以下は、insertとselectのデフォルトストアドプロシージャの実行例です。

```java
    	res = client.callProcedure("TEST_A.insert", msgId, 1);

    	res = client.callProcedure("TEST_A.select", msgId);
```

自動で生成されたデフォルトストアドプロシージャは以下で確認できます。

```
$ sqlcmd
> list procedures;

～省略～
--- User Procedures ------------------------------------------
TEST_A.delete                   varchar
TEST_A.insert                   varchar, smallint
TEST_A.select                   varchar
TEST_A.update                   varchar, smallint, varchar
TEST_A.upsert                   varchar, smallint


```