JDBC-Redisは使えない
MyCassandraとは別にCassandraのMemTable,SSTableを別のデータベースに差し替えてみようと考え、試しにKVSのRedisに注目した。
Redisは簡単に言うと永続化可能なmemcachedであり、
通常はオンメモリなKVSと動作し、インターバルごと、書き込みの頻度、もしくはクライアントの明示的な命令でHDD上にファイルとして書きだすことが出来ます。
http://code.google.com/p/redis/
MyCassandraではJDBCを経由してCassandraからMySQLに読み書きを行うというものでしたので、できればRedisに対してもJDBCを使えれば公平に比較出来そうかなと思ったので、幾つかクライアントライブラリが実装されている中でJDBC-Redisを選択。
JDBC-RedisはMySQLと同様に、JDBC用のドライバーを使ってJavaからRedisにアクセスができるようで、簡単に実装出来ました。
しかし、いざ実行してみると...
JDBC-Redis、バイナリ配列のput/getが実装されていないという驚愕の事実を知るorz
もちろん、Redis自体はどんなデータ型も扱えます。ドライバ側が実装されてなかったという話です。
うーむ。。
仕方がないので、別のJava実装であるJRedisからRedisにアクセスしてみようと思う。こちらの方が開発は進んでてバイナリ配列は使えることは既に分かってるのだが、結構使うのはJDBCと違って難しそう。
出来ればJDBC-Redisの実装を頑張ってもらいたいのだが。。
YCSBパラメータ
実行パラメータ
- load: loading phaseを実行
- t: transaction phaseを実行
- db: 使用するdbインタフェース層
- P: workload用のファイル
- hosts: サーバーホスト(コンマで区切って複数指定可能)
- recordcount: ロードするレコード数(デフォルトは1,000)
- threads: YCSBクライアントスレッド数(デフォルトは1)
- target: 1secあたりの命令数
- measurementtype:値に"timeseries"を指定することで結果を時系列表示可能
- timeseries.granularity:平均遅延を測定するインターバルをms単位で指定(デフォルトは1000=>1sec)
Workloadパラメータ
以下に書かれている
http://wiki.github.com/brianfrankcooper/YCSB/core-properties
カッコはデフォルト値
- fieldcount:レコードのフィールド数(10)
- fieldlength: 各フィールドのサイズ(100)
- デフォルトでは長さ100のフィールドが10個+プライマリキーで1KBになっている
- readallfields: read時に読むべきフィールドが全部:trueか1つ:falseか(true)
- readprportion: 命令群のうちreadの比率(0.95)
- updateproportion: updateの比率(0.05)
- insertproportion: insertの比率(0)
- scanproportion: scanの比率(0)
- readmodifywriteproportion: 1レコード読んで変更してまた書き込むという命令の比率(0)
- requestdistribution: 命令の対象となるレコードの選択分布.uniform,zipfian,latest(uniform)
- maxscanlength: scan命令でスキャンするレコード最大数
- scanlengthdistribution: scan命令の対象となるレコードの選択分布(uniform)
- insertorder: insertするレコードの順序.ordered,hashed.(hashed)
YCSB導入
実装したMyCassandraをフォーマルなクラウドベンチマークで測定する為にgithubのWikiを参考にYCSBを導入した。
http://wiki.github.com/brianfrankcooper/YCSB/getting-started
まずはCassandraについて動かせるようにしたのでメモしておく。
Cassandraのバージョンはここでは0.X.Yとしておく。(現状はx=5,6,7に対応)
ビルド
ソースをgitで取って、ant
git clone git://github.com/brianfrankcooper/YCSB.git
cd YCSB
ant
Cassandraのlib以下のjarファイル全てをYCSBのdb/cassandra-0.X/lib/にコピーして、DBインタフェース層のコンパイルを個別に行う
cd apache-cassandra-0.X.Y/lib/
cp *.jar ~/YCSB/db/cassandra-0.X/lib/
ant dbcompile-cassandra-0.X
Quick Start
実際の起動前に測定するシステムの個別設定が必要。
参考までにMac上でCassandraについて動かせるようにしたのでメモ。
- db/cassandra- 0.X/libに対してCLASSPATHを通す
- CassandraのKeyspace,ColumnFamily?を設定
- Keyspace 名を"usertable",ColumnFamily名を"data"にする@storage-conf.xml
- インタフェース層のプロパティで名前は設定できる
- Cassandraの起動
- WorkLoad Aのload phase
- WorkLoad Aのtransaction phase
- java -cp build/ycsb.jar com.yahoo.ycsb.Client -t -db com.yahoo.ycsb.CassandraClientX -P workloads/workloada -P middle.dat -s > transactions.dat
No More KVS
Cassandraよりも早いオンメモリRDBとされるVoltDBについて調べてて思ったのだが、
一般的なRDBは例えオンメモリでもKVSには遠く及ばないわけだけど、
そこで原因になってるSQLレイヤーを介さずにC++によるUDF使って直接RDBのストレージにアクセスする手法はどうだろうか。
http://labs.cybozu.co.jp/blog/kazuho/archives/2008/06/mysql_direct_access.php
こことかを見る限りでは実際のところストアドプロシージャよりも10数倍早くなるみたいだし、KVSと良い勝負できそうじゃないか。
よし、実装にこれを取り入れてみよう。
MyCassandra測定
Yahoo PNUTSの論文を読んでみて、
別にNoSQL使わなくてもMySQLでも性能は出るんじゃないかと思い、
CassandraのSSTableをMySQLに入れ替えたMyCassandraを実装しました。
シーケンシャルな書き込みを重視するCassandraですが、MyCassandraではMySQL同様に書き込みは更新も取り扱い、読み込み時はCassandraのようにMutationのマージ処理が必要ありません。
以下、iMac1ノード(Core 2 Quad, 4GBメモリ)上でとった簡単な測定
Cassandraは最新の0.6.2を、以下のようにMemtableのサイズを調整して測定。MyCassandraはソースが公開されている0.6.0を元に実装しました。
dbname | Memtable |
Cassandra.L | 128M |
Cassandra.M | 64M |
Cassandra.S | 4M |
Cassandra.N | 0 |
また、MySQLのストレージエンジンにはInnoDBを用い、cnfファイルはmy-innodb-heavy-4G.cnfをそのまま使っています。
※測定結果でスターがついてるのはMemTableがいっぱいになりSSTableに非同期で古いMemTableを書き出しています。
1024 バイトのランダム文字列の単一カラムから成るN件のRowを順にputして、ランダムに全てのRowをgetする
put
db name\N | 1000 | 10000 | 50000 | 100000 |
Cassandra.L | 1.63 | 15.15 | 71.94 | 141.76 |
Cassandra.M | 1.63 | 15.15 | 72.01 | 142.26 |
Cassandra.S | 1.63 | 15.04 | 72.18* | 142.45* |
Cassandra.N | 5.03* | 44.70* | 106.95* | 203.20* |
MyCassandra | 1.61 | 14.82 | 70.49 | 136.72 |
get
db name\N | 1000 | 10000 | 50000 | 100000 |
Cassandra.L | 0.53 | 2.36 | 8.76 | 16.86 |
Cassandra.M | 0.53 | 2.37 | 8.77 | 24.08 |
Cassandra.S | 0.53 | 3.79 | 14.45* | 27.08* |
Cassandra.N | 0.85* | 3.87* | 17.36* | 29.96* |
MyCassandra | 0.69 | 4.03 | 17.21 | 32.89 |
512バイトのランダム文字列のカラムから成るN件のRowをランダムな順でそれぞれM回書き込んで読むこむ
M=5
db name\N | 1000 | 10000 | |
Cassandra.L | 6.34 | 50.65 | |
Cassandra.M | 6.36 | 50.70 | |
Cassandra.S | 6.35 | 57.29* | |
Cassandra.N | 16.90* | 100.06* | |
MyCassandra | 7.17 | 56.44 |
考察
特にindexingやbloom filterなどの最適化をしていないMyCassandraですが、少し最適化を加えればCassandraといい勝負ができそうな感じ。
やはり、データがオンメモリ(on MemTable)かどうかでgetのパフォーマンスに影響を与えるもよう。その点ではMyCassandraのMySQLの設定も大きく関わってくるような気がした。そもそもどのようなメモリ条件にすればCassandraとMyCassandraがイーブンになるかを調査する必要がある。
一方、MyCassandraはCassandraと比べると多くのヒープメモリを利用してるようでN=100000件に対してJVMのヒープサイズが1GBだと足りなかった。この問題を解決する必要がある。
今後は複数ノードで測定することと、YCSBを使った複雑なクエリに対して性能評価したい。
Cassandraのロック機能
- ReentrantReadWriteLockクラスを使ったロック
- 読み込みロックは複数のスレッドが取得できる
- 書き込みロックは1つのスレッドのみが取得できる
- 読み込みロックの取得中は、他のスレッドは書き込みロックを取得できない
- 書き込みロックの取得中は、他のスレッドは読み込みロックを取得できない
- 読み込みロック
- commitLog,MemTableにRowMutationを書き込む処理
- Memtable上の値の読み込み処理(SSTableの読み込みはlock free)
- 書き込みロック
- 古いMemTableの swap処理
- ノードTokenの追加や更新管理
MemTable書き込み時にWriteLockを取得しなくていいのだろうか。
Love Is A Many Splendored Thing
邦題では「慕情」というのでしょうか。哀愁ただよう良い曲です。
今回紹介したいのは2つのアーティスト
5拍子を絡めた急速調な演奏, ソロは4拍子。超カッコいい。
- Roma Trio
Luca Mannutza(P)
Gianluca Renzi(B)
Nicola Angelucci(Ds)
テンポが2段階変わります。最初はミディアム・スローから始まって、次は3連符の頭2つを1拍に見立ててミディアム・ファーストに転調。ピアノのアドリブに入ってからは最初の3連符を倍テンにしてアップテンポでガンガン突き進んでいる。ベースソロではまたテンポ・ダウンする。単に倍テンにするだけだったら誰でもやっていることだけど、このミディアム・ファーストの部分が他では見られないアイデアだなぁ。