CassandraとHectorを試してみる。(3)

Cassandra(カサンドラ)とHector(ヘクタ)をScalaで試してみたいが、Cassandraの学習どまりで、なかなかHectorまで辿りつけない。

(1) Cassandraの復習

Cassandraの最新は、2013/09/25時点で apache-cassandra-2.0.1-bin.tar.gz になっていた。"demo" というkeyspaceに"users"というcolumn familyを作成してみた。このデータにHectorからアクセスできるようにしてみたい。


[default@unknown] create keyspace demo;
f8ddf6cc-99e5-3736-b5c3-1b98d765c3d0
[default@unknown] use demo;
Authenticated to keyspace: demo

[default@demo] create column family users with comparator=UTF8Type and
default_validation_class=UTF8Type and key_validation_class=UTF8Type
da16021c-1c9d-388d-a9ff-e9dacfa47d95

[default@demo] set users['ID00001']['uid'] = 'ID00001'; // ユニークIDはRow名=uidで重複して持たせた
Value inserted.
Elapsed time: 209 msec(s).
[default@demo] set users['ID00001']['name'] = 'tacohachi';
Value inserted.
Elapsed time: 5.5 msec(s).
[default@demo] set users['ID00001']['age'] = '39'; // 数値もとりあえず文字列で代入
Value inserted.
Elapsed time: 9.46 msec(s).

[default@demo] get users['ID00001'];
=> (name=age, value=39, timestamp=1380168529216000)
=> (name=name, value=tacohachi, timestamp=1380168521788000)
=> (name=uid, value=ID00001, timestamp=1380168501150000)
Returned 3 results.
Elapsed time: 95 msec(s).

[default@demo] list users; // listというコマンドが便利
Using default limit of 100
Using default cell limit of 100

                                    • -

RowKey: ID00001
=> (name=age, value=39, timestamp=1380168529216000)
=> (name=name, value=tacohachi, timestamp=1380168521788000)
=> (name=uid, value=ID00001, timestamp=1380168501150000)

1 Row Returned.
Elapsed time: 271 msec(s).
[default@demo]

ちなみにkeyspace,column family,key(=row),super column,valueの関係は、以下の感じになる。実際にはuse demo;して利用するので「demo.」の部分は不要だ。key(=row)の字面を何にすればよいかは判断できていないが、RDBでのunique keyに相当する値を用いるべきだろう。例ではuidをunique keyとしているので、'ID00001'が重複したように見えてしまう。

set demo.users['ID00001']['uid'] = 'ID00001'
set demo.users['ID00001']['name'] = 'tacohachi'
set demo.users['ID00001']['age'] = '39'
│ │ │ │ └ value
│ │ │ └ column
│ │ └ key(=row)
│ └ column family
└ keyspace

set demo.users['ID00001']['home' ]['phone'] = '090-xxxx-xxxx'
set demo.users['ID00001']['home' ]['zip-code'] = '210-xxxx'
set demo.users['ID00001']['company']['phone'] = '03-xxxx-xxxx'
│ │ │ │ │ └ value
│ │ │ │ └ column
│ │ │ └ super column
│ │ └ key(=row)
│ └ column family
└ keyspace

実際にcliのコマンドとして利用するときは、

[default@unknown] use demo;
[default@demo] set users['ID00001']['uid'] = 'ID00001';
の感じだ。またsuper columnを使うには、create column familyでcolumn_type=Superとsubcomparator=UTF8Typeを指定しておく必要がある。階層を合わせるためにダミーで[00](16進で0)を指定してみたが、['base']のような字面でも良いかもしれない。

[default@unknown] use demo;
[default@demo] create column family persons
with column_type=Super // これが必要
and comparator=UTF8Type
and subcomparator=UTF8Type // これも必要
and default_validation_class=UTF8Type
and key_validation_class=UTF8Type;
[default@demo] set persons['ID00001'][00]['uid']= 'ID00001';
[default@demo] set persons['ID00001'][00]['name'] = 'tacohachi';
[default@demo] set persons['ID00001']['home']['phone'] = '090-xxxx-xxxx';
[default@demo] set persons['ID00001']['home']['zip-code'] = '210-xxxx';
[default@demo] set persons['ID00001']['company']['phone'] = '03-xxxx-xxxx';
[default@demo] list persons;
Using default limit of 100
Using default cell limit of 100

                                    • -

RowKey: ID00001
=> (super_column=00,
(name=name, value=tacohachi, timestamp=1380353550907000)
(name=uid, value=ID00001, timestamp=1380353540368000))
=> (super_column=company,
(name=phone, value=03-xxxx-xxxx, timestamp=1380353583390000))
=> (super_column=home,
(name=phone, value=090-xxxx-xxxx, timestamp=1380353569578000)
(name=zip-code, value=210-xxxx, timestamp=1380353576766000))

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

(2) Cassandraのストレージ

Cassandraのデータは標準では、var/lib/cassandra/data にdbファイルとして保持される。Linux風のディレクトリだ。メモリ上にキャッシュされているデータは、dbファイルと同期していない可能性ある。明示的に flush する必要がある。またスナップショトとして保持することも可能だ。スナップショトを取ると強制的にflushされる。スナップショトしたdbファイルはバックアップとして利用できる。これらの実行はcassandraで提供されるツールnodetoolを利用する。"demo"のkeyspaceに対して試してみる。


> nodetool flush demo // flushして保存する。
Starting NodeTool
> nodetool snapshot demo // スナップショトとしてコピーを生成する。
Starting NodeTool
Requested creating snapshot for: demo
Snapshot directory: 1380169417960

> nodetool clearsnapshot // ちなみにclearsnapshotでスナップショトは削除される
Starting NodeTool
Requested clearing snapshot for: all keyspaces

var/lib/cassandra/dataを覗いてみる。demoの配下には"users"というcolumn familyが作られている。column familyはディレクトリで表現されるのだ。その配下に、先ほど作成したスナップショトもある。

var
├─lib
│ └─cassandra
│ ├─commitlog
│ ├─data
│ │ ├─demo
│ │ │ └─users
│ │ │ │ demo-users-jb-1-CompressionInfo.db
│ │ │ │ demo-users-jb-1-Data.db
│ │ │ │ demo-users-jb-1-Filter.db
│ │ │ │ demo-users-jb-1-Index.db
│ │ │ │ demo-users-jb-1-Statistics.db
│ │ │ │ demo-users-jb-1-Summary.db
│ │ │ │ demo-users-jb-1-TOC.txt
│ │ │ │
│ │ │ └─snapshots
│ │ │ └─1380169417960
│ │ │ demo-users-jb-1-CompressionInfo.db
│ │ │ demo-users-jb-1-Data.db
│ │ │ demo-users-jb-1-Filter.db
│ │ │ demo-users-jb-1-Index.db
│ │ │ demo-users-jb-1-Statistics.db
│ │ │ demo-users-jb-1-Summary.db
│ │ │ demo-users-jb-1-TOC.txt
│ │ │
│ │ ├─system
│ │ │ ├─batchlog
│ │ │ ├─compactions_in_progress
│ │ │ ├─hints
│ │ │ ├─IndexInfo
│ │ │ ├─local
│ │ │ ├─NodeIdInfo
│ │ │ ├─paxos
│ │ │ ├─peers
│ │ │ ├─peer_events
│ │ │ ├─range_xfers
│ │ │ ├─schema_columnfamilies
│ │ │ ├─schema_columns
│ │ │ ├─schema_keyspaces
│ │ │ └─schema_triggers
│ │ └─system_traces
│ │ ├─events
│ │ └─sessions
│ └─saved_caches
└─log
└─cassandra
system.log

(3) Cassandraの設定ファイルの変更

cassandraを使用して数日経つと、cassandraサーバーを起動した際に以下のエラーが出るようになった。古いdbファイルを削除できないらしい。


ERROR 11:07:05,889 Unable to delete \var\lib\cassandra\data\system\local\system-local-jb-66-Data.db (it will be removed on server restart; we'll also retry after GC)
ERROR 11:07:05,889 Unable to delete \var\lib\cassandra\data\system\local\system-local-jb-69-Data.db (it will be removed on server restart; we'll also retry after GC)
\var\lib\cassandra\dataあたりにアクセス権限(削除権)がないためなのかと、フォルダの権限をフルコントロールに変更したりと四苦八苦したが、そうではなく、Cassandraの設定ファイルを変更しないといけないことに気付いた。Linux環境での設定になっているので、Windows環境用に変更してやらなければならなかった。/var/lib/... のところが C:\var\lib\... になるように変更すればよかった。該当するファイルは、インストール先の cassandra\conf にある cassandra.yamllog4j-server.properties の2つのファイルだ。cassandra.yam では、以下のように3箇所を変更した。パスの記述をWindowsの表現に変更した。

data_file_directories:
- C:\var\lib\cassandra\data
commitlog_directory: C:\var\lib\cassandra\commitlog
saved_caches_directory: C:\var\lib\cassandra\saved_caches
同様に log4j-server.properties も以下のように変更した。

log4j.appender.R.File=C:\var\log\cassandra\system.log

(4) Cassandraデータのimport/export

Cassandraのデータのimport/exportは、csv形式ではなく、json形式のファイルで行われる。dbファイルは存在しているのでexportというより、ファイルのフォーマット変換といった感じだ。exportにはいろいろあるのかもしれないが、とりあえず、demo.usersというcolumn familyのディレクトリに移動してsstable2jsonを実行するのがよさそうだ。


> cd C:\var\lib\cassandra\data\demo\users
> sstable2json -f demo-users-20130927.json demo-users-jb-2-Data.db
ERROR 16:48:15,504 Exception encountered during startup
FSWriteError in C:\var\lib\cassandra\commitlog\CommitLog-3-1380354419683.log
at org.apache.cassandra.io.util.FileUtils.deleteWithConfirm(FileUtils.java:115)
at org.apache.cassandra.db.commitlog.CommitLogAllocator.recycleSegment(CommitLogAllocator.java:185)
at org.apache.cassandra.db.commitlog.CommitLog.recover(CommitLog.java:130)
at org.apache.cassandra.service.CassandraDaemon.setup(CassandraDaemon.java:300)
at org.apache.cassandra.service.CassandraDaemon.activate(CassandraDaemon.java:443)
at org.apache.cassandra.service.CassandraDaemon.main(CassandraDaemon.java:486)
Caused by: java.io.IOException: Failed to delete C:\var\lib\cassandra\commitlog\CommitLog-3-1380354419683.log
... 6 more
あれ、残念ながらエラーとなってsstable2jsonが機能してくれない。古いlogのdeleteやrenemeでエラーになっているようだ。保留としておこう。早く、Hectorの方に話を進めたい。(つづく)