16 Nov 2010

s4 cluster on aws (ec2 micro instance which is currently free!)

I have made  s4 cluster on aws (ec2 micro instance which is currently free!) and I plan to open its AMI to the public for trying easily s4.

S4のクラスタ環境をEC2無料(micro)インスタンス上に作ってみました。AMI(amazon machine image)は要望があれば公開しようと思っています。

amazonが提供しているquick start用のx86_64イメージを元に、

・java-sdk

・maven

・zookeeper2.x系 stable release版

・s3fs( s3用のfuse )

等をインストールしています。

(※s3fsはexampleのtwittertopiccountが出力する情報をS3を通して外部に公開するために使ってみました。exampleソースを修正すること無しに設定ファイルの出力先をs3fsのmount先にしてあげるだけでwebapiが提供できました。ただし、やりすぎるとs3の無料リクエスト上限を超えてしまうので、無料で試したい方は出力先をローカルにしてください)

後はmicroインスタンス上でクラスタ環境として動作させるためには、VMメモリパラメータの調整や、現状のαバージョン(2010/11/15にgitから取得)だと設定ファイルにいくつかバグがあったので、それらを修正しています。

■how to use on static cluster

基本は「http://wiki.s4.io/Tutorials/GettingStarted#toc1」に沿って進めますが、以下の修正が必要になります。

・${s4_home}/bin/s4_start.sh

#MEM_OPTS="-Xms800m -Xmx2000m"
  MEM_OPTS=""

・${s4_core_home}/conf/*/s4_core_conf.xml

<!--    <property name="minimumMemory" value="52428800"/> -->
    <property name="minimumMemory" value="10240000"/>

※「https://github.com/s4/core/blob/master/src/main/java/io/s4/util/Watcher.java」がメモリ容量監視をしており、上記の設定値以下になると強制的にexitしてしまいます。microインスタンスだと残り10MB近辺にならないとOSのディスクキャッシュを開放しないので、連続稼動させるには上記のような設定が必要になります。

以上の変更を加えれば無事動きはじめます。(※利用するtwitterアカウントを「garden horse」申請していないと、ちょっと出力まで時間がかかるかもしれません)

watch -n 1 cat /tmp/top_n_hashtags

↑でJSON形式でハッシュタグランキングが生成されていくのを確認できるはずです。

■how to use on dynamic cluster

・zookeeperを起動

cp ${zk_home}/conf/zoo_sample.cfg ${zk_home}/conf/zoo.cfg

vi ${zk_home}/conf/zoo.cfg で「dataDir=/export/crawlspace/mahadev/zookeeper/server1/data」を任意のディレクトリに変更します。

${zk_home}/bin/zkServer.sh start > ${zk_home}/log/console.log

・cluster情報をzookeeperに登録

※「https://github.com/s4/comm/blob/master/src/main/java/io/s4/comm/tools/TaskSetupApp.java」というzookeeperへのクラスタ情報登録用のツールが容易されていますが、起動用のスクリプトがないので作りました。それを元に起動させます。

 

osx=false        case "`uname`" in        Darwin*) osx=true;;        esac        if $osx; then            READLINK="stat"        else            READLINK="readlink"        fi        #---------------------------------------------        # USAGE and read arguments        #---------------------------------------------        if [ "$1" == "-h" ]; then          echo "Usage: $0" >&2          echo "   [clean|setup] setup_config_xml" >&2          exit 1        fi        BASE_DIR=`dirname $($READLINK -f $0)`        CORE_HOME=`$READLINK -f ${BASE_DIR}/../s4_core`        CP_SEP=":"        JAVA_LOC=""        if [ "x$JAVA_HOME" != "x" ] ; then          JAVA_LOC=${JAVA_HOME}"/bin/"        fi        JAVA_OPTS=""        echo "java location is ${JAVA_LOC}"        echo -n "JAVA VERSION="        echo `${JAVA_LOC}java -version`        #---------------------------------------------        #ADDING CORE JARS TO CLASSPATH        #---------------------------------------------        CLASSPATH=`find $CORE_HOME -name "*.jar" | awk '{p=$0"'$CP_SEP'"p;} END {print p}'`        if [ "x$USER_CLASS_PATH" != "x" ] ; then            CLASSPATH=${CLASSPATH}${CP_SEP}${USER_CLASS_PATH}        fi        CMD="${JAVA_LOC}java $JAVA_OPTS -classpath $CLASSPATH io.s4.comm.tools.TaskSetupApp $1 $2 $3"        $CMD

 

 

./task_setup.sh localhost setup ${s4_core_home}/conf/redbutton/clusters.xml

/usr/local/zookeeper/bin/zkCli.sh localhost:2181 ls / |grep -v INFO

⇒zookeeprのファイルシステム上のルートに「s4」という要素が追加されているはずです。

/usr/local/zookeeper/bin/zkCli.sh localhost:2181 ls /s4 |grep -v INFO

⇒zookeeprのファイルシステム上のルートに「adapter,s4」という要素が追加されているはずです。

全体的には以下のようにクラスタ情報が格納されます。

/${cluster_name}

|-- ${cluster_type} (s4 or adapter)

|-- task

-- task-0 (task_setup.shによりclusters.xmlに登録してある情報を元にノード情報が登録されます)

|-- process

-- task-0 (s4、adapterを起動させるとクラスタ情報(/${cluster_name}/${cluster_type}/task)を元に、起動プロセスの情報が一時ノード(zookeeper用語でいうと「ephemeral node」)として登録されます。このためプロセスが何らかの形で終了すると自動的に削除されます。「https://github.com/s4/comm/blob/master/src/main/java/io/s4/comm/zk/ZkProcessMonitor.java」がこの情報を監視してクラスタの状態を保持しています。

・s4起動

./s4_start.sh typical &

⇒${s4_core}/conf/typical/s4_core.properties_headerを元に起動するので「commlayer_mode=dynamic」がJava環境変数に設定されzookeeperのクラスタ情報を参照するようになります

・zookeeperでs4起動状況を確認

/usr/local/zookeeper/bin/zkCli.sh localhost:2181 ls /s4/s4/process |grep -v INFO

⇒「task-0」が追加されているはずです。

/usr/local/zookeeper/bin/zkCli.sh localhost:2181 get /s4/s4/process/task-0 |grep -v INFO

⇒起動しているプロセス情報(IPアドレス、待ちうけポートなど)がJSON形式で登録されているのが確認できます。

・adapter_conf.xmlの不備を修正

初期状態ではzookeeperのアドレス情報が取得できないためadapterが起動できません。

${s4_core_home}/conf/*/adapter_conf.xmlの「commLayerEmitter」の「bean」情報に「<property name="clusterManagerAddress" value="localhost:2181"/>」を追加します。

・adapter起動

./run_adapter.sh -u ../s4_apps/twittertopiccount/lib/twittertopiccount-0.0.0.1.jar -d ../s4_apps/twittertopiccount/adapter_conf.xml typical &

・zookeeperでadapter起動状況を確認

/usr/local/zookeeper/bin/zkCli.sh localhost:2181 ls /s4/adapter/process |grep -v INFO

⇒「task-0」が追加されているはずです。

/usr/local/zookeeper/bin/zkCli.sh localhost:2181 get /s4/adapter/process/task-0 |grep -v INFO

⇒起動しているプロセス情報(IPアドレス、待ちうけポートなど)がJSON形式で登録されているのが確認できます。

watch -n 1 cat /tmp/top_n_hashtags

⇒動作を確認してみましょう。

■how it works

おおまかな処理フローは以下のような形です。

adapter ⇒(func call) dispatcher(processMonitor, taskManager, partitioner) ⇒(via queue) emitter(serializer) ⇒(send via udp) ....

s4 ⇒(receive via udp) rawListener(deSerializer) ⇒(via queue) peContainer ⇒(function call) processingElement ⇒(func call)  dispatcher(partitioner) ⇒(via queue) emitter ⇒(send via udp) ....to other or same s4 nodes

以下、処理フローに出てきた役割毎に簡単に責務を説明します。

・adapter (https://github.com/s4/core/blob/master/src/main/java/io/s4/adapter/Adapter.java)

ストリームを受信する役割を担います。examleではtwitterのstreamAPIを用いてGardenHouseからStatusを取得しています。

・dispatcher(https://github.com/s4/core/blob/master/src/main/java/io/s4/dispatcher/Dispatcher.java)

adapterまたはprocessingElementの処理結果(Stream)と、taskManager、partitionerによって決定された送信先ノード情報に基づいて、別のprocessingElementを呼び出すためにemitterに処理結果を渡します

・taskManager (https://github.com/s4/comm/blob/master/src/main/java/io/s4/comm/core/TaskManager.java)

cluster定義情報をzookeeperまたは、fileから取得し管理します。

・processMonitor (https://github.com/s4/comm/blob/master/src/main/java/io/s4/comm/core/ProcessMonitor.java)

clusterのプロセス稼動状況をzookeeperまたは、fileから取得し維持管理します。

・partitioner (https://github.com/s4/core/tree/master/src/main/java/io/s4/dispatcher/partitioner/)

Stream内の任意の情報に基づき送信先を決定するDefaultPartitioner、ランダムに決定するRoundRobinPartitioner等があります。一般的にはprocessingElementをステートフルにする場合はDefaultPartitioner、ステートレスで良い場合はRoundRobinPartitionerを使うことになるでしょう。

・emitter (https://github.com/s4/core/blob/master/src/main/java/io/s4/emitter/CommLayerEmitter.java)

queue経由で受け取ったStream情報を宛先を元にudpで送信します。

・serializer, desirializer (https://github.com/s4/core/tree/master/src/main/java/io/s4/serialize/)

udpでStream情報を送信する際にシリアライズ処理を実行します。

・rawListener (https://github.com/s4/core/blob/master/src/main/java/io/s4/listener/CommLayerListener.java)

udpでStream情報を受信してqueue経由でpeContainerにStream情報を引き渡します。

・peContainer (https://github.com/s4/core/blob/master/src/main/java/io/s4/processor/PEContainer.java)

processingElement毎にインスタンスを管理するprototypeWrapper(https://github.com/s4/core/blob/master/src/main/java/io/s4/processor/PrototypeWrapper.java)を用いて、Stream情報を処理すべきProcessingElementの決定と、processingElementのkey情報を元にprocessingElementを生成し、executeメソッドを逐次実行します。

    <property name="keys">
      <list>
        <value>TopicSeen topic</value>
      </list>
    </property>

↑はexampleの「topicCountAndReportPE」の設定ですが、これはストリームが「TopicSeen」クラスで、そのメンバ変数「topic」の値を「key」に用いる事を示しています。このため「topicCountAndReportPE」はtopic(ハッシュタグ)毎にインスタンスが生成され、インスタンス単位でtopicの件数を維持、管理している事を示しています。

    <property name="keys">
      <list>
        <value>AggregatedTopicSeen reportKey</value>
      </list>
    </property>

↑はexampleの「top10TopicPE」の設定ですが「topicCountAndReportPE」によって「topicSeen.setReportKey("1")」と、常に固定の値が設定されているためインスタンスは一つのみになります。ハッシュタグ毎に出現頻度のランキングを集計するため、情報を集約する必要があるのでこのような実装になっています。

・processingElement (https://github.com/s4/core/blob/master/src/main/java/io/s4/processor/ProcessingElement.java)

ストリームに対して任意の処理を実行します。exampleではユーザ定義の独自ジョブを実装していますが、「https://github.com/s4/core/tree/master/src/main/java/io/s4/processor/」内に予め用意されているprocessingElement(SimpleCountPE等)を用いる事で、ストリーム内の任意のフィールドに応じてカウントする等の簡易な処理は、実装無しで設定のみで実行することが可能です。他のprocessingElementに処理を引き継ぐ場合はdispatcherを使います。

■use case

(準)リアルタイムな集計、解析をする際に利用する分散フレームワークの選択肢になるでしょう。並列分散処理はzookeeperを利用することで簡易に実装されています(ソース行数は1万行程度)。クラスタの増強も簡易に実行できます。

問題になるのはステートフルなジョブ(大抵はそうなるはずです)を扱う場合に、状態をFailoverさせるには共有ストレージが必要になる事です。exampleでは実装されていませんが、耐障害性を高める場合はprocessingElementに対してサービスレベルに応じて定期的に共有ストレージへ状態の書き出しを行い、processingElementの初期化処理で共有ストレージからの読み込み処理を行う実装が必要になるでしょう。awsで実現する場合はS3を利用することで簡易に実現できるでしょう。

またノード間の通信にudpを利用しているため、ストリームが欠落する可能性は否定できません(リトライなども現状は未実装)ので、クリティカルな数字を要求される場合には適していません。しかしストリームの集計、解析という用途の特性上、多少のデータ欠落は許容できるケースがほとんどだと思うので、個人的には良い選択肢だと思います。

■possibly problems

一つ気になるのは現状processingElementの処理が並列化されていない事です。rawListenerはマルチスレッドで動いているのですが、peContainerは単一スレッドでprocessingElementを逐次実行しています。ここは並列度をあげて欲しいところです。

■my conclusion

運用、管理ツールまわりの充実が図られれば採用されるケースは増えると思います。またhadoopのようにJava以外の言語でprocessingElementが実装できるようになると良いですね。個人的には職場復帰したら現場で早速投入してみたいと思っています。皆様もお試しあれー。

 

29 Jun 2010

Cassandra 0.6.3 RELEASED details

月次マイナーアップデートきたよ。

さっそくmyクラスタ環境もUPDATEしたけど、今のところ負荷が高くなければ、順調に動いてます。

翻訳&ソースを確認した所感。比較的重要なUPDATEには★印つけました。

並列、非同期&&ネットワークシステムプログラミングにありがちなバグが重要な問題を引き起こしてます。。。

特に「14」は致命的。

まだまだ本番投入はクリティカルな用途では無理です。。。

 

  1. CASSANDRA-1019 ★streamコネクションのリトライ回数が最高8回に増えた。
    ⇒高負荷かけた時に、クラスタの離脱、参加を繰り返して、無駄にネットワークトラフィックが発生する状況が多少改善されそう

  2. CASSANDRA-1111 「describe_ring」I/Fを不正なkeyspace渡してコールした場合に拒否るようになったよ

  3. CASSANDRA-1129 キャッシュサイズの計算ちょっと違ってた。

  4. CASSANDRA-1129 ★運用中にnodetoolや、JMX経由でキャッシュサイズ変更した場合に、Compactionのタイミングで値が元に戻っちゃってたのを修正。

  5. CASSANDRA-1141 ★gossipによるノード生死状態チェックがミスる可能性があったので、定期的(一時間毎)なHintedHandOff(HHO)のチェックを止めた。変わりに必要であればJMX経由で明示的にHHOチェックを投げれるようにした。
    ⇒これにより「1」と同様にクラスタの状態が安定しそう

  6. CASSANDRA-1152 ★ReadのConsistencyLevelがALLの場合、ちゃんと動いてなかった。(@yukimさんがバグレポしてたやつ)

  7. CASSANDRA-1139 StandardなカラムしかないCFに対して、SuperColumnの削除操作を行った場合にランタイムエラーが出てた(つまりサーバが落ちる)のを直した。
    ⇒普通はありえないオペレーションだけど、セキュリティ的には脆弱。

  8. CASSANDRA-1057 クライアントが切断した場合にログイン情報を削除するようにした。
    ⇒ Thriftのサーバサイドスレッドはプールされてるので、クライアント側からプール数以上のコネクションを張ろうとすると拒否られてた。クライアント側でコネクションプールを実装しているHector等のCassandraクライアント経由で使っている場合には気がつきにくいバグですね。

  9. CASSANDRA-1174 debianのインストールスクリプトでOpenJDKより、SunJDKを優先するように変更

  10. CASSANDRA-1146 ★partitionerを変更してリスタートした場合に、直ぐに検知できるようにした。
    ⇒今までの実装だと、最悪テーブルを壊す可能性があったように見受けられる

  11. CASSANDRA-1118 ノードの置き換えなのか、再参加なのかの判定に「generation time」を利用するようにした。
    ⇒ 今までの実装だと、例えばデータの置き場所をamazonEBSから別のホスト(e.g. rackspace)に移したい場合等に、同じtoken且つ同じIPを割り当てたノードを参加させるとクラスタの状態が壊れるようだ

  12. CASSANDRA-1160 MessageServiceはGossiperが完全に初期化されるまでwaitするようにした。
    ⇒今までの実装では同期をとってなかったので、サーバ開始直後にリクエスト投げるとgossipの準備ができてないのでエラる場合があったように見受けられる

  13. CASSANDRA-1119 コミットログのヘッダが壊れていた場合に、対象レコードを検知。
    ⇒INFOログを吐くようになったようです。対象レコードは無視して処理は続行してますね。

  14. CASSANDRA-1169 ★anti-entropy(レプリカ間で同期を取るアレ)実行時に、送信するstreamの実行順序性が(比較的)保障されるように修正(まだ完全には直っていない。0.7で完全にFIX予定)
    ⇒AESは複数のstreamを作成して投げますが、streamの送信順序を保障しただけで、受信側のネットワーク到達順序は保障されてないように見受けられる。

  15. CASSANDRA-1169 ↑の処理を直すための変更。

  16. CASSANDRA-1027 batch_mutateによる削除でrowが完全に削除されるようになった

  17. CASSANDRA-1212 bloom filterの状態が行単位でJMX経由で確認できるようになった

  18. CASSANDRA-1201 redhatのinitscriptがcontribに提供された

  19. CASSANDRA-1112 クラスタに新規マシン追加時に、tokenの算出方法をランダムから、ちゃんと中間地点を取得するように変更された

  20. CASSANDRA-1226 executorステージでOOMが発生した場合は、thriftの部分と同じようにサーバをkillするように修正

  21. CASSANDRA-1190 ★同じレプリカを担当するノードが、同じタイミングでメジャーコンパクションの実行が完了した場合に、自動実行するリペア処理を削除。現在は、nototool経由でのマニュアル操作のみをサポート。
    ⇒自動リペアはコストが高い処理だし、適切なタイムアウト値を決定するのが難しいと判断されたためと思われる。

  22. CASSANDRA-1229 HintedHandOffが同じノードに対して重複実行されないように修正

  23. CASSANDRA-1181 compactionスレッドのプライオリティを変更できるようにしたみたい
    ⇒「cassandra.compaction_priority」JAVA環境変数にプライオリティを設定できるようです。Javaのスレッドプライオリティについてはここが詳しい

18 Jun 2010

Cassandra, HBase, and PNUTS Compared (using YCSB)

ちょっと古いデータになると思いますが、YCSBを使ったCassandra, HBase, そしてPNUTSの比較記事をめっけたので共有です。

http://nosql.mypopescu.com/post/708675428/cassandra-hbase-and-pnuts-compared

↑をみてわかった事は、

  • やはりCassandraはいけてる(書き込み、読み込みともにバランスが良い)
  • HBaseは書き込みのスループットが非常に良いですが、単にDiskにsyncしてないだけなので・・・。プライマリデータを扱うには怖いですね(※このパフォーマンス比較を行った際に先行書き込みログをsyncをしない設定にしていただけのようです)
  • 今回のテストではCassandraはバージョン0.5をベースに試験を実施しているため、レンジクエリ(範囲問い合わせ)の結果はおそまつ様でしたが、現行のstable(0.6.2)でOderPreservingPartitionerを利用すれば、範囲問い合わせに対してもHBaseと同等のパフォーマンスは出せそうです
  • HBaseは読み込みは重いね。やっぱりインタラクティブな用途には限界がみえる
  • PNUTSはノーコメントで・・・

 

 

18 Jun 2010

Riak - why the reduce phase is run only on a single node? Japanese Translation

翻訳するまでもない事なんですが、Riakメモ。

対象記事:

Question about Riak MapReduce

http://nosql.mypopescu.com/post/711014851/question-about-riak-mapreduce

 

以下、訳

---

ここにRiakのM/Rフェーズが書かれているんだけど、なんでReduceフェーズ一つなんだろう?

下の図「Jon Meredith’s Riak in Ten Minutes embedded below」からの抜粋だけど、Mapフェーズは全体に分散されるんだけど、ReduceフェーズはM/Rのきっかけになった一つのノードに集約してしまうようなんだ。

このアプローチにはいくつか問題がありえるとおもうんだけど、一時的なものなのかな?それとも何か理由が?

Question

  • ネットワーク帯域の無駄遣いしちゃう
  • 処理が一つのノードに集中しちゃう

Answer

  • 暫定的な対応です。他のソリューションを構築中との事です(時期までは明言してません)

 

RiakのM/R実装はHadoopに比較すると他の点に関しても、まだまだの感があります。

これからに期待ですね。

 

8 Jun 2010

How to Recovery from Crushing Cassandra's index files

Indexがこわれちゃった・・・

現状のCassandraの実装だとハードウェアレベルの障害で、Indexが壊れる可能性があります。 そんな非常事態の復旧手順なり。

recovery index

  1. データファイルが壊れてないか確かめるため「bin/sstablekeys」実行 (「sstable2json」だとindexファイルを見に行くのでNGでした)
  2. 以下のスクリプトで、データファイルからIndexを復旧する

とりあえずIndexを復旧するPythonスクリプト書いたよ! 自分の環境では動いたけど、事前に試してね! 特に32bit環境だと動かない可能性がありそうなので。。。

<所感>やっぱログベースのデータ構造は復旧が容易なのが素敵・・・。

usage: buildindex

import sys, struct, stat, os

infname, outfname = sys.argv[1:3]
if '-Data' not in infname:
   raise Exception('%s does not look like a Cassandra data filename' % infname)

inf = open(infname, 'r')
outf = open(outfname, 'w')
fsize = os.stat(infname)[stat.ST_SIZE]

while inf.tell() < fsize:
   # read current row key and write index entry
   dataposition = inf.tell()
   keysize, = struct.unpack('>H', inf.read(2))
   key = inf.read(keysize)
   outf.write(struct.pack('>H', keysize))
   outf.write(key)
   outf.write(struct.pack('>q', dataposition))

   # skip to the next row
   datasize, = struct.unpack('>i', inf.read(4))
   inf.seek(inf.tell() + datasize)
7 Jun 2010

Cassandra vs Riak

・GC

Javaプロダクト共通の課題としてGCによる最大停止時間をいかに短くするか?というものが存在しますが、Cassandraも例外にあらず、MLをみているとGCStormや、OutOfMemoryで苦しんでいる人がたくさんいます。大半はGCの仕組みを理解していないために生じているものですが、個人的にはCassandra固有の問題もあって、この問題が発生しやすいのではないかと思っています。まず、並列実行性を向上させるために内部設計にSEDA(staged event-driven architecture)を適用しているため、一つのリクエストを処理する毎に生成されるオブジェクト数が、通常のスレッドプールモデルと比較すると増加する傾向にあります。また、クライアントI/Fにthriftを用いているため、レスポンスオブジェクトを一度メモリに展開しなければならない、という制約も存在します。(今後のバージョンでストリーム形式にも対応する予定ではある)さらに、SuperColumnFamilyも現状メモリに展開される罠も。

JavaではFullGC発生させたらある意味負けなので、如何にしてマイナーGCでごみ処理させるかが鍵になりますが、上述のような問題があるため、VMチューニングの難易度が格段に高くなります。特にwriteオペレーションが多い場合はなおさらです。FullGCをさけるために、単純にNew領域を大きくし、寿命を長くした場合、今度はマイナーGCのパフォーマンスが悪化し、スループットが劇的に低下する恐れがあります。

そんなこんなでGCの観点で比較するとerlang製の「Riak」は、大いに魅力的に見えてきます。erlangでは基本的(例外はあります)にプロセス全体での共有オブジェクトを持たないため、GCがスレッド単位でイベント処理毎に実行されます。VMの設定が最適化されたJavaプロダクトには、スループットは劣るかもしれませんが、プロセス全体での停止、という事象が基本的には発生しないため、GCStormやFullGCによる致命的な問題は起こりにくいという点で、本番環境で安心して投入できる感が個人的にはややしてきています。

・メンテナンス性

ちゃんと計算したわけではありませんが、ざっと見た感じCassandraに対してRiakが1/3~1/4程度のコード量です。それだけバグ含有率も少ないはず。

・ストレージ層

CassandraはBigtableクローンのSSTableのみですが、Riakはストレージ層がプラガブルのため、ユースケースに合わせて様々な対応が可能です。今のところerlang組み込みのDB(dets)も含めると3種類のストレージエンジンが提供されている模様

・フロントエンドの必要性

Cassandraのthriftは悪意のあるリクエストに対して無防備なため、DMZには配置できません。対してRiakは、自前でデータチェックできる機構をもっているため、別途フロントエンドを用意する事無くインターネットに直接さらせます。また、RestfulなHTTPベースのI/Fを持っているので、クラウドストレージ的なサービスも簡単に構築できそうです。

・Map/Reduce

CassandraにはHadoopとの連携によるM/Rが可能ですが、Riakは独自にM/R実装をもっているため別途Hadoopクラスタを用意する必要がありません。これによりメンテナンス対象が減り、新規サービスを立ち上げる際に初期コストを抑える効果も期待できそうです。

というわけで敢えてCassandraのマイナス面を前面に押し出してみましたが、Cassandraには、より高度なレプリケーション戦略や、データセンタ、ラックを意識した配置戦略、複数のパーティショニング戦略、カラム志向データ構造など機能的には優れている点が多く、コミュニティも活発です。(MLの流量は10倍位)、また大手の採用事例も豊富で、コミッターにはFacebook、Twitter、Digg、Rapitano等のギークがいます。

というわけでCassandraは大筋使いどころが見えてきたので、今度はRiakを後日検証してみる事にしようー

3 Jun 2010

Cassandraを利用した大量の時系列データの取り扱いについて part1

背景

RDBMSの代替手段としての用途を想定している方が多いと思うのですが、現実的にはMySQL+Memcacheで対応できるユースケースが多いと思います。しかしながら、データ爆発のこの時代。位置情報を始めとしてセンシングデバイスから取得できる情報は、今後ますます増えていくことでしょう。 このような莫大な書き込みが発生し、大量データを捨てずに保持し、時系列で分析するユースケースにCassandraは最適な選択かと思っていたのですが、現状のstable(0.6.2)では難しそうだな~と思っていたところで、昨日MLでずばりこの話題が出ました!自分も議論に参戦中ですが、答えはでるのか? 以下、現状までのやり取りです。

質問

How do I handle giant sets of ordered data, e.g. by timestamps, which I want to access by range? I can’t put all the data into a supercolumn, because it’s loaded into memory at once, and it’s too much data. Am I forced to use an order-preserving partitioner? I don’t want the headache. Is there any other way?

—– 訳 —– どうやったらタイムスタンプのような順序付けされた超でかいデータを扱えるのかな? タイムスタンプで範囲問い合わせ(e.g. select * from hoge where date_time > …)したいんだけど、今のSC(SuperColumn)はアクセスするとメモリに全部乗っちゃうからつかえねーし。 OPP(order-preserving partitioner)使うしかないのかな?他に良い方法ない?

質問(補足)

Let’s say you’re logging events, and you have billions of events. What if the events come in bursts, so within a day there are millions of events, but they all come within microseconds of each other a few times a day? How do you find the events that happened on a particular day if you can’t store them all in one row?

—– 訳 —– 例えばトータルでは数十億レコード位の規模感で、日次で数百万レコード、ピーク時にはマイクロ秒単位で発生するようなケースの場合どうするよ? 1レコード(1row)に乗り切らない場合、どうやって特定の日のレコード取り出す?

回答1

イベントの発生契機と、イベントの詳細情報でColumnFamily(CF)を分割すればいいんじゃね? 日次の分割でもrowの限界(2G)に収まりきらなければ、「EventGroupsPerDay」を時間単位「EventGroupsPerHour」とかにするとか。

EventGroupsPerDay: {
 "20100601": {
   123456789: "group123", // nameがタイムスタンプで,valueを「EventGroups」のrowkeyにする
   123456790: "group124"
 }
}

EventGroups: {
 "group123": {
   123456789: "value1",
   123456799: "value2"
  }
}

回答2

OPPつかって、rowkeyにUTF-8な文字列形式のタイムスタンプを使えばいいんじゃね?あ、イベント毎にコラム単位で情報持ちたい場合は上の例でいいと思うよ。

Events: {
 "20100601.05.30.003": {
   "20100601.05.30.003": 
   "20100601.05.30.007": 
   ...
 }
}

回答3(俺)

どっちも書き込みが同じノードに集中するから、スケールしないと思うんだけど。 (補足:回答1の場合は、EventGroupsPerDayの日次rowをもつプライマリノードに必ず書き込みがいくし、回答2の場合はOPPなので、一定期間は同じプライマリノードに書き込みが集中) というわけで、セカンダリIndexのサポートを待つしかないんじゃないかな?

結論(仮)

データ構造的な問題は解決できても、書き込みが分散しない事には実践投入は厳しいと思います。 セカンダリIndexの実装はロードマップに入っているので、やっぱこれを待つしかないと思うわけです。 他に妙案あるかな?

http://github.com/facebook/scribe scribeは管理がめんどうそうだしなぁ。。。

30 May 2010

Cassandra Recently Topics & Tips

ここ最近のCassandraユーザMLで気になる内容をメモ

  1. サーバサイドフィルタリング機能(called stored javascript)

    RDBMSでいうところのストアドですね。(競合のMongo、Riak等では実装済み) Cassandraは設計上、CPUバウンドになりがちなので、ネットワークトラフィックとのトレードオフで、ユースケースに応じて利用する事になるでしょう。それにしても言語がJavascript前提で話しが進んでいるのが面白い。OSSなV8(ChromeのJSVM)の進化と、JS書けるエンジニアの母数の増加が背景にあるのでしょうが、ここまで普及するとはねぇ。もう2年位Javascript書いてないな(汗

  2. メジャーコンパクションの挙動を考慮したディスク使用量の見積もり

    メジャーコンパクションはシングルスレッドで動作するのでColumnFamily(以下「CF」)単位で処理が行われます。なので、メジャーコンパクション中の必要最低限のディスク空き容量は、定義しているCF中の最大サイズになるって事。FAQには単純にディスク使用量の50%を目安にって書いてありますが、厳密にはCFのデザイン次第で、もうちょっと使えますよーというお話し。

  3. メモリ周りの問題

    jdk1.6.19と、Cassandra0.6.0以前はmemoryまわりのバグが多いので、それ以降のバージョン使ってね!

  4. multigetの挙動

    クラスタ内でGC中でJOBがペンディング中のノードがあると、multigetが必ずタイムアウトする場合があるんだけど、低いコンシステンシレベルで、レプリカが複数ある設定でクラスタ組んでる場合は、レプリカを保持している他のノードに処理を委譲してレスポンス返してくれても良くない?という疑問が上がっていました。とりあえず、現状の実装では処理の効率化のために任意のkeyに対する取得先のノードを内部的にキャッシュするようで、他のノードへのリトライなども実装されていないので、このような結果になるとの事。その後はレスなし。つまり、VMのチューニングと、Memtableのフラッシュ間隔をユースケースに応じてちゃんと設定してね!という事なんでしょう。

  5. Cassandraでもシャーディングが必要なユースケース?

    シャーディングの管理から開放されるためのCassandraじゃないのかよ!?と思わず突っ込みを入れたくなりますが、メジャーコンパクションの仕様上、超巨大なCFを単一で使い、更新、削除がほとんど無いようなユースケースの場合、ディスク使用効率が50%に近い値になってしまうため、対費用効果が・・・というわけで、CFをシャーディングすることで対応しようと思うんだけど、どう思う?というお話し。個人的には、シャーディングの管理コストがディスクのコストを上回るケースが多いと思うんだけど、その辺りの計算結果次第ってところなんでしょうね。

  6. Compaction中の性能劣化対策

    コンパクション中にI/Owaitが100%になって応答性能がどうしようもないんだけど?というお話し。ここではVMのメモリ関係のチューニングを薦める回答ではなく、コンパクション実行スレッドのプライオリティを下げる案が提示されました。http://www.javamex.com/tutorials/threads/priority_what.shtml ← にはJava上のプライオリティ設定値とOS毎のスレッドプライオリティのマッピングがのってます。

28 May 2010

Cassandra 0.6.2. RELEASED

Cassandra0.6.2が先ほどリリースされたようです。

個人的には大量インサートが発生するユースケースでのFullGC問題への対応が嬉しいですね。(まだ完全対応ではないようですが、1億レコードの一括追加でも安定稼動した模様。FullCG対象オブジェクトを減らすべくコードの最適化が日々進められているようです)

というわけで大分、安定してきた感があります!

CHANGELOGとjira(BTS)から比較的クリティカルな変更点、変更内容を翻訳して以下に挙げておきます。

  1. Cache capacity settings done via nodetool get reset on memtable flushes

    nodetoolで行った(row|key)cacheサイズの変更が、一度memflush(SSTableへの書き込み)が実行されると元に戻ってしまう問題が修正されたようです。

  2. Cannot Start Cassandra Under Windows

    windows環境で起動しない問題が解決されたようです。

  3. GCInspector uses com.sun.management - Exception under IBM JDK

    GCInspectorが未実装なIBMJDKでもコンパイル&動作可能になったようです。(ExceptionキャッチしてInspectorはDisableで起動する)

  4. SlicePredicate does not always round-trip correctly

    SlicePredicate(一部のカラムを取得するメソッド)が正しく動作しない事があった問題が解決されたようです。

  5. fix contrib/word_count build

    contribで提供されていたhadoop連携のサンプル(word_count)を動作させる環境が初期状態で整っていない問題が解決されたようです。

  6. make it possible to tell when repair has finished

    ノード間でデータ不整合が発生した場合のrepair要求に対して、repair完了応答がなかったのを修正 && ReplicationFactor(レプリカのコピー数)が「1」の場合は動作しないように修正されたようです。GJ!

  7. tombstone-only rows in sstables can be ignored

    ColumnFamilyがtombstonesだけだった場合に無視される(コンパクション対象外になる)問題への対応などが実装されたようです。

  8. GC storming, possible memory leak

    CommitLogのPeriodicモードで並列動作が可能な箇所を増やし、オブジェクト数の同時生成数を減らすことでFullGC対象にされてしまうオブジェクトを減らし、GCストーム→OOM(Out of Memory)のデッドスパイラルに対抗。デフォルトのJVM起動オプションも、急激な一時オブジェクト増加に対してもマイナーGCでごみ処理されやすい設定に変更されています。GJ!

24 May 2010

Apache Cassandra Glossary's Japanese Translation

Cassandraの用語集として書かれたエントリ(http://io.typepad.com/glossary.html)の日本語訳です。

--- 以下、翻訳

これはApacheCassandraを使う際に必ず知っておいた方が良い用語集だよ。

http://wiki.apache.org/cassandra

は良い情報源だけど、初めてこのドキュメントを読むときにはちょっと大変かも。新しい用語がいっぱいで、任意の用語を理解するための説明に、まだ知らない用語が含まれていたりしてドキュメントを追うのも一苦労。というわけで、このままだと新しい利用者に対して敷居が高いので、簡単なリファレンスを提供しようという次第であります。これは単なる用語集なので初めから終わりまで読む必要はないです。いくつかのセクションで関連する内容は重複して説明されてます。

  1. Anti-Entropy
  2. Anti- entropyとreplica synchronizationは異なるノード間で最新のデータを共有、保持するためのメカニズムです。次のように動作します。メジャーコンパクション (compactionを参照)実行中のサーバは、近隣のサーバとMerkleTreeを交換するためにTree状のデータをやりとりします。MerkleTreeはColumnFamilyのhash値です。このMarkelTreeが一致しない場合、 ノード間のデータに不整合がある事になるので、最新のデータはどれなのか?を決定して、最新のものは現状維持、古いものはリペアする必要があります。このTreeを比較、検証する責任はorg.apache.cassandra.service.AntiEntropyService クラスにあります。AntiEntropyServiceはシングルトンパターンで実装されています。またstatic なDifferencerクラスも定義されており、ここでは二つのMerkleTreeを比較して相違があれば、その部分の不整合解消に着手します。

    Anti- entropyはAmazonのDynamoで使われており、Cassandraで はそれを模しています。

    Dynamoでは Anti-entropyの実装にはMerkleTreeが使われており、Cassandraも同様です。ただちょっと実装が違います。 CassandraではColumnFamily毎にMerkleTreeを保持しており、メジャーコンパクション中にスナップショットとして作成され、 同一リング上の近隣ノードとやりとりする瞬間だけ保持します。こうすることでディスクI/Oを削減することができます。(訳者脚注:代償としてコンパクションの時間が長くなるため、更新停止時間は長くなる可能性はありそう)どのように不整合の解消を行うか、より詳細な情報はReadRepairを参照してください。

  3. Async Write
  4. ドキュメント等で出てくる"async writes"は単純に非同期書き込みを意味しています。Cassandraではバッファに書き込む時にExecutorServiceやFuture<T>のようなjava.util.concurrent library componentsを使いまくっています。

  5. Avro
  6. Thriftの代わりになるCassandraのRPCクライアントです。AvroはHadoopのサブプロジェクトでDoug Cuttingによって作成されました。機能的にはThriftと同等ですが、動的なデータ構造をシリアライズできる点でThriftより優れています。また静的なコード生成も必要ありません。また、ThriftからAvroに移行したもう一つの理由として、ThriftはFacebookよりASFに寄与されましたが、それ以降、開発活動があまり活発ではないためです。このためCassandraサーバプログラムのorg.apache.cassandra.thrift.CassandraServerは、org.apache.cassandra.avro.CassandraServerにポートされるでしょう。執筆時(訳者脚注:2010/5/14かな?)では未完成。より詳細な情報はAvroのプロジェクトページで確認できるでしょう。 http://avro.apache.org

  7. Bigtable
  8. Cassandra の前には、2006年にGoogleによってGFS上で動作する高速なカラム型データベースであるBigtableが作られました。Cassandraは sparse array dataや、ディスクを利用するSSTableというデータファイルフォーマットの仕様をBigtableから受け継いでいます。Yahoo!の HBaseはBigtableのクローンです。完全なBigtableの仕様はhttp://labs.google.com/papers/bigtable.htmlで読むことができます。

  9. Bloom Filter
  10. (訳者脚注:wikipediaの説明がわかりやすいです。http://ja.wikipedia.org/wiki/%E3%83%96%E3%83%AB%E3%83%BC%E3%83%A0%E3%83%95%E3%82%A3%E3%83%AB%E3%82%BF

    簡単に言うと、BloomFilterとは集合に要素が存在するかテストするための不確定なアルゴリズムです。不確定なので偽陽性(誤検出)の可能性はありますが、偽陰性はありません。Bloom Filterはデータ集合の値をbit配列に格納し且つ、大きなデータ集合を要約したものに圧縮します。この要約したものは定義上、オリジナルデータをそのまま扱うより空間効率が高いです。Cassandraはキーのルックアップ時にかかる高コストのディスクI/Oを削減するためにBloomFilter を使います。全てのSSTable毎にBloomFilterが存在します。クエリーが実行された際に、ディスクアクセスの前にBloomFilterをチェックします。このフィルターが対象要素が集合に存在しない事を示せば、それは確実に存在せずディスクI/Oは不要になり、集合に存在する可能性がある場合のみ、ディスクI/Oするよ。BloomFilterにとって偽陽性の可能性がある事はデメリットですが、それ以上にとても早く動作するのがメリットです。なぜなら配列や、ハッシュ構造や、リンクリストとは違い要素を完全な状態で保持しないため、空間効率がとても良いのです。そのかわり大量のメモリを利用し、ディスクI/Oを削減します。アルゴリズムの特性上、結果として要素が増える毎に、偽陽性も増加します。BloomFilterはHadoop、 Bigtable、Squidで使われています。BloomFilterは創案者のBurton Bloomにちなんで名づけられました。

  11. Cassandra
  12. ギリシャ神話では CassandraはトロイのKing PriamとQueen Hecubaの娘です。彼女はとても美しかったのでアポロは彼女に千里眼を与えました。しかしアポロからの恋のアプローチを拒んだ際に、能力に頼らず未来を言い当てるように罰を受けてしまいました。そんな彼女を誰も信じようとはしませんでした。彼女はCassandraはトロイの破壊を予見しましたが、それを止めるには力不足でした。Cassandraデータベースは、そんな彼女にちなんで名づけられたようです。

    このデータベースはapache projectとしてhttp://cassandra.apache.orgで稼動中です。Cassandraは以下に示すような特性を持っています。中央集権型ではない、伸縮自在、耐障害性、一貫性レベルをチューニング可能、高可用性、データセンタをまたいだコモディティサーバ群で超スケール可能な設計などなど。Digg, Facebook, Twitter, Cloudkick, Cisco, IBM, Reddit, Rackspace, SimpleGeo, Ooyala, and OpenXのような企業で使われています。CassandraはFacebookのサーチ性能の問題を解決するために作られました。チームは Facebookのサーチ担当エンジニアPrashant Malikと、Jeff Hammerbacher,  Avinash Lakshman, Karthik Ranganathanによって率いられました。このコードは2008/7にGoogleCodeにOSSとしてリリースされました。2009/3には ApacheのIncubatorProjectに移動し、2010/2/17にはトップレベルプロジェクトに格上げされました。"A Decentralized Structured Storage System"と呼ばれるFacebookのLakshmanとMalikによるCassandraの論文はここにあります。http://www.cs.cornell.edu/projects/ladis2009/papers/lakshman-ladis2009.pdf Avinash Lakshmanによる2008のブログ投稿では、FacebookでのCassandraの使用法について述べています。 http://www.facebook.com/note.php?note_id=24413138919&id=9445547199&index=9 Cassandraが何故適切に名づけられたのか(順調に成長を遂げているのか?)を理解するのは簡単です。コミュニティはCassandraや他の同様のNoSQLの時代がくると言い切っています。Amazon, Google, FacebookやTwitterといった企業が結果整合性のデータベースを広範囲で利用しているのにも関わらず、たくさんのアンチはいるみたいです。 GoogleのRanTavoryによって書かれたJavaクライアントHectorはCassandraの兄弟にちなんで名づけられました。

  13. Chiton
  14. 古代ギリシャでChitonは布の衣服を意味し、一般的には袖なしで男女問わず着衣していました。ChitonはBrandon Williamsによるオープンソースプロジェクトと同名で、このプロジェクトはPythonGTKで作成されたCassandra向けのブラウザソフトウェアです。現在は http://github.com/driftx/chitonでホストされています。関連するプロジェクトとしてTelephusがあります。これはTwisted Pythonで書かれたCassandra向けの低レベルなAPIクライアントです。現在はhttp://github.com/driftx/Telephusでホストされています。

  15. Cluster
  16. Clusterとは2つ以上のCassandraインスタンスが協調して動作している状態です。これらのインスタンスはGosshipプロトコルを利用して他のインスタンスと通信します。Clusterに新たにインスタンスを追加する場合には次に示す手順が必要です。最初にSeedNode を設定します。次にCassandraがlistenするGosshipと、Thrift用のポートを指定します。Clusterの設定ができたら、正しくセットアップできたかNodeToolで検査しましょう。

  17. Column
  18. ColumnはCassandraのデータモデルでもっとも基本的な単位を表します。Columnは3つの要素で構成されます。 name(時々「key」として参照されます)と、valueと、timestampです。Columnのvalueはtimestampとともに、クライアントによって提供されます。nameとvalueのデータ型はJavaのバイト配列で通常はStringとして表されます。timestampのデータ型はプリミティブなlong型です。Column群はマルチスレッドの諸問題を避けるため不変です。Colum群はColumnFamiliesに所属します。ColumnFamilyはそれぞれ別のファイルに格納されるので、関連のあるColumnは同じColumnFamilyに所属するように定義してください。Columnはorg.apache.cassandra.db.IColumnでインターフェースとして定義されています。このインタフェースでは、Columnからvalueをバイト配列として取得するメソッドや、SubColumnsをCollection<IColumn>で取得するもの、最新の変更を検索するものなどが提供されます。Column群は次に示すいずれかのタイプに分類されます。AsciiType, BytesType, LexicalUUIDType, LongType, TimeUUIDType, UTF8Type。 ColumnFamilyも参照してください。

  19. Column Family
  20. ColumnFamilyは簡単に言うとリレーショナルDBのテーブルと同質です。 Column群の順序付けされた集合を格納するコンテナの役割を担います。ColumnFamilyはそれぞれ別のファイルに格納されるので、一緒にアクセスされる可能性があるColumnは同じColumnFamilyに所属するように定義してください。ColumnFamilyはstorage-conf.xmlで定義できます。この定義ファイルでは、RowCacheサイズや、 KeyCacheサイズ、ReadRepairChanceなども設定可能です。 ColumnFamily群は、StandardかSuperいずれかのタイプになりえます。row中のColumnの順序は定義可能です。 Column、KeySpace、SuperColumnも参照してください。

  21. Column Name
  22. name/valueペアのname部でRowに格納されます。

  23. Column Value
  24. name/valueペアのvalue部でRowに格納されます。ColumnValue のサイズはマシンのメモリ容量に制限されます。

  25. Commit Log
  26. CommitLog はCassandraの全ての書き込み操作に対して責任があります。書き込み時実行時に、まず最初にCommitLogが書き込まれるので、障害が発生してもデータは失われないでしょう。それからvalueはパフォーマンスを考慮してmemtable(メモリ上のデータ領域)に格納されます。 memtableが一杯になると格納されていたデータはSSTableに書き込まれます。これはorg.apache.cassandra.db.commitlog.CommitLog で実装されています。全ての書き込み、削除操作毎にRowMutationオブジェクト形式のエントリがシリアライズされてCommitLogに追加されます。これらのオブジェクトはCommitLog領域に配置されます。 CommitLogは128Mに到達すると新しいCommitLogが生成され、以降は新しいCommitLogに書き込みが行われます。

    Cassandraクラスタが高い信頼性を提供するためにはサーバ設定ファイルを検査する必要があります。デフォルトでは、CommitLogSyncは「periodic」に設定されています。これは定期的にしかディスクにsyncしない事を意味します。つまりこの設定時はwrite-behindキャッシュ(訳者脚注:OSのページキャッシュ上に存在する状態)上のデータがディスクに同期されず失われる可能性があるという事です。この設定を「periodic」から「batch」に変更することで、writeが完了する前にCommitLogをディスクに同期するようになります。この変更によりパフォーマンスに影響がでますが、これは必要なトレードオフです。 またCommitLogSyncに設定した場合は、CommitLogSyncBatchWindowInMSにも適当な値を設定する必要があります。「MS」は、それぞれのsync操作の間隔をミリ秒単位で表したものです。なおこの設定はレプリケーションを利用する複数ノードクラスタ環境では一般的には必要とされません。なぜならレプリケーションの「定義」によっては、書き込みが他のノードに対して成功するまで完了を認めない設定もあるからです。

    もし「batch」を設定する事に決めた場合は、writeがディスクに同期するまでブロックされるので、パフォーマンスを考慮してCommitLogを別のデバイスに配置するとよいでしょう。

  27. Compaction
  28. Compactionは蓄積された大きなデータファイル群をマージすることで、ディスクスペースを空けるためのプロセスです。これは簡単にいうとリレーショナルDBにおけるテーブルの再構築と同質です。Compactionする事で、マージされたデータはソートされ、新たにインデックスが生成されます。そして新しくマージ、ソート、インデックスされたデータは一つの新しいファイルに書き込まれます。Compaction実行中には次に示す操作も実行されます。keyのマージ、Column群の結合、そしてtombstones(訳者脚注:削除フラグがたったデータ。後述のtombstonesを参照)の削除です。このプロセスはorg.apache.cassandra.db.CompactionManager.CompactionManagerクラスで管理されます。また実行状態を監査できるようにMBeanインタフェースを実装しています。Cassandraでは異なるタイプのCompactionが存在します。メジャーCompactionは次の二つのうちの一つをトリガーにして実行されます。ノードの探査中か、自動的に行われます(訳者脚注:データサイズが一定の基準値を超えた場合とか)ノード探査はTreeRequestメッセージを近隣のノードに送ります。これを受け取ったノードはColumnFamilyの妥当性をチェックするために、すぐさまread-onlyなCompactionを実行します。read-onlyなCompactionは次のステップで行われます。

    1. ColumnFamilyから割り当てられたkeyを取得します


    2. 一度Row群が妥当性チェックされます。この結果ColumnFamilyが妥当性チェックする必要があるなら、MerkelTreeが生成され、近隣のノード群にブロードキャストされる事になります。


    3. MerkleTreesはDifferencers(妥当性のチェックと、差分比較に必要なtree群)のリストとしてお互いに持ち運ばれます。


    4. 比較はStageManagerクラスによって実行され、ジョブを実行する際の並列実行の取り扱いに関して責任を持ちます。この場合StageManagerは、Anti-EntropyStageを利用します。これはorg.apache.cassandra.concurrent.JMXEnabledThreadPoolExecutorクラスを利用し、シングルスレッドでCompactionを実行します。そして監査のためにMBean操作を可能にします。

  29. Compression
  30. データの圧縮は将来的にはサポート予定ですがバージョン0.6では未サポートです。

  31. Consistency
  32. Consistencyはデータベースにトランザクションが半端な状態を残さない事を意味します(整合性が必ず強制される)。これはリレーショナルDBで考案されたもので、ACID特性の一つでもあります。Cassandraでは次のようにConsistencyの度合いを計算できます。

    N = レプリカが格納されるノードの数

    W = 書き込み成功を応答する前に、書き込みが受領されなければならないレプリカの数

    R = 読み込み操作の際に、コンタクトする必要があるレプリカの数

    W + R  > N = strong consistency

    W + R <= N = eventual consistency

  33. Consistency Level
  34. ここでは書き込み操作時に複製される必要があるレプリカの数や、読み込み操作時に成功か否か判定するために、読み込みに応答する必要があるレプリカの数を設定可能です。ConsistencyLevelはReplicationFactorによって設定します。実際のcluster内のノードの数ではありません。パフォーマンスを制御可能なように複数のConsistencyLevelがあります。もっともパフォーマンスを高くするにはConsistencyLevelを一番低くすることです。書き込みと、読み込みはそれぞれ別に設定します。

    ・書き込み操作向けの設定値

    ZERO: 書き込みはバックグラウンドで非同期に制御されます。これは一番速い方法ですが、信頼性も最も低いです。

    ANY: バージョン0.6で提供された設定。一つのノードに対して書き込みが成功したらOK。しかもhint(訳者脚注:駄目だった場合に他の生きてるノードに更新内容をキューイングする手続き。詳細はHinted Handoffを参照。)のみがOKだった場合も含みます。これは比較的低いConsistencyLevelです。

    ONE: 一つのノードに対して書き込みが成功したらOK。これは対象ノードのCommitlogとmemtableへの反映が完了した事を意味します。一つのノードから返答があったら操作は成功とみなします。

    QUORAM: quoramは操作が成功するために合意を得る必要があるノードの数を示します。これは「<ReplicationFactor> / 2 + 1」で決定されます。なので例えば10のReplicationFactorを指定していたら6レプリカ複製される事が、操作が成功するために必要なquoramになります。

    DCQUORAM: quoramの取得先ノードを同一データセンターに限定したバージョンです。これにより高いレベルのConsistencyLevelと低レイテンシのバランスが期待できます。

    ALL: 全部成功しなきゃだめ。一つでも失敗したらアウト。最も高いConsistencyLevelでかつ最も低いパフォーマンス。

    ・読み込み操作向けの設定値

    ONE: 一番最初に返答してきたノードの値を返します。バックグラウンドでread repairを実行します。

    QUORAM: 全部のノードにクエリを投げて・・・

    ALL: 全部のノードにクエリを投げて最も新しいtimestampを持った値を返します。全ノードの応答を待つので、一つでも駄目なら読み込みは失敗します。

    ※ZEROという設定値がないことに気がつかれた方もいらっしゃると思いますが、これは意味がありません!

  35. Data Center Shard Strategy
  36. Replication Strategyを参照

  37. Decentralized
  38. Cassandraにはマスターサーバは存在せず分散型として設計されています。変わりにボトルネックとSPOFを避けるためにP2Pなアプローチを取っています。分散はCassandraでは重要な概念です。これによりclusterのスケールアップ(サーバの追加)、スケールダウン(サーバの離脱)を最小限のリスクで実現できます。これを実現するためにCassandraで採用している方法はGossipプロトコルを利用することです。

  39. Denormalization
  40. RDBMSでは非正規化や、冗長なデータを保持する方式は、OLAPのような参照が多いアプリのパフォーマンスを向上させるためにしばしば適用されています。Cassandraでは一般的に非正規化されています。これはパフォーマンス向上のためや、必要とする問い合わせ毎にデータが構造化されるためです。標準的なRDBMSとの違いは、データが一般的に個別のオブジェクト(訳者脚注:問い合わせ毎の結果セットの事かな?)毎に構造化される事です。

  41. Durability
  42. データベースが信頼できるということは即ち、サーバがクラッシュしたり、突然の電源障害時でも、書き込み完了した操作が保障される事です。Cassandraでは更新内容をCommitLogの末尾に書き込み、サーバがデータファイルから位置を特定するのを防ぐことで高い信頼性を達成しています。CommitLogはファイルシステムに同期される必要があるだけです。同期は定期的か、指定された範囲で一括処理されるか、いずれかの方式で発生します。シングル構成で稼動している時は、ファイルのコアな状態がすぐにはストレージ装置に同期されません。これは書き込みが実行された後にサーバがシャットダウンした場合に、その後のリスタート時に書き込み内容が失われる可能性があるという事です。本番環境ではシングル構成は推奨されないことに注意してください。CommitLogも参照してください。

  43. Dynamo
  44. DynamoはGoogleのBigtableの仕様をもとにAmazonによって2006に参照実装されたものです。Cassandraの主要な基盤はDynamoをもとにしています。Cassandraは次に示す仕様を踏襲しています。KVS、対称なP2Pアーキテクチャ、gossipプロトコル、結果整合性と操作(読み込み、書き込み)毎のConsistencyLevelの設定。Dynamoの完全な論文はhttp://www.allthingsdistributed.com/2007/10/amazons_dynamo.htmlで読むことができます。

  45. Elastic
  46. read/writeのスループットともにマシンを追加する毎にリニアに増加します。

  47. Eventual Consistency
  48. Consistencyとは任意の操作のもとで対象データの内部的な一貫性について述べた特性です。強い一貫性を求められるデータベースの場合には、あるクライアントが書込み操作を実行したら、全ての読み込み処理がすぐに最新の更新された値を返さなければなりません。EventualConsistencyでは大抵すぐには一貫性は保障されませんが、結果的には整合性は取れます("eventually"は転送データ量や、clusterを構成するノード数、これらのノードの地理的な配置状況に依存しますが、通常は全てのレプリカに数ミリ秒で最新のデータが行き渡ります。DNSはEventualConsistencyアーキテクチャを利用したポピュラーな例です。

    EventualConsistencyは時々"weak consistency"と呼ばれます。EventualConsistencyはここ数年で一般的になってきました。巨大なスケーラビリティをサポートできるからです。トラディショナルな完全な一貫性をもったDBで高いスケーラビリティを維持するには、管理のオーバーヘッドが負荷になってきます。もちろんEventualConsistencyにもデメリットはあります。複雑なプログラミングモデルを実装するケース等です。けれどもCassandraにおけるEventualConsistencyの設計はDynamoの使用法に基づいています。CassandraはConsistencyLevelを設定可能な点でより良い特性をもっています。これはCassandraではConsistencyLevelの設定次第で、完全な一貫性も確保できることを意味します。

    Riak, Voldemort, MongoDB, Yahoo!'s HBase, CouchDB, Microsoft's Dynomite, and Amazon's SimpleDB/Dynamoは他の結果整合性データストア製品群です。

  49. Failure Detection
  50. Failuredetectionは耐障害性をもった分散システム上で障害が発生したノードを検知するプロセスです。Cassandraの実装はAccrualFailureDetectionのアイディアに基づいています。この論文は2004のJapan Institute of Science and Technologyで初めて発表されました。AccrualFailureDetectionは2つの主要なアイディアに基づいています。障害検知は監視対象アプリとは独立して柔軟であるべきという点と、障害検知モニタの障害発生判断をどのように信頼して、継続的に障害の嫌疑レベルを出力していくこと、以上の2点です。これはネットワーク環境の変動を考慮に入れることができるので望ましいです。heartbeatのサンプリングに基づく障害嫌疑の予兆は流動的で活発なので、単純な成功か失敗による判断はよろしくないです。Cassandraでの障害検知の実装はorg.apache.cassandra.gms.FailureDetectorクラスです。AccrualFailureDetectionの論文はhttp://ddg.jaist.ac.jp/pub/HDY+04.pdfで読むことができます。

  51. Fault-Tolerant
  52. Faulttoleranceとはシステムの一つ以上のコンポーネントに障害が発生した場合でも、そのシステムが提供するオペレーションを全面的に継続して稼動することができるシステム仕様の事です。Faulttoleranceはシステムが一部劣化する場合にも用いられる事があります。例えば、なんらかの障害によってそのシステムに劣化が発生した際に、その劣化したパフォーマンスの影響範囲が故障したコンポーネントだけに限定される場合などです。

  53. Gossip
  54. gossiperはcluster内の全ノードが、自分以外のノードの重要な状態情報に気がつけるようにする責任があります。gossiperは障害中のノードや、まだclusterに参加していないノードに対しても毎秒稼動することでノードの状態を受信できる事を保障します。これは急激に増加する負荷に対しても耐えうるように設計されました。gossipプロトコルはノードをまたいだkeyの再配置をサポートし、障害検知もサポートします。gossipはAnti-Entropy戦略において重要な役割を担っています。gossiperが共有している状態の情報はkey/valueペアで管理されています。Cassandraでは、gossipプロトコルは他のノードに対して常にノード状態の情報を流し続けます。サーバノードがスタートした際には自身でgossiperの輪に入ります。これ以上の情報はorg.apache.cassandra.service.StorageServiceクラスをチェックしてください。gossipに関するamazonの論文も参照してみてください。http://www.cs.cornell.edu/home/rvr/papers/flowgossip.pdf

  55. Hector
  56. GoogleのRanTavoryによって作成されたOSSプロジェクトでgitでホストされています。HectorはCassandraのJavaクライアントです。これはThriftをラップし、JMX、コネクションプール、F.Oを提供します。

  57. Hinted Handoff
  58. これは可用性、耐障害性、致命的ではない劣化を確保するためのメカニズムです。書き込みが発生した際に書き込み対象ノードが落ちていた場合に、書き込み内容("hint")を落ちたノードが復活した時にリプレイするために、これを他の生存ノードに格納("handed off")します。これにより次の二つが実現されます。障害ノード復活時に一度ミスっただけでも全てのデータを取得する必要がなくなり、F.Bの時間を削減できます。また低いConsistencyLevelで書き込み性能の向上します。HintedHandoffは、ConsistencyLevelがONE, QUORUM, ALLの時には書き込み成功としてはカウントされません。ANYの時だけカウントされます。しかしながらHintedによる書き込みで格納されたデータは、ANY以外のConsistencyLevelでは読み込むことができません。hintを受け取ったノードは障害ノードが復帰した事をGosshipですぐに検知できます。もし何らかの理由でHintedHandoffが動作しなかった場合は、システムはreadrepairを実行します。

  59. Key
  60. RowKeyを参照してください

  61. Keyspace
  62. KeyspaceはColumnFamily群のコンテナです。これはリレーショナルDBにおけるデータベースと同質のものです。Cassandraではアプリケーション毎に分割するするために使います。リレーショナルDBはテーブルの集合ですが、Keyspaceは順序付けされたColumnFamilyの集合です。Cassandraの定義ファイル(storage-conf.xml)でアプリケーションのKeyspaceを定義できます。Keyspaceを定義した場合は、そのReplicationFactorと、レプリカの配置戦略(ラック別とか、DC別とか)も定義できます。Cassandraクラスタでは一つ以上のKeyspaceを持つことができます。(定義したアプリケーション毎には一つまで)ColumnFamilyも参照してください。

  63. Lexicographic Ordering
  64. Lexicographicorderingとは2つの順序付けされた集合のデカルト積をアルファベット順でソートすることです。

  65. Memtable
  66. メモリ中のデータ構造で最新のデータが書き込まれています。memtableが一杯になるとディスク上のSSTableに同期されます。

  67. Merkle Tree
  68. HashTreeという名前なら聞いたことがあるんじゃないかな。MerkelTreeは巨大なデータ集合を小さな形式に要約した2分木構造です。HashTreeでは枝葉は要約(訳者脚注:大抵はhash値です)されたデータブロック(一般的にはファイルシステム中のファイル)のことです。Tree中の全ての親ノードは直接の子ノードのHash値です。きっちりとしたコンパクトな要約ですよね。CassandraではMerkleTreeはorg.apache.cassandra.utils.MerkleTreeで実装されています。CassandraではP2Pのノード間で不変で堅牢なデータのやりとりを確保するために利用しています。MerkleTreeは暗号化や、ファイルや伝送データのベリファイに使われます。GoogleWaveでも使われているようです。創案者のRalph Merkleにちなんで名づけられました。

  69. Multiget
  70. keyの集合を渡して問い合わせます

  71. Multiget Slice
  72. keyの集合を渡してcolum集合の一部を取得するように問い合わせます

  73. Node
  74. Cassandraのインスタンスです。大抵Cassandraのクラスタは複数のノードを持っています。ノードリングと呼ばれたりします

  75. Node Tool
  76. これはクラスタ構成が正しく設定されているか検査したり、様々なメンテナンス操作を実行するための実行ファイルです。bin/nodetoolに配置されます。NodeToolで実行可能なコマンドはcleanup, clearsnapshot, compact, cfstats, decommission, drain, flush, info, loadbalance, move, repair, ring, snapshot [snapshotname], removetoken, tpstatsです。例えば「nodetool drain」はCommitLogへのあらゆる書き込みを停止させます。

  77. NoSQL
  78. NoSQLはSQLを利用しない、もしくはリレーショナルデータモデルではないデータベースの総称です。またNoSQLの支持者が、リレーショナルDBは悪い選択なので提案しないけど、他にも選択肢があることを示したい場合に「Not Only SQL」という意味をこめて利用される場合があります。

  79. Order-Preserving Partitioner
  80. これは次の仕様を持つPartitionerの一種です。rowsはあなたが指示したソート順序でデータを物理的に整列させたkey順で格納されます。Order-PreservingPartitionerを使用するようにColumnFamilyに設定することで、RangeSliceが可能になります。これはCassandraがどのNodeがどのkeyを持っているのか知っていることを意味します。このPartitionerはRandomPartitionerとは対称的な仕様です。効率的な範囲問い合わせが可能になりますが、keyの分散に偏りがでます。Order-PreservingPartitioner(OPP)はorg.apache.cassandra.dht.OrderPreservingPartitionerクラスで実装されています。CollatingOrder-PreservingPartitioner(COPP)と呼ばれる特別なOPPも存在します。基本的にはOPPと同じように振舞いますが、バイトオーダー順のかわりに、English/US辞書順でソートされます。これはロケールを意識したアプリケーションで有効です。COPPはorg.apache.cassandra.dht.CollatingOrderPreservingPartitionerで実装されています。Tokenも参照してください。

  81. Partition
  82. 一般的にPartitionはネットワーク分断を表します。Partitionはスイッチ、ルータ、NICの故障などで引き起こされます。ABCDEという5台のマシンでclusterが組まれていてAB、CDEでサブネットが分かれている場合を考えてみてください。CDEが所属するスイッチに接続障害が起きた場合、AB、CDEでクラスタが分離されネットワーク分断が発生します。Cassandraは耐障害性を備えたDBです。ネットワーク分断のような障害に対しても堅牢です。ネットワーク分断中でもサービスは継続することが可能で、ネットワーク復旧するとすぐにレプリカでデータをマージします。

  83. Partitioner
  84. Partitionerはどのようにデータを分散させるかを制御します。keyの集合をみつけるためにCassandraはノードが所有している値の範囲を知っておく必要があります。Order-PreservingPartitionerと、RandomPartitionerという二つのがあります。storage-conf.xmlで「<Partitioner>org.apache.cassandra.dht.RandomPartitioner</Partitioner>」のようにPartitioner要素を記述することで設定できます。partitioningはcolumnではなくrowkeysをもとに分散させるので注意してください。partitionerは一度決定したら、データを削除しない限りは変更不可能です。(SSTableは変更不可能なため)Order-Preserving Partitioner、RandomPartitionerも参照してください。

  85. Quorum
  86. ある操作に対して応答するノードが過半数であることを意味します。ConsistencyLevelで設定可能です。Quorum読み込みではproxyノード(訳者脚注:Cassandraクライアントが接続したノード)が、過半数のノードから同じ値が帰ってくるのを待ちます。これは読み込み操作を遅くするかわりに、古いデータが返却されないことを保障します。(訳者脚注:最新であるとは限りません)

  87. Rack Aware Strategy
  88. ReplicationStrategyを参照してください

  89. Random Partitioner
  90. MD5によるBigIntegerTokenでNodeRingにおけるkeyの配置場所を決定するPartitionerです。これはkeyがclusterに満遍なく配置されますが、範囲問い合わせが遅くなります。これはデフォルトのPartitionerです。Partitioner、RandomPartitionerを参照してください。

  91. Range Slice
  92. keyの範囲でcolumn群の一部を取得する問い合わせ

  93. Read Repair
  94. NodeRing全体で一貫性を確保するためのもう一つのメカニズムです。読み込み操作で、応答したノードの返却値が一致しない場合、データが古いノードに対してReadRepairを実行するためのメモを残します(非同期で実行するためにキューするという意だと思われる)。ReadRepairはCassandraが読み込み操作で得た最新のデータをもとに、古いデータを保持しているノードを更新するために、書き込み操作することを意味します。これは、まず全てのデータを取得し、マージを実施し(カラム単位で最新の情報を生成する処理かな?)、マージされたデータを同期すべきノードに書き戻すことで実現されます。データ不整合の検知はタイムスタンプとチェックサムの比較で実施されます。このデータを一致させるまでの一連の流れは、org.apache.cassandra.streamingパッケージに実装されています。

  95. Replication
  96. 一般的な分散システムでは、複数のマシンに、複数のコピーを格納することで、マシンの障害や、ネットワーク分断が発生してもclusterがデータを維持することができます。これを理解するための手助けとしては、キャッシュもReplicationの一つという事です。CassandraではReplicationは高いパフォーマンスと、高可用性、耐障害性を提供するための手段です。

  97. Replication Factor
  98. CassandraはConsistencyLevelの強度を高めるために、どの程度パフォーマンスを犠牲にするのかを決定するために設定可能なReplicationFactorを提供します。読み込み、書き込みのConsistencyLevelはReplicationFactorに基づいています。これはノードをまたいだレプリカの複製数を示します。ReplicationFactorはstorage-conf.xmlのReplicationFactor要素で設定可能です。

  99. Replication Strategy
  100. レプリケーション戦略はどのようにレプリカを分散させるかを決定する配置戦略についても言及します。最初のレプリカは常にTokenが所属するkey範囲を担当するノードに配置されます。他のレプリカは設定可能なレプリケーション戦略によって分散されます。レプリケーション戦略をプラガブルにするためにGOFのデザインパターンの一つ「ストラテジパターン」が採用されていますが、Cassandraでは創造的な3つのパターンが提供されます。レプリケーション戦略を選択することは、どのノードがどのkey範囲を担当するのかを決定することであり、どのノードが書き込み対象になるかを決定することでもあります。これは戦略によって性能に大きな影響があるという事です。Cassandraはネットワーク構成や、そのほかの要件に応じて柔軟に対応できるレプリケーション戦略を用意しています。

    最も単純な戦略はRackUnawareStrategyと呼ばれています。名前の通りラックの位置を気にしません。

    より気の利いたポピュラーな戦略としてRackAwareStrategyがあります。この戦略はCassandraクラスタが一つ以上のDCにまたがって収容される場合には重要です。この戦略は次に示す2つの処理を実施します。最初のレプリカは前述の通りTokenに基づいて自動的に決定されます。2番目のレプリカは最初のノードとは異なるDCに配置されます。3番目以降のレプリカは同一DC内の異なるラックに配置されます。

    3番目の戦略はDatacenterShardStrategyです。これはRackAwareStrategyより均等にDC間でデータを配置する指定が可能です。これを使うにはdatacenters.propertiesを用意する必要があります。このファイルにそれぞれのDC毎に要求されるレプリケーション戦略を指定できます。このファイルはorg.apache.cassandra.locator.DataCenterShardStrategyクラスによって読み込まれ、実行されます。レプリケーション戦略群はorg.apache.cassandra.locator.AbstractReplicationStrategyクラスを継承したものです。このクラスを継承することで独自のレプリケーション戦略を実装する事が可能です。

  101. Row
  102. ColumnFamilyでは、RowはColumnNameに対するColumnValueを結びつけてソートしたデータです。SuperColumnでは、RowはColumnNameに対するColumnValueを結びつけた集合をSuperColumnNameと結びつけてソートしたデータです。RowKeyはRow毎に定義されます。Rowはcolumn群のname/valueペアの集合になります。一つのRowあたりのサイズはディスク領域を超過することはできません。Row群はRandomPartitioner, OrderPreservingPartitioner, CollatingOrderPreservingPartitionerの内のいずれか一つのPartitionerによって配置されるノードが決まります。Rowはorg.apache.cassandra.db.Rowクラスで定義されています。Rowkeyも参照してください。

  103. Row Key
  104. 単純に"Key"とも呼ばれます。RowKeyはリレーショナルモデルにおけるプライマリキーと同質です。これによりRowを特定する事ができます。データ型は任意の長さのStringです。ThriftインタフェースではJavaクライアントはRowkeyをUTF-8エンコードされたものとして扱いますが、他の言語クライアントではそうとは限らないので、場合によってはアプリでUTF-8への変換を行う必要があるかもしれません。

  105. SEDA (Staged Event Driven Architecture)
  106. Cassandraは並列実行環境で高い性能を得るためにSEDAを採用しています。SEDAはマルチスレッドによるオーバーヘッドを無視できるような性能を出すことを目論んでいます。マルチスレッドのオーバーヘッドには、スレッドのスケジューリング、ロックの競合、キャッシュミス(訳者脚注:L2キャッシュなどCPUアーキレベルでのお話しかな)等があります。SEDAは一つの仕事を同じ一つのスレッドで完結させません。これはより複雑な実装をすることになりますが、よりよいパフォーマンスをもたらします。したがって、Cassandraの主要な処理(Reading, Mutation, Gossiping, memtable flushing, Compaction)は、Stageとして実装されます。Stageは本質的には分割されたイベントキューです。イベントがキューに入った際に、アプリケーションによって実装されたイベントハンドラが発動します。コントローラは要求に応じてそれぞれのStageに適当なスレッド群を動的に割り当てます。SEDAによる利点は高い並列実行性と、CPU、メモリ、DISK、ネットワーク資源の効率的な利用を実現します。SEDAの提唱者(Matt Welsh, David Culler, and Eric Brewer)による論文を見ることができます。http://www.eecs.harvard.edu/~mdw/proj/seda Stageも参照してください。

  107. Seed Node
  108. SeedNodeはCassandraクラスタにすでに参加しているノードが担当します。これは新しいノードがクラスタに参加する際に利用されます。SeedNodeは新しい参加ノードに対してクラスタの形状や、状態の情報を伝えるためにgossipします。SeedNodeはクラスタで複数存在する可能性があります。storage-conf.xmlで次のように設定します。「<Seeds><Seed>10.0.1.1</Seed></Seeds>」

  109. Slice
  110. 読み込み系問い合わせの一種です。任意の一つ以上のColumnNameを取得する場合は「get_slice()」を使いましょう。複数Keyを渡して複数Column群を取得する場合は「get_range_slice()」を使いましょう。

  111. Snitch
  112. Snitchはノードとネットワークの物理的な位置を結びつけるためのCassandraの手法です。新ノードの発見と効率的なネットワーク越しのリクエストを実現するために、適切なノード位置を決定するのに役に立ちます。Snitchは何種類かあります。

    EndpointSnitchは、例えば、二つのノードが同一DCに属するのか、同一ラックに属するのかを決定します。これは対象ノードのIPアドレスの2、3番目のオクテットに基づいてノード間の相対的な距離を推測することで判断します。

    DataCenterEndpointSnitchは、ラックとDCとの紐付け情報と、サブネットに基づいたラックの指定をもとに判断します。

    PropertyFileSnitchは「cassandra-rack.properties」というファイルで、IPアドレス単位でラックとDCを紐付けを行い判断します。

    Snitchクラス群はorg.apache.cassandra.locatorパッケージにあります。

  113. Sparse
  114. リレーショナルモデルではテーブル毎に例え値がnullの場合でも、カラム毎に値を保持しなければなりませんが、一方Cassandraでは、スパースもしくはスキーマフリーなデータモデルを提供します。これは定義したColumn分データを持つこともあれば、ほんの一部だけ持つ場合もあるという事です。これは空間効率を考慮しています。例えば、1000×1000のスプレッドシートをリレーショナルDBで実現することを考えてみてください。多くのセルがnullの場合は非効率です。

  115. SSTable
  116. SSTableは"Sorted String Table"の略です。disk上のデータ格納形式はGoogleのBigtableを継承しています。バッファリングとデータのソートをするために、SSTableへの書き込みの前に、ログ構造のMemtableへの追記書き込みのみを行います。このためSSTableは書き込みで高いパフォーマンスを発揮します。またコンパクションも可能です。Compactionを参照してください。SSTableは不変です。MemtableからSSTableへの書き込みがされたら、アプリケーションからは変更できません。CompactionだけがSSTableのディスク上の構造を変更できる唯一の手段です。JSON形式でのインポートまたはエクスポートするには、org.apache.cassandra.tools.SSTableImporterとSSTableExporterをチェックしてください。

  117. Stage
  118. Cassandraでは一部SEDAを利用して、処理の基本単位をStageとしてラップしています。StageはCassandraの性能を左右する重要な要素です。一つの操作は一つのスレッドで完結する場合よりも、むしろ複数のStageを経由して完了することになるでしょう。Stageはイベントキュー、イベントハンドラとそれに結び付けられたスレッドプールで成り立ちます。Stageはスレッドのスケジューリングとアロケーションを決定するコントローラによって管理されます。Cassandraではjava.util.concurrent.ExecutorServiceを利用してスレッドプールを使った並列実行モデルを実装しています。より詳細な情報はorg.apache.cassandra.concurrent.StageManagerクラスを参照してください。次に示す処理がCassandraではStageとして実装されています。

    Read,Mutation,Gossip,Response,Anti-Entropy,Load Balancer,Migration,Streaming

    他にもいくつかStageとして実装されているものがあります。ColumnFamilyStoreクラス内のMemtableと協調動作するところや、StorageService内のConsistencyManagerです。操作は複数のスレッドを経由して実行されます。スレッド間の連携は当該スレッド同士で直接は行われませんが、複数のStageをまたいで実行されます。SEDAも参照してください。

  119. Strong Consistency
  120. 読み込み系の操作で、修復の必要性(一貫性がない場合)を検知した場合に、まずReadRepairを実行して、それから結果を返します。

  121. SuperColumn
  122. SuperColumnは値がStringではなく、Column集合の写像です。この場合、Column集合はSubcolumnsと呼ばれます。Subcolumnsは順序付けされており、写像するColumnの数に制限はありません。タイムスタンプを持たない点でも他の通常のColumnとは違います。SuperColumnは再帰的には利用できません。一階層までしか利用できないという事です。通常のColumn群の写像をもてるだけで、SuperColumnの写像はできません。SuperColumnはIColumnと、IColumnContainerインタフェースを実装したSuperColumn.javaで定義されています。このインタフェースは次のような機能を提供します。名前から一つのSubcolumnを取得したり、Subcolumnを追加したり、削除したり、SuperColumn内のSubcolumnの数をチェックしたり、Subcolumnの最終更新時間をチェックしたりなど様々です。SuperColumnはオリジナルのBigtableにはないFacebookによって加えられた更新の一つです。

  123. Thrift
  124. ThriftはCassandraサーバと通信するためのRPCクライアントです。様々な言語向けのインタフェースを静的に生成します。C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk, やOCaml等です。これによりこれら複数の言語でCassandraと通信することが可能になります。Thriftは2007年にFacebookで開発され、2008年3月にapacheのincubatorプロジェクトに寄与されました。この原稿の執筆時(2010年5月)では、Thriftはより新しく、より活発なApacheProjectのAvroに置き換えられています。Avroは静的なコード生成が必要ありません。Thriftについての詳細はhttp://incubator.apache.org/thriftのプロジェクトページを読んでください。

  125. Timestamp
  126. Cassandraでは、タイムスタンプはクライアントが付与するので、クライアントのシステムクロックを同期させておかねばなりません。タイムスタンプはUnixepochからの経過ミリ秒単位で表されます。

  127. Token
  128. クラスタ内のそれぞれのノードは格納するデータのKey範囲を決めるためのTokenを保持しています。これはクラスタ内の直前のノードのTokenによって決められます。Tokenの値はPartitionerに依存します。RandomPartitionerではTokenは0~2の127乗の範囲内の整数値になります。これはKeyのMD5を適用することで生成されます。org.apache.cassandra.dht.BigIntegerTokenクラスで実装されています。OrderPreservingPartitionerではTokenはKeyに基づいたUTF-8のStringです。org.apache.cassandra.dht.StringTokenクラスで実装されています。Tokenはorg.apache.cassandra.dht.Tokenで実装されています。

  129. Tombstone
  130. Cassandraではパフォーマンスを考慮して、削除操作をしてもすぐにデータは削除されません。かわりにデータに、すでに削除されたにも関わらず、実は完全には削除されていない事を示す"tombstone"がマークされます。"tombstone"は他のレプリカに伝播されます。"tombstone"はメジャーCompactionで削除されます。

  131. Vector Clock
  132. (訳者脚注:少々わかりずらいので、アルゴリズムの詳細を知りたい方はhttp://funini.com/kei/logos/clock.shtmlを参照してください)

    ※VectorClockによるイベントの同期機構は0.7で取り込まれる予定

  133. Weak Consistency
  134. 読み込み系の操作で、一番最初に帰ってきた値を返すことで高いパフォーマンスを提供します。この後に必要に応じてReadRepairを実行します。

 

★関連記事

 

yoshiyuki kanno's Space


  • software engineer

  • drumer

  • fuji rocker

  • cat lover