YCSB (Yahoo Cloud Serving Benchmark)でインメモリデータベースVoltDBの性能を計測する

# はじめに

YCSBを使用してインメモリデータベースVoltDBの性能を計測しています。
YCSBはYahoo! Cloud Serving Benchmarkの略で、 Yahoo! Research が開発したNoSQL用のベンチマークツールです。
YCSBはデフォルトでCassandraやRedis等の多くのNoSQLに対応しているのですがVoltDBには対応していません。VoltDB側でクライアントが用意されているのでそちらを利用します。


# 環境設定

YCSBを以下からダウンロードします。

https://github.com/brianfrankcooper/YCSB/releases/download/0.15.0/ycsb-0.15.0.tar.gz

YCSBを展開し、環境変数YCSB_HOMEを設定します。

```
tar xzvf ycsb-0.15.0.tar.gz -C /opt
export YCSB_HOME="/opt/ycsb-0.15.0"
```

VoltDBのYCSBクライアントは以下にあります。

https://github.com/VoltDB/voltdb/tree/master/tests/test_apps/ycsb

これを/opt以下に格納します。

以下のコマンドでVoltDB用のYCSBクライアントをコンパイルします。

```
# cd /opt/ycsb
# chmod 755 run.sh
# ./run.sh jars
```

ベンチマークに必要なSTOREテーブルとプロシージャを作成します。

```
# ./run.sh init
```

作成されたテーブルのDDLは以下のようになっています。
（ycsb_ddl.sqlファイルから抜粋）

```ycsb_ddl.sql
CREATE TABLE Store
(
    keyspace VARBINARY(128)  NOT NULL
,   key      VARCHAR(128)    NOT NULL
,   value    VARBINARY(2056) NOT NULL
,   PRIMARY KEY (keyspace, key)
);
PARTITION TABLE Store ON COLUMN key;
```

次にさきほど作成したSTOREテーブルにベンチマーク用のデータをロードします。

```
# ./run.sh load
```

使用される設定ファイルは「base.properties」と「load.properties」の2ファイルです。以下のように修正して実行しました。

```base.properties
voltdb.servers=localhost
threadcount=150
fieldcount=5 # 100から変更
fieldlength=10 # 100から変更
```

```load.properties
# load で挿入するレコード数
recordcount=2000000 # 10000000から変更
# run で実行されるクエリの回数
operationcount=2000000 # 10000000から変更
# workload を実行するクラス
workload=com.yahoo.ycsb.workloads.CoreWorkload
```

ベンチマーク用のデータの準備なのですが、これ自体もベンチマーク結果が以下のように出力されました。
秒間スループットが24,184件となっています。

```
[OVERALL], RunTime(ms), 82696
[OVERALL], Throughput(ops/sec), 24184.96662474606
[TOTAL_GCS_Copy], Count, 127
[TOTAL_GC_TIME_Copy], Time(ms), 223
[TOTAL_GC_TIME_%_Copy], Time(%), 0.2696623778659185
[TOTAL_GCS_MarkSweepCompact], Count, 3
[TOTAL_GC_TIME_MarkSweepCompact], Time(ms), 28
[TOTAL_GC_TIME_%_MarkSweepCompact], Time(%), 0.03385895327464448
[TOTAL_GCs], Count, 130
[TOTAL_GC_TIME], Time(ms), 251
[TOTAL_GC_TIME_%], Time(%), 0.303521331140563
[CLEANUP], Operations, 150
[CLEANUP], AverageLatency(us), 801.54
[CLEANUP], MinLatency(us), 2
[CLEANUP], MaxLatency(us), 114431
[CLEANUP], 95thPercentileLatency(us), 18
[CLEANUP], 99thPercentileLatency(us), 3325
[INSERT], Operations, 2000000
[INSERT], AverageLatency(us), 6024.355673
[INSERT], MinLatency(us), 69
[INSERT], MaxLatency(us), 294911
[INSERT], 95thPercentileLatency(us), 16143
[INSERT], 99thPercentileLatency(us), 25855
[INSERT], Return=OK, 2000000
```

# ベンチマークを実行する

ベンチマークを実行します。「workloada」はベンチマークのシナリオで、50%の読み込みと50%の書き込みのシナリオになります。

```
./run.sh workload workloada
```

使用される設定ファイルは「base.properties」と「workload.properties」の2ファイルです。「workload.properties」は以下のように修正して実行しました。

```workload.properties
maxexecutiontime=600
# load で挿入するレコード数
recordcount=2000000 # 10000000から変更
# run で実行されるクエリの回数
operationcount=2000000 # 1000000000から変更
```

また、ベンチマークの種類は以下のページが参考になりました。
[YCSBの概要とCassandraへのベンチマーク](
https://qiita.com/noralife/items/35a956f682b1aca475f6)

実行するとベンチマーク完了後に以下のような結果が表示されます。
秒間スループットが全体で23,723件になっています。

```
[OVERALL], RunTime(ms), 84306
[OVERALL], Throughput(ops/sec), 23723.10393091832
[TOTAL_GCS_Copy], Count, 100
[TOTAL_GC_TIME_Copy], Time(ms), 201
[TOTAL_GC_TIME_%_Copy], Time(%), 0.23841719450572912
[TOTAL_GCS_MarkSweepCompact], Count, 3
[TOTAL_GC_TIME_MarkSweepCompact], Time(ms), 27
[TOTAL_GC_TIME_%_MarkSweepCompact], Time(%), 0.032026190306739734
[TOTAL_GCs], Count, 103
[TOTAL_GC_TIME], Time(ms), 228
[TOTAL_GC_TIME_%], Time(%), 0.2704433848124689
[READ], Operations, 999883
[READ], AverageLatency(us), 6112.663878673805
[READ], MinLatency(us), 58
[READ], MaxLatency(us), 391679
[READ], 95thPercentileLatency(us), 18431
[READ], 99thPercentileLatency(us), 30815
[READ], Return=OK, 999883
[CLEANUP], Operations, 150
[CLEANUP], AverageLatency(us), 1108.56
[CLEANUP], MinLatency(us), 1
[CLEANUP], MaxLatency(us), 162943
[CLEANUP], 95thPercentileLatency(us), 21
[CLEANUP], 99thPercentileLatency(us), 1658
[UPDATE], Operations, 1000117
[UPDATE], AverageLatency(us), 6134.580398093423
[UPDATE], MinLatency(us), 68
[UPDATE], MaxLatency(us), 346623
[UPDATE], 95thPercentileLatency(us), 18495
[UPDATE], 99thPercentileLatency(us), 31071
[UPDATE], Return=OK, 1000117

```

VoltDBだと、CPU負荷等の情報もWeb画面で確認できて便利です。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/179607/3e659111-2916-6b15-3114-bdf4ea826b9c.png)


# 参考

YCSBについては以下の情報が参考になりました。

- [YCSBの概要とCassandraへのベンチマーク](https://qiita.com/noralife/items/35a956f682b1aca475f6)
- [YCSBでNoSQLのベンチマーク　その1](http://imai-factory.hatenablog.com/entry/2013/03/24/174624)
- [YCSBでNoSQLのベンチマーク　その2](http://imai-factory.hatenablog.com/entry/2013/04/05/010858)


# VoltClient4.javaの修正

VoltDB用のクライアント（VoltClient4.java）はコンパイルが通らなかったので、以下のように修正しています。

```
# diff VoltClient4.java VoltClient4.java.kaizou 
34a35
> import java.util.Map;
109c110
<     public Status insert(String keyspace, String key, HashMap<String, ByteIterator> columns)
---
>     public Status insert(String keyspace, String key, Map<String, ByteIterator> columns)
115c116
<     public Status read(String keyspace, String key, Set<String> columns, HashMap<String, ByteIterator> result)
---
>     public Status read(String keyspace, String key, Set<String> columns, Map<String, ByteIterator> result)
174c175
<                     result.add(unpackRowData(table, columns));
---
>                     result.add((HashMap)unpackRowData(table, columns));
193c194
<     public Status update(String keyspace, String key, HashMap<String, ByteIterator> columns)
---
>     public Status update(String keyspace, String key, Map<String, ByteIterator> columns)
251c252
<     private byte[] packRowData(HashMap<String, ByteIterator> columns)
---
>     private byte[] packRowData(Map<String, ByteIterator> columns)
273c274
<     private HashMap<String, ByteIterator> unpackRowData(VoltTable data, Set<String> fields)
---
>     private Map<String, ByteIterator> unpackRowData(VoltTable data, Set<String> fields)
279c280
<         HashMap<String, ByteIterator> res = new HashMap<String, ByteIterator>(size, (float) 1.25);
---
>         Map<String, ByteIterator> res = new HashMap<String, ByteIterator>(size, (float) 1.25);
283c284
<     private HashMap<String, ByteIterator> unpackRowData(VoltTable data, Set<String> fields, HashMap<String, ByteIterator> result)
---
>     private Map<String, ByteIterator> unpackRowData(VoltTable data, Set<String> fields, Map<String, ByteIterator> result)
291c292
<     private HashMap<String, ByteIterator> unpackRowData(byte[] rowData, ByteBuffer buf, int nFields, Set<String> fields, HashMap<String, ByteIterator> result)
---
>     private Map<String, ByteIterator> unpackRowData(byte[] rowData, ByteBuffer buf, int nFields, Set<String> fields, Map<String, ByteIterator> result)
```