Apache Cassandra データベースについての検討

この NoSQL データベースの利点と欠点は何か?

NoSQL ストレージは、リレーショナル・データベースに代わる、柔軟でスケーラブルな手段となります。NoSQL ストレージは数多くありますが、なかでもよく選ばれているものの 1 つが Cassandra です。この記事では、Cassandra に関してよく知られている詳細から一歩踏み込んで、あまりよく知られていない詳細を探ります。具体的には、Cassandra のデータ・モデル、ストレージ・スキーマの設計、アーキテクチャー、そして Cassandra に関する意外な事実を詳しく探ります。

Srinath Perera, Senior Software Architect, WSO2 Inc

Photo of Srinath PereraSrinath は、WSO2 Inc. のシニア・ソフトウェア・アーキテクトとして、CTO と共に WSO2 プラットフォーム・アーキテクチャー全体を監督しています。その傍ら、Lanka Software Foundation では研究科学者として働き、モラトゥワ大学のコンピューター・サイエンスおよびエンジニア学部では客員教授として教鞭をとっています。彼は Apache Axis2 の共同創始者であり、2002年から Apache Web Service プロジェクトに関わっています。現在、Apache ソフトウェア財団、PMC、および Apache Web Service プロジェクトのメンバーです。さらに、Apache オープンソース・プロジェクトの Axis、Axis2、Geronimo のコミッターでもあります。彼は米国のブルーミントンにあるインディアナ大学でコンピューター・サイエンスの博士号と理学修士号を取得し、スリランカのモラトゥワ大学でコンピューター・サイエンスおよびエンジニアリングの理学士号を取得しました。



2012年 8月 02日

はじめに

データベースの歴史を辿った論文「What Goes Around Comes Around」(「参考文献」を参照) の中で、Michal Stonebraker 氏はストレージ手法が時間とともにどのように進化していったかを詳しく説明しています。リレーショナル・モデルに辿り着くまでは、開発者たちは階層型有向グラフなどの他のモデルを試していました。注目に値するのは、今でもデファクト・スタンダードとなっている SQL ベースのリレーショナル・モデルは、約 30 年間も主流となっていることです。コンピューター・サイエンスの短い歴史と急速に進化するペースを考えると、これは目覚しい業績と言えます。リレーショナル・モデルは、長年にわたって定着していたため、ソリューション・アーキテクトがアプリケーションのデータ・ストレージを選択するのは簡単でした。つまり、いつも決まってリレーショナル・データベースが選択されていたのです。

システムのユーザー・ベースやインターネット・ユーザーが増加し、モバイル機器、クラウド・コンピューティング、マルチコア・システムなどが進化したことにより、大規模なシステムが次第に増えてきました。このような規模 (スケール) の問題にいち早くぶつかったのは、Google や Amazon などのハイテク企業です。これらの企業が、リレーショナル・データベースは大規模システムをサポートするには不向きであることに気付くまでに、時間はかかりませんでした。

スケールの問題に対処するために、Google と Amazon のそれぞれが代替ソリューションを考案しました。それが、Big Table と Dynamo (「参考文献」を参照) です。これらのソリューションでは、より高度なスケーラビリティーを実現するために、リレーショナル・データ・モデルによって提供される「保証」が緩和されています。Eric Brewer 氏の「CAP 定理」(「参考文献」を参照) は、こうした保証の緩和に関する見解を正式な定理としてまとめたものです。この定理では、スケーラブルなシステムにおける一貫性、可用性、および分断耐性はトレードオフであり、このすべての特性を同時に満たすシステムを構築することは不可能であると主張しています。Google および Amazon による前述の取り組みと、スケーラブルなシステムに関して得られた理解を基に、まもなく新たな種類のストレージ・システムが提案されました。これらのストレージ・システムに付けられたのが、「NoSQL」という名前です。この名前は当初、「スケーリングが必要であれば、SQL を使用すべきでない (do not use SQL if you want to scale)」ことを意味していましたが、後になって、SQLベースのソリューション以外にもソリューションがあることを意味する「SQL だけでなはない (not only SQL)」という定義に変更されました。

NoSQL システムは数多くあり、そのそれぞれがリレーショナル・モデルの何らかの側面を緩和、または修正しています。注目に値する点は、すべてのシナリオに有効な NoSQL ソリューションは 1 つもないことです。いずれのソリューションにもリレーショナル・モデルに勝る利点がありますが、そのスケーラビリティーが実現されるのは、そのソリューションを使用する一部のケースに限られます。アプリケーションの要件に合った NoSQL ソリューションを判断する方法については、私が以前に書いた記事「Finding the Right Data Solution for Your Application in the Data Storage Haystack」で説明しています (「参考文献」を参照)。

Apache Cassandra (「参考文献」を参照) は、今でも最も広く使用されている初期の NoSQL ソリューションの 1 つです。この記事では、Cassandra を詳しく取り上げ、初めて Cassandra を検討するときにはすぐに気付かない詳細かつ難解な点を指摘します。


Apache Cassandra

Cassandra は、NoSQL のカラム・ファミリーを実装したものであり、Amazon Dynamo が導入したアーキテクチャーの側面を利用して Big Table データ・モデルをサポートします。Cassandra には、例えば以下の特長があります。

  • 単一障害点のない、高度なスケーラビリティーと可用性
  • NoSQL のカラム・ファミリーの実装
  • 極めて優れた書き込みスループットと、優れた書き込みスループット
  • SQL ライクな問い合わせ言語 (0.8 以降) およびセカンダリー・インデックスによる検索のサポート
  • 調整可能な一貫性とレプリケーションのサポート
  • 柔軟なスキーマ

以上の特長から、Cassandra を推奨することは簡単ですが、開発者がこのプログラムの複雑さを理解するためには、Cassandra の詳細かつ難解な点を深く掘り下げることが不可欠です。

Cassandra は、カラム・ファミリー・データ・モデルに従ってデータを保管します (図 1 を参照)。

図 1. Cassandra のデータ・モデル
キー・スペース内でのカラムと行の関係を示す図

カラムについて

「カラム (column)」という名前はその概念を表す上では若干不適切であり、おそらく「セル (cell)」という名前を付けたほうが理解しやすかったかもしれません。しかしこの記事では、一般に使われている呼称であることから「カラム」を使います。

Cassandra のデータ・モデルは、カラム、行、カラム・ファミリー、キー・スペースからなります。それぞれの要素について、詳しく見ていきましょう。

  • カラム ― Cassandra データ・モデルの最も基本的な単位。各カラムは、名前、値、タイムスタンプで構成されます。この記事ではタイムスタンプについては無視するので、カラムは名前と値のペアとして表現することができます (例えば、author="Asimov")。
  • 行 ― ラベルとして名前が付けられたカラムの集合。一例としてリスト 1 に、行を表現する方法を示します。
    リスト 1. 行の例
        "Second Foundation"-> {
        author="Asimov", 
        publishedDate="..",
        tag1="sci-fi", tag2="Asimov"
        }

    Cassandra は多数のストレージ・ノードからなり、ストレージ・ノードごとに 1 つの行を保管します。各行のなかにはカラムが保管され、常にカラム名でソートされます。Cassandra がサポートするスライス・クエリーでは、このソート状態を利用することで、ユーザーが指定した行に保管されたカラムのうち、特定のカラム名の範囲に含まれるカラムだけを取得することができます。例えば、tag0 から tag9999 の範囲を指定したスライス・クエリーでは、名前が tag0 から tag9999 の範囲に入るすべてのカラムが取得されます。

  • カラム・ファミリー ― 名前のラベルが付けられた行の集合。リスト 2 に、カラム・ファミリーのサンプル・データの一例を記載します。
    リスト 2. カラム・ファミリーの例
        Books->{
        "Foundation"->{author="Asimov", publishedDate=".."},
        "Second Foundation"->{author="Asimov", publishedDate=".."},
        …
        }

    カラム・ファミリーは、リレーショナル・モデルでのテーブルのようなものだとよく言われます。けれども以降の例でわかるように、リレーショナル・モデルとの類似性はそこまでです。

  • キー・スペース ― 多数のカラム・ファミリーで構成されるグループ。これは単なるカラム・ファミリーの論理グループ分けに過ぎず、名前のスコープを分離するためのものです。

さらに、カラム・ファミリー内には、1 つのキーで複数のカラムをグループ化するスーパー・カラムがあります。開発者がスーパー・カラムを使用することは推奨されないため、スーパー・カラムについては説明しません。


Cassandra と RDBMS とのデータ・モデルの比較

上記の Cassandra データ・モデルの説明から、データは各カラム・ファミリー内で 2 次元 (2D) 空間に配置されることになります。したがって、ユーザーがカラム・ファミリーのデータを取得するには、行の名前とカラムの名前という 2 つのキーが必要になります。この点に関しては、リレーショナル・モデルと Cassandra は同様ですが、この 2 つの間には重要な違いがいくつかあります。

  • リレーショナル・モデルでの列は、テーブル内のすべての行で一様です。通常、データ項目の間には明らかな縦の関係が存在しますが、これは Cassandra のカラムの場合には当てはまりません。これが、Cassandra が各データ項目 (カラム) と一緒にカラム名を保管する理由です。
  • リレーショナル・モデルの場合、2D データ空間は完全に満たされます。2D 空間の各ポイントには、少なくとも null 値が保管されていなければなりません。この点に関しても、Cassandra には当てはまりません。Cassandra では、一部の行には数少ない項目だけを含め、その他の行には数百万の項目を含めることができます。
  • リレーショナル・モデルでは、スキーマが事前に定義され、実行時にスキーマを変更することはできません。一方、Cassandra ではユーザーが実行時にスキーマを変更することができます。
  • Cassandra は常に、カラムがその名前でソートされるようにデータを保管します。そのため、スライス・クエリーを使用して容易にカラムでデータを検索できるようになっています。その一方、OrderPreservingPartitioner を使用しない限り、行でデータを検索するのは困難です。
  • もう 1 つの重要な違いとして、RDMBS での列名は、データに関するメタデータを表すのであって、データを表すことは決してありません。一方、Cassandra では、カラムの名前にデータを組み込むことができます。したがって、Cassandra では行に何百万ものカラムを含めることができる一方、リレーショナル・モデルで一般に行に含められる列の数は数十に限られます。
  • 明確に定義された不変のスキーマを使用するリレーショナル・モデルは、JOIN や集約などの高度なクエリーをサポートします。リレーショナル・モデルでは、ユーザーがクエリーについて懸念することなくデータ・スキーマを定義することができます。Cassandra は、JOIN のみならず、SQL 検索メソッドをほとんどサポートしていません。そのため、スキーマはアプリケーションで必要なクエリーに対応しなければなりません。

上記の違いを詳しく探るために、例として書籍を評価するサイトを取り上げます。このサイトでは、ユーザーが書籍に関する情報 (著者、ランク、価格、リンク) とコメント (テキスト、時刻、名前) をサイトに追加し、その書籍にタグを付けることができます。このアプリケーションは、以下のユーザー操作をサポートする必要があります。

  • 書籍の追加
  • 書籍に対するコメントの追加
  • 書籍に対するタグの追加
  • ランクでソートした書籍リストの作成
  • タグを基準とした書籍リストの作成
  • 書籍の ID を基準としたコメントのリストの作成

このようなアプリケーションをリレーショナル・モデルで実装するのは、ごくありきたりなことです。図 2 に、データベース設計の ER (Entity-Relationship) 図を示します。

図 2. 書評サイトの ER モデル
書評サイトのデータ・モデルを示すフロー図。

それではこのアプリケーションを、Cassandra データ・モデルを使用して実装する方法を見ていきましょう。リスト 3 に、Cassandra で考えられるスキーマを記載します。最初の行は、Books カラム・ファミリーを表します。このカラム・ファミリーに束ねられた複数の行のそれぞれには、書籍のプロパティーがカラムとして含まれます。<TS1> と <TS2> はタイムスタンプです。

リスト 3. 書評サイトに対する Cassandra スキーマの例
Books[BookID->(author, rank, price, link, tag<TS1>, tag<TS2> .., 
    cmt+<TS1>= text + "-" + author) …] 
Tags2BooksIndex[TagID->(<TS1>=bookID1, <TS2>=bookID2, ..) ] 
Tags2AuthorsIndex[TagID->(<TS1>=bookID1, <TS2>=bookID2, ..) ]
RanksIndex["RANK" -> (rank<TS1>=bookID)]

表 1 に、このスキーマに従ったデータ・セットの例を記載します。

表 1. 書評サイトのサンプル・データ
カラム・ファミリー名 サンプル・データ・セット
Books


"Foundation" -> ("author"="Asimov", "rank"=9, "price"=14, "tag1"="sci-fi", "tag2"="future", "cmt1311031405922"="best book-sanjiva", "cmt1311031405923"="well I disagree-srinath")
"I Robot" -> ("author"="Asimov", "rank"=7, "price"=14, "tag1"="sci-fi" "tag2"="robots", "cmt1311031405924"="Asimov's best-srinath", "cmt1311031405928"="I like foundation better-sanjiva")
RanksIndex "Rank" -> (9="Foundation", 7="I Robot")
Tags2BooksIndex
"sci-fi" -> ("1311031405918"="Foundation", "1311031405919"="I Robot"
"future" -> …
Tags2AuthorsIndex "sci-fi" -> (1311031405920="Asimov")
"future" -> …

この例には、リレーショナル・モデルと Cassandra モデルとの間の設計の違いがいくつか示されています。Cassandra モデルは、書籍に関するデータを Books という名前の単一のカラム・ファミリーに保管します。他の 3 つのカラム・ファミリーは、クエリーをサポートするために作成されたインデックスです。

Books カラム・ファミリーを詳しく見てみると、このモデルは行を使用してそれぞれの書籍を表し、書籍の名前を行の ID としています。書籍に関する詳細はカラムとして表されて、行の中に保管されます。

よく見ると、保管されるデータ項目 (コメントや、書籍と 1 対 M の関係を持つタグなど) も、同じ 1 つの行に保管されることに気付くはずです。このように単一の行に保管するために、タグおよびコメントのカラム名にはタイムスタンプが追加されます。この手法では、すべてのデータが同じカラム内に保管されます。この措置により、JOIN を行わなくてもデータを検索することが可能になります。この手法で、Cassandra は JOIN のサポートの欠如を解決しています。

この手法には、以下の利点があります。

  • 行全体を読み取る単一のクエリーで、書籍に関するすべてのデータを読み取ることができます。
  • JOIN を使用しなくても、cmt0-cmt9999 と tag0-tag9999 をそれぞれ範囲の開始と終了に設定したスライス・クエリーを使用すれば、コメントとタグを取得することができます。

Cassandra はカラム名を基準にソートしてカラムを保管するため、スライス・クエリーの実行に要する時間は極めて短く済みます。注目に値する点は、データ項目に関するすべての詳細を単一の行に保管すること、そしてソート状態を利用することが、Cassandra のデータ設計の背後にある最も重要な概念であることです。ほとんどの Cassandra データ・モデル設計は、何らかの形でこの 2 つの概念に従います。ユーザーはデータを保管してインデックスを作成する際に、ソート状態を利用することができます。例えば、タイムスタンプをカラム名に追加することによる副次効果として、カラム名はソートされた順序で保管されるため、カラム名の末尾にタイムスタンプが追加されたコメントは、作成された順序で保管され、検索結果もこの順序になります。

Cassandra の基本設計では、検索メソッドは一切サポートされません。Cassandra はセカンダリー・インデックスをサポートするものの、そのサポートには、後で作成されたインデックスが使用されます。そのため、セカンダリー・インデックスには、範囲クエリーのサポートが欠如していることを含め、いくつかの制約があります。

したがって、Cassandra データ設計で最善の結果を得るためには、ユーザーがカスタム・インデックスを作成し、カラムと行のソート状態を利用して、検索機能を実装する必要があります。まさにそれを行っているのが、他の 3 つのカラム・ファミリー (Tags2BooksIndex、Tags2AuthorsIndex、RankIndex) です。ユーザーはタグを指定して書籍を検索しなければならないことから、Tags2BooksIndex カラム・ファミリーは、タグ名を行の ID として保管し、その行に、該当するタグが付けられたすべての書籍をカラムとして保管して、インデックスを作成します。上記の例に示されているように、タイムスタンプはカラムのキーとして追加されていますが、その目的は一意のカラム ID を提供するためです。検索機能の実装は単に、タグ名を基準に行を検索し、該当する rowID 内に保管されたすべてのカラムを読み取ってマッチしたものを検出するという方法でインデックスを読み取っているに過ぎません。

表 2 で、アプリケーションに必要な各クエリーを上記の Cassandra インデックスを使用して実装する方法を説明します。

表 2. クエリーの実装の比較
クエリーの説明 SQL としてのクエリー Cassandra での実装
ランクでソートした書籍リストを作成する

クエリー
Select * from Books order by rank」を実行した後、それぞれの結果で「Select tag from Tags where bookid=?」および「Select comment from Comments where bookid=?」を実行する。
RankIndex カラム・ファミリーでスライス・クエリーを実行して書籍の順序付きリストを取得し、それぞれの書籍について、Books カラム・ファミリーでスライス・クエリーを実行してその書籍の詳細を読み取る。
タグを指定して、そのタグを持つ書籍の著者を検出するSelect distinct author from Tags, Books where Tags.bookid=Books.bookid and tag=?スライス・クエリーを使用して、Tags2Authors から指定のタグに対応するすべてのカラムを読み取ります。
タグを指定して、そのタグを持つ書籍のリストを作成するSelect bookid from Tags where tag=? スライス・クエリーを使用して、Tags2BooksIndex から指定のタグに対応するすべてのカラムを読み取ります。
書籍を指定し、その書籍に対するコメントをコメントが作成された時刻でソートしてリストにするSelect text, time, user from Comments where bookid=? Order by timeBooks カラム・ファミリーで、指定の書籍に対応する行に対してスライス・クエリーを実行します。スライス・クエリーの結果は、カラム名として使用されているタイムスタンプによってソートされます。

上記の設計は、書評サイトに必要なクエリーを効果的にサポートできますが、そのサポートは設計対象となっているクエリーだけに限られ、臨機応変なクエリーをサポートすることはできません。例えば、以下のクエリーを行うには、新たにインデックスを作成する必要があります。

  • Select * from Books where price > 50;
  • Select * from Books where author="Asimov"

適切なインデックスを作成するか、データをウォーク・スルーするコードを作成することによって、上記のクエリーや他のクエリーをサポートするように設計を変更することはできます。けれども、新しいクエリーをサポートするためにカスタム・コードを作成しなければならないという点は、スキーマを変更することなく新規クエリーを追加できるリレーショナル・モデルと比較すると、明らかな制約です。

リリース 0.8 以降、Cassandra ではセカンダリー・インデックスをサポートするようになっています。セカンダリー・インデックスでは、ユーザーが特定のプロパティーを基準とした検索を指定すると、Cassandra がそのプロパティーを基準に検索するためのインデックスを自動的に作成します。ただし、このモデルでは柔軟性が比較的制限されます。例えば、セカンダリー・インデックスは範囲クエリーをサポートしません。また、結果のソート状態を保証することもありません。


Java 環境から Cassandra を使用する

Cassandra には、さまざまな言語で多くのクライアントが作成されています。そのうち、この記事では最も広く使用されている Cassandra の Java クライアントとして、Hector クライアント (「参考文献」を参照) に焦点を絞ります。ユーザーは、Hector の JAR をアプリケーションのクラス・パスに追加することにより、アプリケーションに Hector クライアントを追加することができます。リスト 4 に、サンプル Hector クライアントを記載します。

まず、Cassandra クラスターに接続して、Cassandra の「Getting Started」ページ (「参考文献」を参照) に記載されている説明に従って Cassandra ノードをセットアップします。構成が変更されていない限り、Cassandra ノードは通常、ポート 9160 で実行されます。次に、キー・スペースを定義します。キー・スペースは、クライアントから定義するか、conf/cassandra.yaml 構成ファイルを使用して定義します。

リスト 4. Cassandra の Hector クライアントのサンプル・コード
Cluster cluster = HFactory.createCluster('TestCluster', 
        new CassandraHostConfigurator("localhost:9160"));

//define a keyspace
Keyspace keyspace = HFactory.createKeyspace("BooksRating", cluster);

//Now let's add a new column. 
String rowID = "Foundation"; 
String columnFamily = "Books";

Mutator<String>
 mutator = HFactory.createMutator(keyspace, user);
mutator.insert(rowID, columnFamily, 
        HFactory.createStringColumn("author", "Asimov"));

//Now let's read the column back 
ColumnQuery<String, String, String>
        columnQuery = HFactory.createStringColumnQuery(keyspace);
columnQuery.setColumnFamily(columnFamily).setKey(”wso2”).setName("address");
QueryResult<HColumn<String, String>
 result = columnQuery.execute();
System.out.println("received "+ result.get().getName() + "= " 
        + result.get().getValue() + " ts = "+ result.get().getClock());

書評サイトの完全なコードは、「ダウンロード」にあります。このコードには、スライス・クエリーとその他の複合操作のサンプルも含まれています。


Cassandra のアーキテクチャー

ここまでで Cassandra のデータ・モデルについて見てきたので、次は分散システムの観点から Cassandra の利点と欠点を理解するために、そのアーキテクチャーに再び目を向けます。

図 3 に、Cassandra クラスターのアーキテクチャーを示します。最初に注目する点は、Cassandra は分散システムであることです。複数のノードからなる Cassandra は、これらのノードにデータを分配します (データベースの用語で言うと、シャーディングします)。

図 3. Cassandra クラスター
各ノードがループ状に接続されている様子を示す、Cassandra クラスターの図

Cassandra は、コンシステント・ハッシュ法を使用してデータ項目をノードに割り当てます。簡単に言えば、Cassandra はハッシュ・アルゴリズムを使用して、Cassandra に保管される各データ項目のキー (例えば、カラム名や行 ID など) のハッシュを計算します。ハッシュの範囲、つまり考えられるすべてのハッシュ値 (キー・スペースとも呼ばれます) が、Cassandra クラスター内のノードに分配されます。続いて Cassandra が各データ項目をノードに割り当てると、そのノードがデータ項目の保管と管理を担当します。Cassandra アーキテクチャーの詳細については、論文「Cassandra - A Decentralized Structured Storage System」(「参考文献」を参照) で詳しく説明しています。

以上の仕組みにより、アーキテクチャーには以下の特性が備わります。

  • Cassandra はノードにデータを分配しますが、その詳細はユーザーには見えないように行われます。どのノードもあらゆる要求 (読み取り、書き込み、または削除) を受け入れることができ、そのノードに該当データが保管されていないとしても、要求を適切なノードにルーティングすることができます。
  • ユーザーが必要なレプリカの数を定義すると、Cassandra はレプリカの作成および管理を行いますが、その詳細はユーザーには見えないように行われます。
  • 調整可能な一貫性: データを保管して読み取る際に、ユーザーはそれぞれの操作ごとに、期待する一貫性レベルを選択することができます。例えば、書き込みまたは読み取り時に使用される一貫性レベルが「quorum」だとすると、データの書き込み、読み取りは、クラスター内の過半数のノードから行われます。このように一貫性レベルを調整できることから、ユーザーは状況に最も適した一貫性レベルを選択することができます。
  • Cassandra では非常に高速な書き込みが可能です。実際、書き込みの速度は読み取りの速度を上回っており、ノードあたり約 80-360MB/秒でデータを転送することができます。この高速な処理を実現するために使用されているのは、以下の 2 つの手法です。
    • Cassandra はデータの大半を、そのデータを担当するノードでメモリーに保持します。メモリー内で更新が行われると、その更新を遅延方式で永続ストレージ (ファイルシステム) に書き込みます。データ損失を防ぐために、Cassandra はすべてのトランザクションをディスク上のコミット・ログに書き込みます。ディスク上でデータ項目を更新する場合とは異なり、コミット・ログへの書き込みは追記のみであるため、ディスクに書き込む間に回転待ちが発生することはありません。ディスク・ドライブのパフォーマンス特性に関する詳細は、「参考文献」を参照してください。
    • 書き込みに完全な一貫性が要求されない限り、Cassandra はデータの矛盾を解決することなく、十分な数のノードにデータを書き込みます。この場合、Cassandra は最初の読み込み時にのみ、データの矛盾を解決します。このプロセスは、「読み込みの修復」と呼ばれます。

最終的なアーキテクチャーは、極めてスケーラブルです。テラバイトからペタバイト規模のデータを処理可能な数千のノードからなる Cassandra クラスターを構築することもできます。ただし、分散システムにはトレードオフがあり、何の犠牲もなくスケーリングすることはほぼ不可能です。前述のとおり、ユーザーがリレーショナル・データベースから Cassandra に移行するときには、さまざまな驚きに直面することが考えられます。次のセクションでは、そのような意外な事実のいくつかについて説明します。


Cassandra の意外な事実

リレーショナル・データベースから Cassandra に移行するときには、以下の違いに注意してください。

トランザクションも JOIN もサポートされません

Cassandra が ACID トランザクションをサポートしていないことは、よく知られています。バッチ処理は使用できますが、バッチ処理の中で行われるサブ処理がアトミックに行われる保証はありません。これについては、「処理に失敗すると変更がそのまま残る場合があります」で詳しく説明します。

さらに、Cassandra は JOIN もサポートしていません。ユーザーが 2 つのカラム・ファミリーを結合しなければならない場合、プログラムによってデータを取得して結合する必要があります。大規模なデータ・セットとなると、それにはコストも時間もかかります。Cassandra はこの制約を回避するために、前の例で説明したように、同じ行にできるだけ多くのデータを保管するという方法を採っています。

外部キーはサポートされず、キーは不変です

Cassandra は外部キーをサポートしていないため、Cassandra がユーザーに代わってデータ整合性を管理することは不可能です。したがって、アプリケーションがデータ整合性に対処する必要があります。また、ユーザーがキーに変更を加えることもできません。キーに変更を加えなければならない状況では、サロゲート・キーを使用することが推奨されます (つまり、キーの代わりに生成されるキーをプロパティーとして管理します)。

キーは一意でなければなりません

それぞれのキー (例えば、行キーやカラム・キーなど) は、その適用範囲の中で一意でなければなりません。同じキーが 2 回使用されていると、データが上書きされてしまいます。

この問題には、2 つのソリューションがあります。まず、1 つ目のソリューションとしては、複合キーを使用することできます。つまり、複数のフィールドを結合してキーを作成します。行キーでは、このソリューションがよく使われています。同じキーが 2 つ作られる危険がある場合には、2 つ目のソリューションとして、キーの末尾に乱数値あるいはタイムスタンプを追加します。インデックスが値をカラム名として保管する場合、同じキーが作られることはよくあります。例えば、書評アプリケーションでは、ランクをカラム名として使用しました。2 つのエントリーのランクが同じ場合に、この 2 つのカラム名が同じにならないようにするためには、タイムスタンプをランクの後に接尾辞として追加します。

処理に失敗すると変更がそのまま残る場合があります

前に説明したように、Cassandra はアトミックな処理をサポートしていません。代わりに、Cassandra はべき等な処理をサポートしています。べき等な処理では、処理が何度行われるかに関わらず、システムが同じ状態に維持されます。Cassandra での処理はすべてべき等です。処理に失敗した場合は、問題なくその処理を再試行することができます。これは、一時的な障害から回復するためのメカニズムです。

Cassandra はバッチ処理もサポートしていますが、バッチ処理のアトミック性は保証されません。しかし処理はべき等であることから、クライアントはバッチ処理を構成するすべての処理が成功するまで、再試行を繰り返すことができます。

べき等処理は、アトミックな処理と同じではありません。処理が成功した場合、すべてが上手く行っていれば、その結果はアトミックな処理とまったく同じになります。処理に失敗した場合、クライアントは再試行することが可能です。再試行が成功すれば、何も問題ありません。ただし、再試行しても処理に失敗した場合には、アトミックな処理とは異なり、副次的な影響が残る可能性があります。Cassandra では、残念ながら、この複雑な問題にはプログラマーが自ら対処しなければなりません。

検索が複雑です

検索機能は Cassandra アーキテクチャーのコアに組み込まれていません。検索メカニズムは、前に説明したソート状態を利用した層として追加されます。Cassandra は、システムが自動的に作成するセカンダリー・インデックスをサポートしていますが、そのサポート機能は限られています。セカンダリー・インデックスが機能しない場合には、ユーザーがデータ・モデルについて学習し、ソート状態とスライスを利用してインデックスを作成しなければなりません。

検索メソッドを作成する際には、以下に挙げる 3 つの複雑な領域が関係してきます。

  1. カスタム検索メソッドを作成するには、プログラマーがインデックス化とストレージに関する詳細をある程度理解していなければなりません。したがって、Cassandra にはリレーショナル・モデルの場合よりも高度なスキルを持つ開発者が必要です。
  2. カスタム・インデックスはソートされた順序に大きく依存しますが、ソート状態は複雑です。ソート状態には、2 つのタイプがあります。1 つは、カラムを常に名前でソートするタイプです。もう 1 つは、OrderPreservingPartitioner (「参考文献」を参照) が使用されている場合に限り機能する、行のソート状態です。
  3. リレーショナル・モデルとは異なり、新しいクエリーを追加するには、多くの場合、新しいインデックスとコードの変更が必要になります。そのため、データを保管する前に、開発者がクエリーを分析しなければなりません。

スーパー・カラムと OrderPreservingPartitioner は推奨されません

マルチレベルのデータをモデル化する際には、Cassandra のスーパー・カラムが役に立つことがあります。その場合、階層にもう 1 つのレベルが追加されます。ただし、スーパー・カラムを使ってモデル化できるものは、カラムでもサポートすることが可能です。すなわち、スーパー・カラムならではの追加機能はありません。また、スーパー・カラムはセカンダリー・インデックスをサポートしていません。これらの理由から、Cassandra の開発者たちはスーパー・カラムの使用を推奨していません。サポートを中止する日程は確定されていないものの、今後のリリースではスーパー・カラムのサポートが中止されることになるはずです。

Cassandra では、パーティショナーが、Cassandra ノードにデータを分配 (シャーディング) する方法を決定します。パーティショナーにはさまざまな実装があり、OrderPreservingPartitioner が使用されている場合、rowID はソートされた順序で保管されるため、Cassandra は rowID でもスライス (検索) を行うことができます。けれども、このパーティショナーはノードに均一にデータを配分しないので、大規模なデータ・セットでは、ノードの一部に大きな負荷がかかり、他のノードの負荷は軽くなる可能性があります。したがって、Cassandra の開発者たちは OrderPreservingPartitioner の使用も推奨していません。

障害からの回復は手作業で行います

Cassandra クラスター内のノードで障害が発生した場合、レプリカがあれば、クラスターは引き続き機能します。完全に回復させるための処理 (つまり、データを再分配して欠落したレプリカを補うこと) は、ノード・ツールと呼ばれるコマンドライン・ツール (「参考文献」) を使用して手作業で行います。また、手作業での処理が行われている間、システムは使用不能になります。

削除処理は記憶されます

Cassandra は、ノードがダウン状態 (または切断された状態) になった後で再び使用可能になった場合でも、問題なく動作を続けるように設計されています。そのことから、データの削除が複雑になっています。例えば、ノードがダウンしたとします。ノードがダウンしている間、データ項目はレプリカ内で削除されます。使用不可能だったノードが使用可能になると、Cassandra がデータ項目の削除を記憶していなければ、削除されたデータ項目は同期プロセスで再び取り込まれることになります。

したがって、Cassandra はデータ項目が削除されたことを記憶していなければなりません。Cassandra のリリース 0.8 では、データが削除されたとしても、すべてのデータを記憶するようになっていました。そのため、データの更新を駆使する操作では、ディスク使用量が増加し続けます。Cassandra が、削除されたすべてのデータを記憶する必要はありません。データ項目が削除されたことだけを記憶するだけで十分です。リリース 0.8 より後の Cassandra のリリースでは、そのように修正されました。


まとめ

この記事では、Cassandra を検討するときに容易には気付かない詳細について深く掘り下げました。具体的には、Cassandra データ・モデルをリレーショナル・データ・モデルと比較して説明し、Cassandra での典型的なスキーマ設計の例を紹介しました。注目すべき重要な点は、データを多数のテーブルに分割するリレーショナル・モデルとは異なり、Cassandra はデータを取得する際にデータを結合しなくても済むように、可能な限り多くのデータを同じ行に保持しようとすることです。

こ記事では、Cassandra ベースの手法に伴ういくつかの制約についても説明しました。けれども、これらの制約は大部分の NoSQL ソリューションに共通していることであり、ほとんどの場合は、高度なスケーラビリティーを可能にするための意識的な設計上のトレードオフです。


ダウンロード

内容ファイル名サイズ
Book rating sample codeCassandraSample.zip42KB

参考文献

学ぶために

製品や技術を入手するために

  • Cassandra について、このプロジェクトの Web サイトで調べてください。
  • Hector クライアントについて、このプロジェクトの Web サイトで調べてください。
  • cassandra.apache.org から Cassandra をダウンロードしてください。ここでは、Cassandra の使用方法についても説明しています。
  • ご自分に最適な方法で IBM 製品を評価してください。評価の方法としては、製品の試用版をダウンロードすることも、オンラインで製品を試してみることも、クラウド環境で製品を使用することもできます。また、SOA Sandbox では、数時間でサービス指向アーキテクチャーの実装方法を効率的に学ぶことができます。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Open source, Information Management
ArticleID=828035
ArticleTitle=Apache Cassandra データベースについての検討
publish-date=08022012