分散ストレージの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に例が載っています。
1 2 3 4 5 6 7 8 9 |
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が値でその他にタイムスタンプも入っています。
1 2 3 4 5 |
{ "name": "site", "value": "rest-term", "timestamp": 1271591729517000 } |
インストール
CassandraはJavaで書かれています。(JDK 1.6以降が必要)
* 確認
1 2 3 4 |
$ 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
ログとデータベースを保存するディレクトリを作成し、パーミッションを変更。
1 2 3 4 |
$ 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 |
起動、接続確認
フォアグラウンドで起動
1 |
$ bin/cassandra -f |
データ操作
コマンドラインクライアント cassandra-cli を利用
(‘Test Cluster’で接続、デフォルトポートは9160)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
$ 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. |
データの登録/取得/削除の操作が直感的でわかりやすいですね。
1 |
get Keyspace1.Standard1['rt'] |
で複数のカラムのvalueが取得できているところも単純なKVSとの違いです。
Cassandraの設定ファイルはconf/storage-conf.xml で、ここでKeyspaceやColumnFamily、ポート番号などを指定します。MySQLだとコマンドでtableを追加したりできますが、Cassandraの場合は起動前にこのファイルで追加することになります。
Perlクライアントの利用
ここではCPANの Net::Cassandra::Easy モジュールを使います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
#!/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 $@; |
* 結果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
$VAR1 = { 'rt' => { 'Standard1' => { 'name' => 'wellflat', 'age' => '25' } } }; $VAR1 = { 'rt' => { 'Standard1' => { 'site' => 'rest-term', 'name' => 'wellflat', 'age' => '25' } } }; |
なかなか良いんではないでしょうか。Thriftで通信できるので他の言語からでも簡単に使えそうです。引き続き調査を進めたいと思います。