Cassandraについて

分散ストレージのCassandraについて調査中。

CassandraはNoSQL(Not only SQL)データベースの一つ。TwitterやDiggがMySQLからCassandraに移行したことでも有名です。公式サイトのOverviewにもあるように特徴は以下のようになっています。

特徴

Proven (証明) Facebook, Twitter, Diggなど多くのサービスでの利用実績
Fault Tolerant (耐障害性) レプリケーションによりサーバがダウンしても自動復帰可能
Decentralized (分散) クラスタにより単一障害点がない
Eventually Consistent (結果整合性) 楽観的なデータ一貫性を提供
Rich Data Model (高度なデータモデル) 単純なKVSを超える機能を提供
Highly Available (高可用性) 一貫性のレベルを設定可能

分散ストレージを扱う上で、Eventually Consistent (結果整合性) は大切な概念なのでしっかり抑えておく必要があります。(参考:EventuallyConsistent – 結果整合性)

また、Cassandraはmemcachedのような従来のKVSよりリッチなデータ構造を提供していて、従来のKVSでは1つのkeyに対して1つのvalueを保持しますが、Cassandraではそのkeyの部分を4次元、もしくは5次元で扱うことができます。READMEに例が載っています。

  set Keyspace1.Standard2['jsmith']['first'] = 'John'
       \            \         \        \          \
        \            \         \_ key   \          \_ value
         \            \                  \_ column
          \_ keyspace  \_ column family

Data stored in Cassandra is associated with a column family (Standard2),
which in turn is associated with a keyspace (Keyspace1). In the example
above, we set the value 'John' in the 'first' column for key 'jsmith'.

Cassanndraの構造を図にしてみると以下のようになります。

RDBに置き換えると、Keyspaceがデータベース、ColumnFamilyがテーブル、keyが主キー、columnがテーブルのカラムにそれぞれ対応しているイメージです。5次元の場合はSuperColumn(columnのリスト)という次元が追加されます。columnはCassandraにおけるデータの最小単位で以下のような構造。nameがcolumnの名前、valueが値でその他にタイムスタンプも入っています。

{
  "name": "site",
  "value": "rest-term",
  "timestamp":  1271591729517000
}

インストール
CassandraはJavaで書かれています。(JDK 1.6以降が必要)

* 確認

$ java -version
java version "1.6.0_17"
Java(TM) SE Runtime Environment (build 1.6.0_17-b04-248-10M3025)
Java HotSpot(TM) 64-Bit Server VM (build 14.3-b01-101, mixed mode)

以下からビルド済みバイナリを入手して任意の場所に置きます。
The Apache Cassandra Project
ログとデータベースを保存するディレクトリを作成し、パーミッションを変更。

$ sudo mkdir -p /var/log/cassandra
$ sudo chown -R `whoami` /var/log/cassandra
$ sudo mkdir -p /var/lib/cassandra
$ sudo chown -R `whoami` /var/lib/cassandra

起動、接続確認
フォアグラウンドで起動

$ bin/cassandra -f

データ操作
コマンドラインクライアント cassandra-cli を利用
(‘Test Cluster’で接続、デフォルトポートは9160)

$  bin/cassandra-cli --host localhost --port 9160
Connected to: "Test Cluster" on localhost/9160
Welcome to cassandra CLI.

cassandra> show cluster name
Test Cluster
cassandra> show keyspaces
Keyspace1
system
cassandra> set Keyspace1.Standard1['rt']['name'] = 'wellflat'
Value inserted.
cassandra> set Keyspace1.Standard1['rt']['age'] = '25'
Value inserted.
cassandra> set Keyspace1.Standard1['rt']['site'] = 'rest-term'
Value inserted.
cassandra> get Keyspace1.Standard1['rt']
=> (column=73697465, value=rest-term, timestamp=1271591793931000)
=> (column=6e616d65, value=wellflat, timestamp=1271591729517000)
=> (column=616765, value=25, timestamp=1271591787205000)
Returned 3 results.
cassandra> del Keyspace1.Standard1['rt']['site']
column removed.
cassandra> get Keyspace1.Standard1['rt']
=> (column=6e616d65, value=wellflat, timestamp=1271591729517000)
=> (column=616765, value=25, timestamp=1271591787205000)
Returned 2 results.

データの登録/取得/削除の操作が直感的でわかりやすいですね。

get Keyspace1.Standard1['rt']

で複数のカラムのvalueが取得できているところも単純なKVSとの違いです。

Cassandraの設定ファイルはconf/storage-conf.xml で、ここでKeyspaceやColumnFamily、ポート番号などを指定します。MySQLだとコマンドでtableを追加したりできますが、Cassandraの場合は起動前にこのファイルで追加することになります。

Perlクライアントの利用
ここではCPANの Net::Cassandra::Easy モジュールを使います。
[perl]
#!/usr/bin/perl

use strict;
use warnings;
use Net::Cassandra::Easy;
use Data::Dumper;

eval {
my $client = Net::Cassandra::Easy->new(
server => ‘localhost’,
port => 9160,
keyspace => ‘Keyspace1’);
$client->connect();
my $key = ‘rt’;
# get
my $result = $client->get([$key],
family => ‘Standard1’,
byname => [qw/name age site/]);
print Dumper($result);
# insert
$client->mutate([$key],
family => ‘Standard1’,
insertions => { ‘site’ => ‘rest-term’ });
# get
$result = $client->get([$key],
family => ‘Standard1’,
byname => [qw/name age site/]);
print Dumper($result);
};
die Dumper($@) if $@;
[/perl]
* 結果

$VAR1 = {
          'rt' => {
                    'Standard1' => {
                                     'name' => 'wellflat',
                                     'age' => '25'
                                   }
                  }
        };
$VAR1 = {
          'rt' => {
                    'Standard1' => {
                                     'site' => 'rest-term',
                                     'name' => 'wellflat',
                                     'age' => '25'
                                   }
                  }
        };

なかなか良いんではないでしょうか。Thriftで通信できるので他の言語からでも簡単に使えそうです。引き続き調査を進めたいと思います。

あわせて読む:

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です