2012年9月17日月曜日

Cassandraってなんじゃ?

今回はCassandraをさわってみました。
Cassandraは、もともとFacebookが開発したKVSで、現在はApache Cassandraとしてオープンソース化されています。

Cassandraには以下の特徴があります。
  • 分散方式はAmazonのDynamoと同様なConsistentHashing方式
  • ストレージ方式はGoogleのBigTagleと同様なコミットログ/メモリテーブル/ディスクテーブル方式
  • CAP定理の3つの要素では、一貫性よりも可用性と分割耐性を重視
  • データモデルは列指向
  • アプリケーションからはThrift APIを使用

KVSのモデルはどれも一長一短で、Cassandraの場合デフォルトでは一貫性保証が弱いとされていますが、一貫性オプションの選択ができ、すべてのサーバーが最新の値に更新されてから読み出しができるようにすることも可能です。

Cassandraのデータの保持構造は以下のような階層で保存されます。
  • Keyspace:RDBのデータベースにあたるもの
  • Column Family:RDBのテーブルにあたるもの
  • Row:RDBのレコードにあたるもの
  • Column:RDBのカラムにあたるもの、name, value, timestampから構成される
この他にもSuper Columnがありますが必須要素ではありません。

それではインストールしてみます。

JDKのインストール

CassandraはJVM上で動作するため、まずはJDKをインストールします。
OpenJDKはCassandra1.1.5ではセグメンテーション違反が起こったため、Sun SDKを使用しました。
また、RPM版はインストールエラーがでたのでTAR版でインストールします。

ダウンロードはOracleのダウンロードページから行いますが、linuxシェル上では、Acceptボタンの操作ができないので、ブラウザでAcceptをチェックしダウンロードを開始した直後に一時停止した後、通信コンソール上のURLをコピーしてターミナル上でwgetしました。
今回は記事の都合上すべてrootで作業しましたが、適宜専用ユーザーで行なってください。

# yum install -y wget
# cd /usr/local/src
# wget http://download.oracle.com/otn-pub/java/jdk/7u7-b10/jdk-7u7-linux-i586.tar.gz?AuthParam=1347660783_4d176083902db0eb4404c8b5fcd542d0
# mv jdk-7u7-linux-i586.tar.gz\?AuthParam\=1347660783_4d176083902db0eb4404c8b5fcd542d0 jdk-7u7-linux-i586.tar.gz
# tar xzvf jdk-7u7-linux-i586.tar.gz 
# mkdir -p /usr/java/
# mv jdk1.7.0_07  /usr/java/jdk1.7.0_07

JAVA用の環境変数を定義します。
# vim /etc/profile
-----
JAVA_HOME=/usr/java/jdk1.7.0_07
PATH=$PATH:$JAVA_HOME/bin
CLASSPATH=.:$JAVA_HOME/jre/lib:$JAVA_HOME/lib/tools.jar
export JAVA_HOME CLASSPATH
------
# source /etc/profile
# cd /usr/local/src

簡単なコードを書き、コンパイルして動作確認します。
# vim HellowWorld.java
public class HelloWorld
{
    public static void main(String args[])
    {   
        System.out.println("Hello World!");
    }   
}
# javac HelloWorld.java
# java HelloWorld
Hello World!
これでJDKがインストールされました。


Cassandraのインストール

それではCassandraをインストールします。 CassandraはApache Cassandraのダウンロードページのリンクからwgetします。

# cd /usr/local/src/
# wget http://ftp.riken.jp/net/apache/cassandra/1.1.5/apache-cassandra-1.1.5-bin.tar.gz
# tar xzvf apache-cassandra-1.1.5-bin.tar.gz 
# mv apache-cassandra-1.1.5 /usr/local/
# cd /usr/local/
# ln -s apache-cassandra-1.1.5/ cassandra


Cassandraの起動

 今回はフォアグラウンドで起動してみます。 正常に動作すれば起動ログが出力されます。
# cd /usr/local/cassandra
# ./bin/cassandra -f


Cassandraのクライアントコンソールの起動

別のシェルからクライアントを起動します。
# ./bin/cassandra-cli --host localhost

[default@unknown]

[default@unknown]と表示されれば、Cassandraのコンソールに入れたことになります。 ここで、keyspaceの操作や、データのCRUDを行います。

以降、コンソール内での操作になります。
本来であればRDBとは概念が異なるため、するべきではないかも知れませんが、 とっつきやすくするために、対応するMySQLの操作を参考に記載します。


Keyspaceの作成

Hogebookという名前でkeyspaceを登録してみます。
MySQLでいうところのCREATE DATABASEにあたります。
[default@unknown] create keyspace Hogebook;
983a327c-bab3-37c3-958d-d856986f4f2b
Waiting for schema agreement...
... schemas agree across the cluster


Keyspaceの切り替え

使用したいKeyspaceをHogebookに切り替えます。
MySQLのUSE databaseにあたります。
[default@unknown] use Hogebook;
Authenticated to keyspace: Hogebook


Column Familyの作成

Column Familyを作成します。
UserというColumn Familyを作成してみます。
MySQLではCREATE TABLEに相当します。
作成する際にColumnのソート順を決めるComparatorを指定します。 ここではUTF8Typeを指定しますが、他にBytesType、AsciiType、UTF8Type、LexicalUUIDType、TimeUUIDType、LongTypeがあります。
[default@Hogebook] create column family User with comparator = UTF8Type;      
4ce3585d-9f66-36cb-8433-76bd1acf7827
Waiting for schema agreement...
... schemas agree across the cluster


Column Familyの設定の変更

emailとgenderというカラムのデータ型を定義します。
Column Familyの設定を変更します。
MySQLでのALTER TABLEに該当します。
[default@Hogebook] update column family User with column_metadata =  
... [
... {column_name: email, validation_class: UTF8Type},
... {column_name: gender, validation_class: UTF8Type} 
... ]
... ;
86ed51a4-e3b1-36b7-9cb9-93e4bf1a5fde
Waiting for schema agreement...
... schemas agree across the cluster

※追記:org.apache.cassandra.db.marshal.MarshalException: cannot parse ...とエラーになる場合は、
create column family User with comparator = UTF8Type and default_validation_class=UTF8Type and key_validation_class=UTF8Type;
のようにするといいようです。

データの投入

それではデータを保存してみます。
MySQLでのINSERTやUPDATEにあたります。
この例では'memorycraft'というキーでemail,genderというcolumnを保存します。
このようにCassandraでは、連想配列をつくるようにコマンドをセットするため、INSERTとUPDATEは同じ処理になります。
[default@Hogebook] set User['memorycraft']['email'] = 'memorycraft@gmail.com';
Value inserted.
Elapsed time: 47 msec(s).
[default@Hogebook] set User['memorycraft']['gender'] = 'male';
Value inserted.
Elapsed time: 2 msec(s).
[default@Hogebook] set User['memorycraftgirl']['gender'] = 'female';
Value inserted.
Elapsed time: 3 msec(s).
[default@Hogebook] set User['memorycraftgirl']['email'] = 'memorycraft+girl@gmail.com';
Value inserted.
Elapsed time: 17 msec(s).


データの取得

ここで、投入したデータを取得してみます。
MySQLでいうPKを条件にしたSELECTにあたります。
 
[default@Hogebook] get User['memorycraft'];                   
=> (column=email, value=memorycraft@gmail.com, timestamp=1347799163739000)
=> (column=gender, value=male, timestamp=1347799233322000)
Returned 2 results.
Elapsed time: 2 msec(s).


データの検索

キー以外のカラムの条件でデータを検索したい場合があります。
以下のように行います。
 
[default@Hogebook] get User where gender = 'male';
No indexed columns present in index clause with operator EQ
しかし、エラーになりました。インデックスがないことが原因のようです。
Cassandraではキー以外で検索をかける場合、そのカラムにインデックスを張る必要があります。
そこで、カラムの設定にインデックスを設定します。
[default@Hogebook] update column family User with column_metadata =  
... [
... {column_name: email, validation_class: UTF8Type},
... {column_name: gender, validation_class: UTF8Type, index_type: KEYS} 
... ]
... ;
86ed51a4-e3b1-36b7-9cb9-93e4bf1a5fde
Waiting for schema agreement...
... schemas agree across the cluster

このように、index_type: KEYSとすることで、genderカラムで検索をかけることが可能になります。

[default@Hogebook] get User where gender = 'male';
-------------------
RowKey: memorycraft
=> (column=email, value=memorycraft@gmail.com, timestamp=1347799163739000)
=> (column=gender, value=male, timestamp=1347799233322000)

1 Row Returned.
Elapsed time: 119 msec(s).


データの削除

データを削除してみます。
MySQLでのDELETEに該当します。
[default@Hogebook] del User['memorycraftgirl'];   
row removed.


とりあえず、駆け足で簡単なCRUD処理をさらってみました。
ここまでは、一風変わったデータストアというところまでしかわかりません。次はもう少しCassandraらしい部分を調べてみます。