分散データベースをベンチマークするチェックリスト

はじめに

ベンチマークとは、正確にちゃんとやろうとすると凄く時間のかかるもので、結局のところ、どこかを妥協しないといけないものだと思います。しかし時間を節約しようとサボってやると、何の知見も得られず、最悪誤った判断を与えて、信頼性を落としてしまうということが考えられます。なので、ソフトウェアの検証段階のうちは、ある程度の慎重性が求められるのではないでしょうか。

手短に正確にベンチマークするには

手短に正確にベンチマークをするためには、それからどのようなことを知りたいかを明確化することが重要だと思います。研究の場合は、自分の提案した手法なりシステムが従来のものと比べて特徴的な部分を中心に計測をすると良いですが、私のようにOSSを使ってサービスを運用していくエンジニアの場合は、例えば、以下のようなことを知りたいかもしれません。

  • そもそも動作するのか
  • 基本的な性能性質
    • (データ数に関わらずクエリが早い・メモリに載らなくなると途端に性能劣化するなど)
  • データ数およびスレッド数が増えたときにどのように性能劣化するか
    • => スケールアウト or ソフトウェアそのものの入替のタイミングを知る
  • 使おうとしているアプリケーションの性質にマッチしているか
    • データの整合性・永続性、クエリの機能面・読み書きの性能性質

チェックリスト

データベースに限らず今後新しいOSS技術が出てきた時に検証およびベンチマークは必須だと思うので、どのような点に気をつけたら良いのか、チェックリストにまとめておきました。
「ベンチとりましたエントリ」には以下の記載はひと通り欲しいところですね。

設定
  • サーバー側
    • ハードウェア・ネットワーク
      • OS・CPU・メモリ・ディスクのversion・種類・サイズ等
      • ネットワーク帯域
    • ソフトウェア (DB)
      • ノード数 per マシン1台
      • メモリ割当 (buffer pool等)
      • master / slaveなど役割構成・データ複製数
      • ノード配置 (dc-aware / rack-aware)
      • クエリ (主に書き込み)がディスクに対して同期的に永続 or 非同期的に永続 or 揮発
      • 複製間の整合性
      • ファイルの最適化 (OPTIMIZE, コンパクションの設定)
      • ファイルシステム
      • ファイルディスクリプタ
  • クライアント側 (ベンチマーク実行側)
    • サーバー側同様にハードウェア的な内容
    • サーバーに対するクライアントノードの配置
    • 実行マシン数・インスタンス数・スレッド数
    • データ
      • ロード済データ数 (DB側のメモリに収まるサイズ・収まりきらないサイズ)
      • 主キー、及びバリューのサイズおよびデータ型、カラム数
      • 上記の偏り具合
    • データへのアクセス分布 (uniform/zipfian/latest/...)
    • ベンチマークオペレーション
      • オペレーション数
      • オペレーションの種類 (insert, update, get, delete, CAS, range query)
      • オペレーションのアクセス粒度 (full row, one or more columns)
      • オペレーションの比率 (実際のアプリケーションから算出できるとよい)
測定項目
  • オペレーションごとの遅延 (avg, min/max, 95th/99thPercentile, std)
  • スループット (qps)
  • system stats (特にio_tps, mem_space, cpu, load averageなど)

YCSB / web-api-benchmark

一般的なデータベースのベンチマークといえばTPCですが、NoSQLのベンチマークといえば、僕の中ではYCSBが主流となっています。YCSBを用いることで上で述べられた項目はひと通り算出できます。(一部、取得できない部分はOSSなので自分でコード書き足します)
最近では、SoCC2011に論文があがってるYCSB++というものもあるそうです。

ところで、このような便利なベンチマークをNoSQLにしか使えないというのも不便なので、NoSQLだけでなく一般のWeb APIに対してベンチマークができるようなツールを今作ってます。

web-api-benchmark

まだ書き殴った程度でソースを見せるのは恥ずかしいですが、
汎用かつ正確にかつ様々な情報を取得できるようなものを作っていきたいと考えています。

EC2 AMI作成&Auto Scaling設定スクリプト

すんすくです。
AWSで一番好きなサービスはRDSです。

AWS上でApacheなどstatelessな鯖をAuto Scalingで立ち上げたいことはよくあります。
instance storeなAMIでAuto Scalingする場合は以下の操作が必要です。

しかし現在のところ、まだAWS Mangement Consoleではこれらの設定ができないので、
API経由で行う必要があり、ゆとりには辛い。
特に後者は毎回手動でやる大変手間なので、スクリプトで自動化しました。


root deviceが"instance store"のインスタンスのAMI化用にスクリプトを書きます。


s3cmdはpython版を利用。
CloudFrontと連携できたり、ruby版より多機能で断然使いやすいです。

#!/bin/bash

nowtime=`date +"%Y%m%d%k%M"`
bucketname="ami-${nowtime}"
region="ap-northeast-1"
mnt="/mnt"
manifest="image.manifest.xml"

# create bucket for storing AMI
s3cmd mb s3://${bucketname} --bucket-location ${region}
# create image 
ec2-bundle-vol -d ${mnt} -k ${EC2_PRIVATE_KEY} -c ${EC2_CERT} -u ${AWS_ACCOUNT_ID}
# put image to bucket
ec2-upload-bundle -b ${bucketname} -m ${mnt}/${manifest} -a ${AWS_ACCESS_KEY_ID} -s ${AWS_SECRET_ACCESS_KEY}
# register image in bucket as AMI (この操作はwebからでも可能)
ec2-register ${bucketname}/${manifest} --region ${region} -n ${bucketname}

rm -r ${mnt}/image* ${mnt}/img-mnt

このスクリプトを作りたいAMIのインスタンス上で実行すると、ami-${time}というIDのAMIが作成されます。

このAMIを使ってAuto Scaling設定。

事前に以下を用意・決めておきます。

  • ELB: MyLoadBalancer
  • 増減させるAMI ID
    • 上のスクリプトで出力されるID または ec2-describe-imagesで確認
  • Auto Scaling名: testas (任意)
  • SNS topic: admin
    • subscriberを適宜登録しておく

Auto Scalingの設定スクリプト (as-action.pl)は以下の通り。こっちはperlで書きました。
変えなさそうな値は決め打ちしてあるので、適宜変数化して用いたいです。

#!/usr/bin/perl

use strict;
use Getopt::Long;

my $op = '';
our $keyname = "sunsuk7tp";
our $securitygroupname = "admin";
our $elbname = "MyLoadBalancer";
our $basename = "autoscale" . time;
our $imageid = '';
our $instancetype = "t1.micro";
our ($minsize, $maxsize) = (1,4);
our $policytype = "cpu";
our $period = 60;
our $region = "ap-northeast-1";
our $az = $region . "b";
our ($uthres, $dthres) = (0, 0);
our $topicname = "admin";

GetOptions(
  "op=s"		=> \$op, # start | stop | clear | restart | info
  "name=s"		=> \$basename, # label
  "imageid=s" 		=> \$imageid, # ami-XXXXXXXX
  "instancetype=s"	=> \$instancetype, # t1.micro | m1.small | m1.large
  "minsize=i"		=> \$minsize, 
  "maxsize=i"		=> \$maxsize,
  "policytype=s"	=> \$policytype, # cpu | drb | dro | dwb | dwo | nin | not 
  "period=i"		=> \$period, # >= 60
  "uthres=i"		=> \$uthres,
  "dthres=i"		=> \$dthres,
);

our $configname = $basename . "_config";
our $groupname = $basename . "_group";
our $scaleoutpolicyname = $basename ."_scaleout_policy";
our $scaleinpolicyname = $basename . "_scalein_policy";
our $scaleoutalarmname = $basename . "_scaleout_alarm";
our $scaleinalarmname = $basename . "_scalein_alarm";

if ($op eq 'start') {
  my $res = _register_as();
  start() if $res > 0;
  print "specify imageid \n" if $res < 0;
} elsif ($op eq 'restart') {
  restart();
} elsif ($op eq 'stop') {
  stop();
} elsif ($op eq 'clear') {
  as_delete();
} elsif ($op eq 'info') { 
  info();
} else {
  print "specify op param (start|restart|stop|clear|info)\n";
}

sub start {
  # set policy (scale in/out)
  my $alarm_scaleout_id  = `as-put-scaling-policy ${scaleoutpolicyname} --auto-scaling-group ${groupname} 
  --adjustment=1 --type ChangeInCapacity | tr -d '\n'`;
  my $alarm_scalein_id  = `as-put-scaling-policy ${scaleinpolicyname} --auto-scaling-group ${groupname}  
  --adjustment=-1 --type ChangeInCapacity | tr -d '\n'`;

  my $metricname = {
    "cpu" => 'CPUUtilization',
    "drb" => 'DiskReadBytes',
    "dro" => 'DiskReadOps',
    "dwb" => 'DiskWriteBytes',
    "dwo" => 'DiskWriteOps',
    "nin" => 'NetworkIn',
    "not" => 'NetworkOut',
  }->{$policytype};

  # set alarm (scale in/out)
  `mon-put-metric-alarm ${scaleoutalarmname} --comparison-operator GreaterThanThreshold --metric-name ${metricname} 
  --namespace \"AWS/EC2\" --period ${period} --statistic Average --threshold ${uthres} --evaluation-periods 1
  --dimensions \"AutoScalingGroupName=${groupname}\" --alarm-actions ${alarm_scaleout_id}`;
  `mon-put-metric-alarm ${scaleinalarmname} --comparison-operator LessThanThreshold --metric-name ${metricname} 
  --namespace \"AWS/EC2\" --period ${period} --statistic Average --threshold ${dthres} --evaluation-periods 1
  --dimensions \"AutoScalingGroupName=${groupname}\" --alarm-actions ${alarm_scalein_id}`;

  # register notification of the above alarm to the sns's topic specified by arnid
  my $arnid = `sns-list-topics | grep ${topicname} | tr -d '\n'`;
  `as-put-notification-configuration ${groupname} 
  --notification-types autoscaling:EC2_INSTANCE_LAUNCH,autoscaling:EC2_INSTANCE_LAUNCH_ERROR,
  autoscaling:EC2_INSTANCE_TERMINATE,autoscaling:EC2_INSTANCE_TERMINATE_ERROR --topic-arn ${arnid}`;
}

sub _register_as {
  return -1 if $imageid eq "";
  # create launch config
  `as-create-launch-config ${configname} --image-id ${imageid} 
  --instance-type ${instancetype} --key ${keyname} --group ${securitygroupname}`;
  # create as group
  `as-create-auto-scaling-group ${groupname} --launch-configuration ${configname} 
  --availability-zones ${az} --min-size ${minsize} --max-size ${maxsize} --load-balancers ${elbname} --region ${region}`;  
  return 1;
}

sub restart {
  `as-update-auto-scaling-group ${groupname} --min-size ${minsize} --max-size ${maxsize}`;
  start();
}

sub stop {
  my $arnid = `sns-list-topics | grep ${topicname} | tr -d '\n'`;
  `as-delete-notification-configuration ${groupname} --topic-arn ${arnid} -f`;

  `mon-delete-alarms ${scaleoutalarmname} -f`;
  `mon-delete-alarms ${scaleinalarmname} -f`;

  `as-delete-policy ${scaleoutpolicyname} --auto-scaling-group ${groupname} -f`;
  `as-delete-policy ${scaleinpolicyname} --auto-scaling-group ${groupname} -f`;

  # auto scaling stop
  `as-update-auto-scaling-group ${groupname} --min-size 0 --max-size 0 -f`;
}

sub as_delete {
  `as-delete-auto-scaling-group ${groupname} -f`;
  `as-delete-launch-config ${configname} -f`;
}

sub info {
  print "####LAUNCH CONFIG####\n";
  system("as-describe-launch-configs | grep ${configname}");
  print "####AUTO SCALING GROUP####\n";
  system("as-describe-auto-scaling-groups | grep ${groupname}");
  print "####POLICIES####\n";
  system("as-describe-policies | grep ${basename}");
  print "####ALARMS####\n";
  system("mon-describe-alarms | grep -e ${basename}");
  print "####NOTIFICATION####\n";
  system("as-describe-notification-configurations | grep ${groupname}");
}

このスクリプトでAuto Scalingの起動・再設定&再起動・停止・削除・情報取得ができます。

  • 起動 (指定AMIをsmall type、1~5台でCPU使用率90% or 30%で増減するauto scalingエントリを作成)
# ./as-action.pl --name=testas --op start --imageid=ami-XXXXXXXX --instancetype=m1.small
--minsize=1 --maxsize=5 --policytype=cpu --uthres=90 --dthres=30
  • 停止 (as config & groupは保持したまま、policy&alarmの削除し、起動インスタンス増減を0に)
# ./as-action.pl --name=testas --op stop
  • 再起動 (新しい閾値を指定する)
# ./as-action.pl --name=testas --op restart --minsize=1 --maxsize=3 --policytype=cpu --uthres=95 --dthres=25 --period=120
  • 削除 (as config&groupの削除)
# ./as-action.pl --name=testas --op clear
  • auto scalingに関する情報一括取得
# /root/bin/as-action.pl --name=testas --op info

シリコンバレーから帰ってきて

まだ微妙に時差ぼけに苦しみながら少しブログ書いてみます。

CassandraSFは前夜祭含め2日間のイベントだったのですが、
その後いろいろアポとって、合計一週間滞在しました。

せっかく来たということで予定をバシバシ入れると、
こんなスケジュールになってしまいました;

  • 10(日): San Francisco へ, Pre-Cassandra SF Happy Hour
  • 11(月): Cassandra SF 2011
  • 12(火): Twitter 社訪問, Pier 39, Thanh Long
  • 13(水): Fisherman's Wharf, Global Catalyst Partners 大澤さん訪問, PARC 訪問, Facebook 社 (アポなし), Stanford Shopping Center, Castro St.
  • 14(木): ITOCHU Technology 社訪問, Google 社訪問, Computer History Museum, Santana Row
  • 15(金): Stanford University (アポなし), Apple 社 (アポなし), Hacker Dojo 訪問, 山内さん宅訪問, Hacker Dojo の Happy Hour
  • 16(土): 帰国

さすがに詰め込みすぎです;

ブログ書くのは面倒なので、後はスライドにまとめましたw
実は昨日の研究室であった報告会スライドを少しイジッただけです。
CassandraSFのことも書いてます、見てください!

CassandraSF2011に行ってきた!

MyCassandraの宣伝の為にCassandra SF2011@Mission Bay Conference Centerに行ってきたので、
いち早く報告ブログを書いてみます。

発表内容は録画されており、また人によってはスライド公開もされるので、
深い話については述べずに、あくまで一参加者として、どんなイベントだったのか、
ずらずらと並べておきます。

CassandraSFの話をする前にまず前日にあったPre-CassandraSFについて。
Pre-CassandraSFはAcunu (Cassandraを使ったスタートアップベンチャー)主催の前夜祭で、こんなシャレオツなBarで行われました。

Acunuの人やNateさん始め、Data Staxの人がいたので、宣伝活動。
Cassandraに毎日のごとく関わっていると、「あっ、この人はあの部分を作ってるコミッターだ!」とか分かるわけで。。
Acunuのプロジェクトはとても興味深いので皆さん注目ですよ!!





この日はイベント終わってホテル戻って、爆睡しました。


さて、慣れない海外で時差ボケに苦しめられる中、CassandraSF本体です。

8時受付、9時開始という、とても健康的なイベントだったので、
まだ霧がかった天候の中、レンタカーで移動。

会場はUCSF (カリフォルニア大学)のホールらしく、サンフランシスコの栄えてるところから少し遠ざかったところにありました。
右の茶色の建物です。

会場入口。
ホール名が変わっていて、Datastaxの看板が無いと分からなかった;

受付を済ませて、ホールへ移動。
受付時にDatastaxのjake (Briskのプロジェクトリーダ&Solandraの開発者)が"MyCassandra guy?"
と話しかけてくれて嬉しかったです。
おそらくNateさんが伝えてくれてるのでしょう。




さて、ホールへ行く前にプログラムを見てみましょう。
見てもらえば分かるんですが、1日でCassandra関係のイベントを全て詰め込んでいて、朝から晩までCassandraの話という感じでお腹いっぱいでした。

3並列セッションのうち、最初の基調講演があるメインのホールへ。




参加者は450人と多く、Cassandraコミッターや、利用ユーザー、これから利用を考えてるユーザーと様々。
でも、日本の勉強会と比べるとギーク色が強い印象を受けましたw
ちなみに一番前列に並んでる緑の人たちがdatastaxのスタッフです。
モニタには、Cassandraを利用してる企業のインタビュー動画が流れており、
おー力いれてるなぁという印象を受けました。


Datastaxの人たちによるイントロダクションが開始。
Bily Bosworth (CEO)。
スポンサーへの感謝や会社の目標など。





Jonathan Ellis (CTO)。
Cassandraを利用した各プロジェクトの紹介 (知らないものが沢山)や
昨年のCassandraSFからどのようなところに技術的に力を入れてきたのか。
基調講演なのに、かなり技術の詳細まで踏み込んだ講演でした。




Cassandra-0.8は機能の追加 (secondary index, atomic counter, CQL)だけでなく、
基本的な性能向上にも力を入れており、
特にBest in class performance (not just writes!)であることを強調していました。
普通に機能追加すると、元々の性能が気になるところですが、
0.6と0.8のスループットの対比。
基本的にreadもwriteも向上してることを表してますが、
ただ、実験環境の詳細やレコード数1,000万と少ないので、正しい性能評価と言えるかは少し怪しい感じはします。




休憩時間にCTOのJonathan Ellisにコンタクト。
MyCassandraを宣伝しました。
写真で見ると、いかつい兄ちゃんって感じがしていたのですが、
意外と体はスマートな感じで驚きました。

ジョナサン&スタッフにお願いして、MyCassandraのbrochureを置かせてもらいました。
こうゆうのは本来、スポンサーとしてお金払わないと置かしてと思うのですが、
元々スペースを借りていたスポンサーが会場に来ないので、どかして置いていいよ!とのこと。
なんと、ありがたい。


Chris Goffinet (Twitter) – Cassandra at Twitter
ここからは箇条書きで。

  • スライド: http://www.scribd.com/doc/59830692/Cassandra-at-Twitter
  • Twitterでのユースケースの紹介
    • twitter button
    • Chuckoo
    • Rainbirdの話は無かった・・・
  • 300+ Cassandra nodes in production
  • CassandraのJava GCの問題に言及していて、
    • Cassandraの中にmemcacheを組み込んで利用しているとのこと
      • twitter社に限らず、素のCassandraではなく、少し拡張・改造して利用しているという話が幾つかあった
    • 「If you select not Java-based storage engine with MyCassandra, you need not to worry about GC problems. #CassandraSF2011」とアピールしてみる
  • Custom framework that uses YCSB
    • YCSBをテスト時に利用して性能評価を行うっていうのが面白い
    • YCSBのパラメータ表記はMyCassandraのベンチマークや研究の参考になるので超ありがたい
  • Cassie: Light-weight Cassandra Client


  • ちょうど今日 (07/12)、Twitter社を訪問するので、いろいろ疑問に思ったことを聞いてみたいと思ってます



  • Eric Evans (Rackspace) – CQL – Not just NoSQL, It’s MoSQL
  • Sylvain Lebresne (DataStax) – Counters in Cassandra
    • Cassandraの新機能ごとに1時間のセッションが設けられていた
    • 意外とどれも人気
    • 僕自身はあまりクライアント周りの話に興味を持てないのでCQLの話はあまりちゃんと聞けませんでした
    • Atomic Counterの実装は一度自分でソースを読んでいて、その答え合わせをしに聞きにいった感じです
      • Vector Clockを利用した実装になってる
      • やっぱり実装は単純には行かないわけで、ストレージ周りだけではなく、プロキシ部分にも手を入れていて、通常のwrite pathとは異なるワークフローとなってる
      • ZooKeeperを利用すれば良いとか思うかもしれませんが、それはCassandraのdecentralized = no SPOFの利点に反するわけで。


ランチ

  • サンドイッチ
  • 惣菜サラダ×2
  • スナック
  • りんご
  • クッキー

少食なので、これだけで僕はお腹いっぱい。



Jake Luciani (DataStax) – Introduction to Brisk


Jake Luciani (DataStax) – Scaling Solr with Cassandra

  • JakeさんのSolaandraの話
    • Lusandra (2010)は以下のような制限があった
      • Thrift for 100k hit data
      • Read performance
      • Only Order partitioner, load balancing
    • それでSolandraを作ったという話の流れ
      • 基本のアイデアは主キーをtoken名+検索用キーというふうにして、Solandra用のPartitionerを新たに拡張して作ったという感じ



追記:CassandraSFについて何かしら追記しようかと思ったのですが、
スライドは以下に全てあがっていて、そちらを見てもらう方が早いです。

http://www.datastax.com/events/cassandrasf2011/presentations

生で聴いていた感じとしては、
午前中のDatastax CTOのテッキーな基調講演、Twitter社のユースケース紹介、Netflix社のOracle on 自社DCからの移行話といったどれも面白い話でお腹いっぱいになってしまい、後半のBriskやSolandraといったCassandra応用やCounter/CQLといったCassandra新機能の紹介は割と聴き流す感じになってしまいました。

クラウド系の国際会議メモ

ここのサイトによると28種類あるらしい。
http://sites.google.com/site/cloudcomputingsystem/conference

  1. Workshop on Virtualization in High-Performance Cloud Computing
  2. Cloud computing for the Public Sector (CCPS)
  3. The International Workshop on the Economics and Business of Grids, Clouds, Systems, and Services (GECON)
  4. ASOC Workshop on Applications and Services on Cloud.
  5. IEEE International conference on Cloud computing.
  6. International Conference on Cloud Computing (CloudComp)
  7. International Conference on Cloud Computing Technology and Applications (CloudCom)
  8. Workshop on Automated control for datacenters and clouds
  9. International Workshop on Cloud Computing, Applications and Technologies
  10. International Workshop on Data Center - Converged and Virtual Ethernet Switching (DC CAVES)
  11. IEEE International Workshop on Emerging Applications for Cloud Computing (CloudApp)
  12. ACM Workshop on Scientific Cloud Computing (ScienceCloud)
  13. The ACM Symposium on Cloud Computing, (ACM SOCC)
  14. The IEEE Workshop on Emerging Applications for Cloud Computing (CloudApp)
  15. International Workshop on Mobile Cloud Computing (MCC)
  16. International Workshop on Security and Privacy in Cloud Computing, ICDCS-SPCC
  17. The International Workshop on Cloud Computing Interoperability and Services (InterCloud)
  18. International Workshop on Workflow Approaches to New Data-centric Science (WAND)
  19. International Symposium on Cloud Computing
  20. IEEE/ACM International Symposium on Cluster, Cloud and Grid Computing (CCGrid)
  21. Workshop on System-level Virtualization for High Performance Computing (HPCVirt)
  22. IEEE workshop on collaboration and cloud computing
  23. The ACM Cloud Computing Security Workshop (CCSW)
  24. Workshop: Computing in the Cloud, Princeton.
  25. workshop on Cloud Computing and its Applications (CCA)
  26. HotCloud
  27. Workshop on Software Engineering Challenges in Cloud Computing
  28. Strategies and Technologies for Cloud Computing Interoperability (SATCCI)

現在 (2011/04/10)の時点でsubmission deadlineが過ぎていないものを下に挙げておく。
この中のいずれかにアプライする予定。

  • VHPC '11 (8/30)
    • http://vhpc.org/
    • May 2 2011 - Abstract submission due
    • June 13 2011 - Full paper submission
    • July 15 2011 - Acceptance notification
    • August 3 2011 - Camera-ready version due
    • August 30 2011 - September 2- conference
  • CloudCAT 2011 (9/21-23)
  • SOCC 2011 (10/27-28)
  • CloudComp 2011 (11/28-29)
    • http://www.cloudcomp.eu/
    • Paper Submission Deadline: July 18th 2011
    • Notification of Acceptance: September 19th 2011
    • Camera Ready Deadline: October 31st 2011
    • Workshop Proposal Deadline: April 11th 2011
    • Tutorial/Industry Track Proposal Deadline: July 1st 2011
    • Demo/Exhibit/Poster Proposal Deadline: September 5th 2011
    • Conference Date: November 28-30th 2011
  • CloudCom 2011 (11/29-12/1)
    • http://www.cloudcom.org/
    • Submission deadline: August 21, 2011
    • Author notification: September 26, 2011
    • Camera-ready manuscript: October 14, 2011
    • Author registration: October 14, 2011
    • Workshop proposal: 31 May, 2011
    • Workshop notification: 15 June, 2011
    • Poster/Demo/Exhibition: October 01, 2011
    • Poster/Demo/Exhibition Notification: October 10, 2011

MyCassandraというモジュラー式のNoSQLを作ってます。

今まではCassandra-0.6.2をベースに開発を行っていましたが、
せっかく公開するならば、新しいバージョンでやるべきと考え、現在MyCassasndra-0.7にアップグレードを行っているところです。近いうちに公開できるよう頑張ります!

今日はMyCassandraに関するドキュメントを書いていないなぁと思い、エントリにしました。

MyCassandraとは・・

Apache CassandraBigtable likeなストレージエンジンをMySQL,BDBといったRDBMSMemcached,TT,RedisといったKVSに差し替えることが出来る分散データストアです。

要するに、

Cassandraは、Dynamo + Bigtableですが、


MyCassandraは、Dynamo + MySQL, Bigtable, etc...ですw

発端

Bigtableストレージエンジンは、差分をシーケンシャルに書きこみ、読み出し時にその差分をマージするために、書き込みは高速に出来ますが、読み出し性能は犠牲になるような設計がなされています。
これを他のデータストアにすることで、これらの性能が丸々変わるのではないかと考えて、私の修士の研究として始めました。

MyCassandraの利点

MyCassandraを利用することの利点として以下のようなものがあります。

  • データの性質(書き込み頻度が高い、読み出し頻度が高い)に応じたストレージエンジンを同一データストア内で差し替え可能
  • Cassandraの分散の機構や、データモデルなどはそのままにして、利用者が普段使い慣れているデータストアをNoSQLとして使える
  • 異なるストレージエンジンのノードからクラスタを構成することで、読み出しと書き込み性能を両立できる

MyCassandraのコアストレージエンジン

今のところ、MyCassandraで扱えるストレージエンジンは以下の3つです。

  • Bigtable(cassandra original)
  • MySQL(mycassandra default)
  • Redis(jredis apiを使用)

以下ではそれぞれのストレージエンジンの差し替え方法と各ストレージエンジンに関する特別な設定について説明します。

共通の設定

ここではcassandraのパラメタ設定については省きます(マニュアルを参考にしてください)。
MyCassandraストレージエンジンの選択は非常にシンプルです。
設定はconf/cassandra.yamlで行います。

cassandra.yamlに以下1行を追加してください。

:
auto_bootstrap: false

database: Bigtable # ←追加

hinted_handoff_enabled: true
:

Bigtable

上のように一行追加するだけで素のCassandraと同じように使えます。

MySQL

データモデルのマッピング

cassandraのデータモデルはkeyspace->columnfamily->columnといったデータ構造になっていますが、
これをmysqlではdatabase->table->fieldに対応付けます。
mycassandraの実装しているmysqlはcolumnfamilyのデータをkey/value形式でストアするため、
mysqlでのスキーマ設定をする必要がありません。

mysqlのコネクション設定

上の設定に加えて以下をcassandra.yamlに追加します。

:
auto_bootstrap: false

database: MySQL
sqlhost: localhost # ←mysqlのhost
sqlport: 3306 # ←mysqlのport
sqluser: mycassandra_user # ←mysqlのmycassandra用user名 
sqlpass: XXXXXXXX # ←mysqlのuser password

hinted_handoff_enabled: true
:
keyspaceの設定

また、MySQLはkeyspaceに対応するmysqlのdatabaseを作成する必要があります。
例えば、"keyspace1","keyspace2"というkeyspaceをスキーマで定義している場合は、

mysql> create system;
mysql> create keyspace1;
mysql> create keyspace2;

というように、databaseを事前に作成します。
systemというdatabaseは、cassandraの内部データ用のkeyspaceなので、これも作成してやります。

column familyの設定

keyspaceと異なり、column familyは自動的に上で作成したdatabase上にtableとして作成されます。
column familyの設定として、cassandra.yamlに以下を追記します。

column_families:
         - name: cf1
           rowkeysize: 16 # keyのサイズ(byte)
           columnfamilysize: 2048 # column familyのサイズ(byte)
           storageenginetype: InnoDB # column familyにマッピングするtableのmysqlストレージエンジン
           compare_with: BytesType
           keys_cached: 10000
                :
         - name: cf2
                :

Redis

Bigtableと同様に一行追加するだけで素のCassandraと同じように使えます。
RedisにアクセスするAPIとしてはjredisを利用しています。

MyCassandraのアーキテクチャ


Cassandraのリクエスト受理とストレージの間にStorage Engine Interfaceを導入しました。
このinterfaceを実装することでストレージエンジンを追加できます。

ストレージエンジンの追加方法

利用者は今説明した3つのストレージエンジンの他に自分で追加できます。

追加方法
  1. jdbcなどデータベースにアクセスするドライバにパスを通す
  2. ストレージエンジンに対応したコードをStorage Engine Interface実装する
  3. cassandra.yamlの設定

以下はRedisのようなKVSをStorage Engine Interfaceを用いて実装した例です。

public class KVSStorageEngine implements StorageInterface
{
  StorageInstance client;
  String keyspace, cf;
  final String KEYSEPARATOR = ":";

  public Instance(String keyspace, String cf)
  {
    client = new KVSClient(<HOST>,<PORT>);
    this.keyspace = keyspace;
    this.cf = cf;
  }
  
  public int put(String rowKey, ColumnFamily newcf)
  {
    String key = keyspace+KEYSEPARATOR+cf+KEYSEPARATOR+rowKey;
    ColumnFamily cf = (client.exists(rowKey) ? updateCF(rowKey, newcf) : newcf);
    return client.set(key, serialize(cf));
  }
  
  public ColumnFamily get(String rowKey)
  {
    String key = keyspace+KEYSEPARATOR+cf+KEYSEPARATOR+rowKey;
    return deserialize(client.get(key));
  }
}

発表スライドなど

研究会やカンファレンスなどで発表した資料を以下に挙げていきます!!
http://www.slideshare.net/sunsuk7tp/edit_my_uploads