ラベル cassandra の投稿を表示しています。 すべての投稿を表示
ラベル cassandra の投稿を表示しています。 すべての投稿を表示

2013年3月9日土曜日

Cassandraってなんじゃ?(CQL)

CassandraにはCQLというSQLライクに問い合わせるインターフェースが用意されています。
いままでのスクリプト風の問い合わせよりも多くの人に馴染みやすいと思うので、ちょっと触ってみます。



CQLコンソール


CQLのインターフェースはいままでのcassandra-cliとは別に、binディレクトリにあるcqlshコマンドを使用します。
まずhelpを見てみます。

# /usr/local/cassandra/bin/cqlsh --help
Usage: cqlsh [options] [host [port]]

CQL Shell for Apache Cassandra

Options:
  --version             show program's version number and exit
  -h, --help            show this help message and exit
  -C, --color           Always use color output
  --no-color            Never use color output
  -u USERNAME, --username=USERNAME
                        Authenticate as user.
  -p PASSWORD, --password=PASSWORD
                        Authenticate using password.
  -k KEYSPACE, --keyspace=KEYSPACE
                        Authenticate to the given keyspace.
  -f FILE, --file=FILE  Execute commands from FILE, then exit
  -t TRANSPORT_FACTORY, --transport-factory=TRANSPORT_FACTORY
                        Use the provided Thrift transport factory function.
  --debug               Show additional debugging information
  --cqlversion=CQLVERSION
                        Specify a particular CQL version (default: 3).
                        Examples: "2", "3.0.0-beta1"
  -2, --cql2            Shortcut notation for --cqlversion=2
  -3, --cql3            Shortcut notation for --cqlversion=3

Connects to localhost:9160 by default. These defaults can be changed by
setting $CQLSH_HOST and/or $CQLSH_PORT. When a host (and optional port number)
are given on the command line, they take precedence over any defaults.


-2や-3とオプションを渡すと指定されたバージョンのCQLを使用します。
ここではバージョン2を指定してみます。
# /usr/local/cassandra/bin/cqlsh -2

Connected to Memorycraft Cluster at localhost:9160.
[cqlsh 2.3.0 | Cassandra 0.0.0 | CQL spec 2.0.0 | Thrift protocol 19.35.0]
Use HELP for help.
cqlsh>


このようにSQLクライアント風のコンソールが開始されます。
コンソール上でもう一度helpを打ってみます。
cqlsh:Hogebook> help

Documented shell commands:
===========================
ASSUME   CONSISTENCY  DESC      EXIT  SHOW    TRACING
CAPTURE  COPY         DESCRIBE  HELP  SOURCE

CQL help topics:
================
ALTER                        CREATE_KEYSPACE       SELECT_EXPR    
ALTER_ADD                    CREATE_TABLE          SELECT_LIMIT   
ALTER_ALTER                  CREATE_TABLE_OPTIONS  SELECT_TABLE   
ALTER_DROP                   CREATE_TABLE_TYPES    SELECT_WHERE   
ALTER_WITH                   DELETE                TEXT_OUTPUT    
APPLY                        DELETE_COLUMNS        TIMESTAMP_INPUT
ASCII_OUTPUT                 DELETE_USING          TIMESTAMP_OUTPUT
BEGIN                        DELETE_WHERE          TRUNCATE       
BLOB_INPUT                   DROP                  TYPES          
BOOLEAN_INPUT                DROP_COLUMNFAMILY     UPDATE         
CONSISTENCYLEVEL             DROP_INDEX            UPDATE_COUNTERS
CREATE                       DROP_KEYSPACE         UPDATE_SET     
CREATE_COLUMNFAMILY          DROP_TABLE            UPDATE_USING   
CREATE_COLUMNFAMILY_OPTIONS  INSERT                UPDATE_WHERE   
CREATE_COLUMNFAMILY_TYPES    SELECT                USE            
CREATE_INDEX                 SELECT_COLUMNFAMILY   UUID_INPUT     


さらに各コマンドでhelpを打つと詳細が分かります。
cqlsh:Hogebook> help SELECT

        SELECT [FIRST n] [REVERSED] <selectExpr>
          FROM [<keyspace>.]<table>
            [USING CONSISTENCY <consistencylevel>]
            [WHERE <clause>]
            [ORDER BY <colname> [DESC]]
            [LIMIT m];

        SELECT is used to read one or more records from a CQL table. It returns
        a set of rows matching the selection criteria specified.


        Note that FIRST and REVERSED are only supported in CQL 2, and ORDER BY
        is only supported in CQL 3 and higher.

        For more information, see one of the following:

          HELP SELECT_EXPR
          HELP SELECT_TABLE
          HELP SELECT_WHERE
          HELP SELECT_LIMIT
          HELP CONSISTENCYLEVEL

かなりSQLっぽく使えるようです。
いくつかコマンドを打ってみたいと思います。



DESC TABLE
cqlsh:Hogebook> desc table User;

CREATE TABLE User (
  KEY text PRIMARY KEY,
  email text,
  gender text
) WITH
  comment='' AND
  comparator=text AND
  read_repair_chance=0.100000 AND
  gc_grace_seconds=864000 AND
  default_validation=text AND
  min_compaction_threshold=4 AND
  max_compaction_threshold=32 AND
  replicate_on_write='true' AND
  compaction_strategy_class='SizeTieredCompactionStrategy' AND
  compression_parameters:sstable_compression='SnappyCompressor';

CREATE INDEX User_gender_idx ON User (gender);



SELECT
cqlsh:Hogebook> select * from User;

 KEY             | email                      | gender
-----------------+----------------------------+--------
     memorycraft |      memorycraft@gmail.com |   male
 memorycraftgirl | memorycraft+girl@gmail.com | female



INSERT
cqlsh:Hogebook> insert into User(KEY, email, gender) VALUES('hellocql', 'memorycraft+cql@gmail.com', 'male');
cqlsh:Hogebook>
cqlsh:Hogebook> select * from User;

 KEY             | email                      | gender
-----------------+----------------------------+--------
     memorycraft |      memorycraft@gmail.com |   male
        hellocql |  memorycraft+cql@gmail.com |   male
 memorycraftgirl | memorycraft+girl@gmail.com | female

これなら違和感なくデータの操作や参照ができそうです。



APIアクセス



これらのCQLコマンドはコンソール上でも使用しますが、アプリケーションコードからも発行できます。
以前の記事で使用したphpcasaからも使うことができます。

例えば以下はテーブルの全データを取得して出力するスクリプトになります。


このスクリプトを実行してみます。

$ php ./cql.php 
cassandra\CqlResult Object
(
    [type] => 1
    [rows] => Array
        (
            [0] => cassandra\CqlRow Object
                (
                    [key] => memorycraft
                    [columns] => Array
                        (
                            [0] => cassandra\Column Object
                                (
                                    [name] => KEY
                                    [value] => memorycraft
                                    [timestamp] => -1
                                    [ttl] => 
                                )

                            [1] => cassandra\Column Object
                                (
                                    [name] => email
                                    [value] => memorycraft@gmail.com
                                    [timestamp] => 1589299048
                                    [ttl] => 
                                )

                            [2] => cassandra\Column Object
                                (
                                    [name] => gender
                                    [value] => male
                                    [timestamp] => 1596347048
                                    [ttl] => 
                                )

                        )

                )

            [1] => cassandra\CqlRow Object
                (
                    [key] => hellocql
                    [columns] => Array
                        (
                            [0] => cassandra\Column Object
                                (
                                    [name] => KEY
                                    [value] => hellocql
                                    [timestamp] => -1
                                    [ttl] => 
                                )

                            [1] => cassandra\Column Object
                                (
                                    [name] => email
                                    [value] => memorycraft+cql@gmail.com
                                    [timestamp] => 1562593385
                                    [ttl] => 
                                )

                            [2] => cassandra\Column Object
                                (
                                    [name] => gender
                                    [value] => male
                                    [timestamp] => 1562593384
                                    [ttl] => 
                                )

                        )

                )

            [2] => cassandra\CqlRow Object
                (
                    [key] => memorycraftgirl
                    [columns] => Array
                        (
                            [0] => cassandra\Column Object
                                (
                                    [name] => KEY
                                    [value] => memorycraftgirl
                                    [timestamp] => -1
                                    [ttl] => 
                                )

                            [1] => cassandra\Column Object
                                (
                                    [name] => email
                                    [value] => memorycraft+girl@gmail.com
                                    [timestamp] => 1611666048
                                    [ttl] => 
                                )

                            [2] => cassandra\Column Object
                                (
                                    [name] => gender
                                    [value] => female
                                    [timestamp] => 1602906048
                                    [ttl] => 
                                )

                        )

                )

        )

    [num] => 
    [schema] => cassandra\CqlMetadata Object
        (
            [name_types] => Array
                (
                    [KEY] => AsciiType
                )

            [value_types] => Array
                (
                    [KEY] => UTF8Type
                    [email] => UTF8Type
                    [gender] => UTF8Type
                )

            [default_name_type] => UTF8Type
            [default_value_type] => UTF8Type
        )

)


出力をみると、オブジェクト形式で結果が帰ってくるようです。 これなら普段のSQLと近い感覚でコードがかけるので、移行もしやすいと思います。

以上です。


Cassandraってなんじゃ?(EC2で起動時自動ノード追加とノードの削除)

Cassandraを使っているときに、容量が足らない、負荷が高いなどの理由でノードを増やしたい場合があります。また、負荷が長期的に落ち着き、予算を抑えるためにノードを減らすこともあります。

今回はノードの追加と削除をやってみます。



トークンの算出



ノードを追加するケースはリングの負荷状態によって、大きく分けて
  1. 保存するデータのハッシュやアクセスに偏りが大きく、特定ノードの容量または処理の負荷が高いので負荷集中を分散したい。
  2. リング全体の容量、または処理の負荷が高いので、全体的に数を増やして均一に分散したい。

の2通りかと思います。

1の場合、負荷の高いノードの近くにノードを追加し、ノードの均一化はせずに特定のトークンの範囲にノードを集中させたほうがよさそうです。
2の場合、ノードを追加したら、全ノードが均等にリング上に再配置したほうが全体としての負荷は抑えられそうです。

今回は1の場合を例にとってみます。


まず、トークンを計算するためのツールをつくっておくと便利です。
引数にノードの数を与えるとそれがリング上に均等に配置されるようなトークンのリストを返すツールです。

計算式はPartitionerによってことなり、
#RandomPartitionerの場合
 
def tokens(nodes):
  for x in xrange(nodes):
    print 2 ** 127 / nodes * x
 
#Murmur3Partitionerの場合
 
def tokens(num_nodes):
  for i in range(num_nodes):
    print ((2**64 / num_nodes) * i) - 2**63

のようになります。

今回はMurmur3Partitionerを使用しているので、
以下のようなpythonスクリプトを作ります。


引数にノード数を入れて実行すると、ノードリングに均等に配置された場合のハッシュトークンのリストが出力されます。
$ ./tokens.py 4
-9223372036854775808
-4611686018427387904
0
4611686018427387904
 
$ ./tokens.py 8
-9223372036854775808
-6917529027641081856
-4611686018427387904
-2305843009213693952
0
2305843009213693952
4611686018427387904
6917529027641081856


既存のノードが4つだったとして、トークンが以下のように配置されているとします。
  • A:0
  • B:4611686018427387904
  • C:-9223372036854775808
  • D:-4611686018427387904




この場合Aの両脇にノードを2つ追加しておくとよさそうです。
また、追加ノードのトークンは、
  • B、DもCに比べて負荷が高めであればA-B, A-Cのそれぞれ中間
  • BDの負荷はそれほどでもなければもっとAより

に配置する形になります。
前者であれば、./tokens 8、後者であれば./tokens 12 などでAの位置の両脇のトークンを使用します。
たとえば後者のパターンで追加してみます。

$ ./tokens 12
-9223372036854775808
-7686143364045646507
-6148914691236517206
-4611686018427387905
-3074457345618258604
-1537228672809129303
-2
1537228672809129299
3074457345618258600
4611686018427387901
6148914691236517202
7686143364045646503

-2の部分がAのノード位置です。
その両脇の
  • -1537228672809129303
  • 1537228672809129299

が今回の追加ノードとなります。




以前の記事でちょっと触れましたが、自動でリングに追加されるようなAMIを作ります。



AMIの作成



AMIの作成をします。cassandraをインストールしただけでまだ一度も起動していないインスタンスをベースに作業を行うとスムーズです。

以下の設定ファイルにはIPなどノード固有の情報(赤字の部分)が含まれます。

/usr/local/cassandra/conf/cassandra.yaml
cluster_name: 'クラスタ名'
initial_token: トークン
seed_provider:
- class_name: org.apache.cassandra.locator.SimpleSeedProvider
parameters:
- seeds: "シードIP"

listen_address: 自分のIP
endpoint_snitch: Ec2Snitch
auto_bootstrap: シードならfalse,非シードならtrue

/etc/hosts
127.0.0.1       localhost.localdomain localhost  ip-10-0-1-10

この固有の項目を書き換えるようなシェルスクリプトを書きます。

このスクリプトは後でUserDataから呼び出され、第1引数にトークン、第2引数にauto_bootstrapのブーリアン文字列、第3引数にシードIPが渡されます。

/etc/cloud/cassandra.sh


ここまで設定したらno rebootでAMIを作成します。




ノードの自動追加



このAMIをつかって新規ノードを追加します。
クラスタも何もない状態で、一番最初のノードを立ち上げる場合は以下のようにUserDataを指定します。

#!/bin/sh
/etc/cloud/cassandra.sh "0" false 10.0.1.10


そして、プライベートIPアドレスを10.0.1.10に固定して起動します。



また、2個め以降(ここでは例として6個目)の追加ノードは以下のようにUserDataを指定します。
ここで、第1引数のトークンは冒頭で記した計算ツールで算出しておきます。

#!/bin/sh
/etc/cloud/cassandra.sh "1537228672809129299" true 10.0.1.10,10.0.1.146,10.0.1.176,10.0.1.232,10.0.1.134


特にプライベートIPを指定する必要はありません。



これにより、立ち上がったインスタンスでは、hostsファイルとcassandra.yamlが自動的に設定され、cassandraが起動し、リングに自動的に追加されます。

起動前は、

# /usr/local/cassandra/bin/nodetool ring

Datacenter: ap-northeast
==========
Replicas: 3

Address         Rack        Status State   Load            Owns                Token                                     
                                                                               -4611686018427387905                       
10.0.1.146      1a          Up     Normal  75.43 KB        66.67%              -1537228672809129303                       
10.0.1.10       1a          Up     Normal  61.65 KB        50.00%              0                                         
10.0.1.176      1a          Up     Normal  58.33 KB        58.33%              -9223372036854775808                       
10.0.1.232      1a          Up     Normal  58.45 KB        50.00%              4611686018427387901                       
10.0.1.134      1a          Up     Normal  58.48 KB        75.00%              -4611686018427387905

だったのが、起動後


# /usr/local/cassandra/bin/nodetool ring

Datacenter: ap-northeast
==========
Replicas: 3

Address         Rack        Status State   Load            Owns                Token                                       
                                                                               -4611686018427387905                        
10.0.1.213      1a          Up     Normal  49.55 KB        33.33%              1537228672809129299                         
10.0.1.146      1a          Up     Normal  63.7 KB         66.67%              -1537228672809129303                        
10.0.1.10       1a          Up     Normal  66.59 KB        50.00%              0                                           
10.0.1.176      1a          Up     Normal  63.28 KB        50.00%              -9223372036854775808                        
10.0.1.232      1a          Up     Normal  63.4 KB         33.33%              4611686018427387901                         
10.0.1.134      1a          Up     Normal  63.43 KB        66.67%              -4611686018427387905 

↑のようにノード追加されていることが分かります。



ノードの取り外し



逆に問題があるノードを取り外すときは、そのノードに入って、

# /usr/local/cassandra/bin/nodetool decommission

とすれば外れます。
再び接続させるには、cassandraを追加します。

#/etc/init.d/cassandra stop
#/etc/init.d/cassandra start



接続できない場合


たまに、リングに接続できなかったりした場合は、まず7000(ノード間), 9160(Thrift), 7199(JMX)などのポートを準備できているかセキュリティグループやnsetstatで確認します。netstatでlistenできていない場合は、cassandraを再起動させたりログを調べたりして原因を探していく流れになります。




# netstat -tupln | grep -i listen | grep java
tcp        0      0 0.0.0.0:9160                0.0.0.0:*                   LISTEN      3967/java         
tcp        0      0 0.0.0.0:38250               0.0.0.0:*                   LISTEN      3967/java         
tcp        0      0 0.0.0.0:53170               0.0.0.0:*                   LISTEN      3967/java         
tcp        0      0 10.0.1.213:7000             0.0.0.0:*                   LISTEN      3967/java         
tcp        0      0 0.0.0.0:7199                0.0.0.0:*                   LISTEN      3967/java



これで、ノードを簡単に増やせるようになりました。
以上です。

2013年2月14日木曜日

Cassandraってなんじゃ?(EC2でDataStaxOpsCenterを動かす)

前回の記事で、Cassandraでクラスタリングをした場合、クラスタの情報をみるためのnodetoolの紹介をしました。
ただ、nodetoolやcassandra-cliでは、ノードの構成や配置が複雑だったりデータの分布を直感的に確認することは大変です。

そんな場合、DataStax社が提供しているOpsCenterという管理画面ツールを使うと便利なようです。DataStaxはApache Cassandraのプロジェクトリーダーが起こした会社でcassandraのコミッタの多くが在籍しているそうです。

DataStax OpsCenterに関しては、以下のサイトで紹介されています。
Cassandraクラスタと、DataStax OpsCenterの構築

また、DataStaxの公式サイトに詳細なガイドがあります。
DataStax OpsCenter Documentation

これらのサイトを参考に、OpsCenterをEC2にインストールしてみました。いくつかEC2ならではの部分やはまった点に触れながら手順を紹介しようと思います。


OpsCenterのインストール


前回の記事でAPIアクセスをしたpublicサブネットのappサーバーにインストールします。

# cd /usr/local/src/
# rpm -Uvh http://ftp-srv2.kddilabs.jp/Linux/distributions/fedora/epel/6/x86_64/epel-release-6-8.noarch.rpm
# cat /etc/yum.repos.d/datastax.repo
[datastax]
name= DataStax Repository
baseurl=http://rpm.datastax.com/community
enabled=1
gpgcheck=0
# yum install opscenter-free -y

接続先の設定をします。
opscenterd.confで設定します。
ここで、seed_hostsには、前回設定したseedインスタンスのIPを指定します。
また、ここではuse_sslをfalseにしてHTTPアクセスにしておきます。

# vi /etc/opscenter/opscenterd.conf
---

[jmx]
port = 7199
[webserver]
port = 8888
interface = 0.0.0.0
[cassandra]
seed_hosts = 10.0.1.10

[agents]
ssh_port = 22
use_ssl = false
https_port = 61621
incoming_port = 61620

---


セキュリティグループの設定


OpsCenterでは、以下のサイトに説明があるように接続に複数のポートを使用するようです。
OpsCenter and OpsCenter agent ports

ここでは以下のようにポートを設定します。

app(OpsCenter)インスタンス
  • 22  10.0.0.0/16 →ssh(natから入る想定)
  • 80  0.0.0.0/0  →通常のWEB接続
  • 8888  0.0.0.0/0  →OpsCenterコンソールのUI画面
  • 61620  10.0.0.0/16 →cassandraの各ノードからOpsCenterへ情報を送るポート

cassandraインスタンス
  • 22  10.0.0.0/16 →ssh(natから入る想定)、agentのインストールに必要
  • 7000  10.0.0.0/16 →cassandraノード同士の通信
  • 7199  10.0.0.0/16 →JMXポート
  • 9160  10.0.0.0/16 →Thrift API
  • 61621  10.0.0.0/16 →OpsCenterから各ノードへの情報取得用ポート


赤字が前回から新たに付与する設定です。


ここまでできたらOpsCenterを起動します。
# /etc/init.d/opscenterd start



ブラウザで確認


それでは、ブラウザでOpsCenterでUIを確認してみます。
app(OpsCenter)インスタンスにEIPをつけて、
http://インスタンスのEIP:8888/
を確認してみます。


おお!表示されました。
seedインスタンスのIPしか設定しませんでしたが、ノードが3つあることは理解されているようです。

しかし現時点では、情報が空のようです。
これは、cassandra側にOpsCenterのエージェントツールがないためです。


エージェントのインストール


次にエージェントをインストールします。
適切な設定がしてあると、OpsCenter側からcassandraにインストールすることが出来るようです。

タイトル部分の「Fix」というリンクをクリックします。



すると、以下のようなダイアログが開き、3つのノードのIPが表示されます。



ここで、「Edit Credentials」というリンクをクリックします。
すると、別のダイアログが更に開き、これらのインスタンスに入るためのユーザーとパスワードなどを聞かれます。


ここでは、rootユーザーか、もしくはsudoersに登録されているユーザーとパスワードを指定します。
sshがパスワードログインを許可していない場合は、「Private Key」に鍵ファイルのテキストをペーストします。
また、ここで登録された情報は永続化されることはない旨の説明が表示されています。
入力を終えたら「Done」ボタンをクリックします。

元のダイアログに戻るので、「Install on all nodes」ボタンをクリックします。
すると、SSHのフィンガープリントを確認されるので、「Accept Fingerprint」ボタンをクリックします。



エラー


しばらくすると、エラーダイアログが表示されてしまいました。



そこで、cassandra側を見てみます。
# ls -l /etc/init.d/ | grep ops
なにもインストールされていないようです。

インストールログがあるようなので、確認します。
# tail -1000f /var/log/opscenter-agent/installer.log
2013-02-13 23:12:09 +0900  Started: Wed Feb 13 23:10:26 2013 - 04:15 ago
2013-02-13 23:12:09 +0900  State  : Sleeping, pid: 25343
2013-02-13 23:10:27 +0900  Could not retrieve mirrorlist http://www.atomicorp.com/mirrorlist/atomic/centos-6-x86_64 error was
2013-02-13 23:10:27 +0900  12: Timeout on http://www.atomicorp.com/mirrorlist/atomic/centos-6-x86_64: (28, 'connect() timed out!')
2013-02-13 23:10:27 +0900  Could not retrieve mirrorlist http://mirrorlist.centos.org/?release=6&arch=x86_64&repo=os error was
2013-02-13 23:10:27 +0900  12: Timeout on http://mirrorlist.centos.org/?release=6&arch=x86_64&repo=os: (28, 'connect() timed out!')
2013-02-13 23:10:27 +0900  Could not get metalink https://mirrors.fedoraproject.org/metalink?repo=epel-6&arch=x86_64 error was
2013-02-13 23:10:27 +0900  12: Timeout on https://mirrors.fedoraproject.org/metalink?repo=epel-6&arch=x86_64: (28, 'connect() timed out!')
これは、VPCでEIPをつけずにyumインストールをするときによく見るログです。
OpsCenterはSSHでcassandraノードに入ってyumでエージェントをインストールしようとしているようです。


なので、エージェントをインストールするときだけ、各ノードにEIPを付与してあげます。



では同じ手順でリトライしてみます。
OpsCenterの画面で再度「Install on all nodes」をクリックします。



。。。。やはりエラーになります。


くわしい原因が書かれていないので、OpsCenterのログを見てみます。
# tail -1000f /var/log/opscenter/opscenterd.log
2013-02-13 23:30:41+0900 [Test_Cluster]  INFO: Beginning install of OpsCenter agent to 10.0.1.176
2013-02-13 23:30:42+0900 [Test_Cluster]  INFO: Installing rpm package on 10.0.1.176
2013-02-13 23:31:12+0900 [Test_Cluster]  WARN: HTTP request http://10.0.1.227:61621/cluster/datacenter?node_ip=10.0.1.176 failed: Connection was refused by other side: 111: Connection refused.
2013-02-13 23:31:12+0900 [Test_Cluster]  WARN: Unable to collect datacenter, rack information: Failed query to http://10.0.1.227:61621/cluster/datacenter?node_ip=10.0.1.176 : Connection was refused by other side: 111: Connection refused.
2013-02-13 23:31:12+0900 [Test_Cluster]  WARN: HTTP request http://10.0.1.227:61621/cluster/datacenter?node_ip=10.0.1.10 failed: Connection was refused by other side: 111: Connection refused.
2013-02-13 23:31:12+0900 [Test_Cluster]  WARN: Unable to collect datacenter, rack information: Failed query to http://10.0.1.227:61621/cluster/datacenter?node_ip=10.0.1.10 : Connection was refused by other side: 111: Connection refused.
2013-02-13 23:31:12+0900 [Test_Cluster]  WARN: HTTP request http://10.0.1.10:61621/cluster/datacenter?node_ip=10.0.1.227 failed: Connection was refused by other side: 111: Connection refused.


どうやら61621ポートでHTTPリクエストが拒否されているようです。
再度cassandra側を見てみます。
# ls -l /etc/init.d/ | grep ops
-rwxr-xr-x 1 opscenter-agent opscenter-agent  3197  7月  3 05:39 2012 opscenter-agent


今度はインストール自体はできているようです。
このファイルの中を見てみます。
# cat /etc/init.d/opscenter-agent
...
OPSC_ADDR_DIR="/var/lib/opscenter-agent/conf"
...


/var/lib/opscenter-agent/confがcassandra側のエージェント設定ディレクトリのようです。
confディレクトリにはaddress.yamlしかないので、中身を見てみます。
# cat /var/lib/opscenter-agent/conf/address.yaml
stomp_interface: "10.0.0.39"


ググってみたところ、OpsCenter側でSSLをOFFにしてある場合エージェント側でもHTTPSをHTTPに切り替える必要があるようで、use_sslを0に設定するといいようです。
そこで3つのcassandraノードに以下のように設定しました。
# cat /var/lib/opscenter-agent/conf/address.yaml
stomp_interface: "10.0.0.39"
use_ssl: 0



そして、再びOpsCenterの画面をみると、以下の状態になっているので同じ手順で再度「Install on all nodes」をクリックします。





すると、うまくいったようで、OpsCenterの画面が更新されて、情報が表示されるようになりました!





左ペインのメニューを適当にクリックしてみると、、、

CLUSTER:RING VIEW」:ハッシュの分散状態を確認できます。




CLUSTER:PHYSICAL VIEW」:ap-north-eastにまとまっていることがわかります。(Ec2Snitchの場合)




CLUSTER:LIST VIEW」:リスト状にノードが表示されます。




PERFORMANCE」:各ノードや全体平均のパフォーマンス統計情報が表示されます。




Data Modeling」:KeySpaceやColumnFamilyなど、データモデルを追加編集できたりします。




Data Explorer」:登録されているデータも見ることができます。




EVENT LOG」:クラスタに起こったイベントのログを確認できます。



EDIT CLUSTER...」:クラスタの簡単な編集もできるようです。



CLUSTER」の各ビューでは各ノードのアクション(CLEAR、DISCOMMISION、REPAIRなど)もできるようです。一部は有償のEnterprise版にしかできない機能もあるようです。


まとめ



cassandraクラスタの状態がかなり直感的にわかりとても便利そうです。
そして、ここまでできて無料というのはすごいです。

これから勉強しながらちょっとずつ利用してみたいと思います。


2013年2月12日火曜日

Cassandraってなんじゃ?(EC2でクラスタリング:シングルリージョン編)

以前cassandraの記事が途中で終わってしまっていたため、突然復活です。
前回までは、ec2にcassandraを入れて、ローカルからThrift APIでアクセスするところまで行いました。

今回はクラスタリングです。
cassandraはread/writeを分散できるクラスタリングの機能をサポートしており、負荷分散や冗長化がしやすいため、ここで勉強したいと思います。

構成は以下の通りです。



VPCのprivateサブネットに3台でクラスタリングして、publicサブネットからAPIでアクセスしてみます。
ここでは、natインスタンスを踏み台にしてcassandraの各ノードにsshで接続して作業します。

cassandraは、新しくノード(EC2インスタンス)が追加されたときに、クラスタ上のどれか1台につながればあとは自動的にすべてのノードに新ノードの情報が伝わるようになっています。そのため新規ノードが立ち上がった時に接続するためのseedといわれるノードが1つ以上必要です。ここではそのノードをseed (10.0.1.10)とします。


cassandraインスタンスの準備


cassandraは複数立ち上げますが、設定ファイルに自分のIPなどを記載する必要があるため、インスタンスごとに設定しなくても良いよう設定を自動化したAMIを作成します。
ベースとなるインスタンスに、以下のようにインストールします。
前回の記事から変化がある部分もあるため、最初から記載します。

javaのインストール

javaは以前の記事と同じように、ブラウザでOracleのサイトからダウンロードを初めて一旦キャンセルし、通信上のURLをコピーして使用します。またcassandraではjdk7ではなくjdk6が推奨されているようなので、今回はjdk6をインストールします。
# cd /usr/local/src
# curl -o jdk-6u39-linux-x64-rpm.bin -L http://download.oracle.com/otn-pub/java/jdk/6u39-b04/jdk-6u39-linux-x64-rpm.bin?AuthParam=1360419913_e2b3080676cab457471a1ee88b4dc0c5
# chmod a+x jdk-6u39-linux-x86-rpm.bin
# ./jdk-6u39-linux-x86-rpm.bin


cassandraのインストール

# cd /usr/local/src
# curl -OL http://ftp.tsukuba.wide.ad.jp/software/apache/cassandra/1.2.1/apache-cassandra-1.2.1-bin.tar.gz
# tar xzvf apache-cassandra-1.2.1-bin.tar.gz 
# mv apache-cassandra-1.2.1 /usr/local/
# cd /usr/local
# ln -s apache-cassandra-1.2.1 cassandra


cassandra.yaml


クラスタリングの設定では、cassandra.yamlの以下の部分を変更します。
seeds: 10.0.1.10
 本来はseedも動的に取得すべきですが、ここではseedが10.0.1.10を1つだけの決め打ちでいきます。

rpc_address: 0.0.0.0
 thriftプロトコルを受け付けるIPです。ここでは自分のprivateIPを指しますが、0.0.0.0でも動きます。

endpoint_snitch: Ec2Snitch
 ノードの置かれているネットワークトポロジの情報をcassandraが判断するための方式です。
 通常のデータセンターではデータセンターやラックという単位で区分けされますが、Ec2Snitchを使用すると、
 それがリージョンやゾーンとして区分けされます。

listen_address: 自分のprivateIP
 ノード間の通信に使用するときの自分のアドレスです。
 ここでは正しくIPを指定する必要があるようです。

auto_bootstrap: 自動でクラスタ参加するかどうか
 自分がseedのときはfalse、非seedのときはtrueを設定します。

このうち、listen_addressはノードによって変わるため、起動前に動的に書き換えられるようにする必要があります。
方法は後述します。


/etc/hosts


たとえば10.0.1.10は、hostnameとしてip-10-0-1-10などと振られますが、hostsファイルに記載がないためcassandraの起動時にエラーが発生します。そのため、起動時にhostsに自動登録する必要があります。
方法は、cloud-initや起動スクリプト内で行うなどありますが、ここではcassandraの起動スクリプト内で実行してみます。


/etc/init.d/cassandra (簡易版)

ここでは最もシンプルな起動スクリプトを使います。必要であればもっと高機能のものでもよいです。
ただ、上述のcassandra.yamlと/etc/hostsへの自動登録をstart時に行うようにしておきます。

# chkconfig: 345 95 1
# description: cassandra
# processname: cassandra

#!/bin/sh

CASS_BIN=/usr/local/cassandra/bin/cassandra
CASS_PID=/var/run/cassandra.pid

case "$1" in
    start)
        # hosts1行目(127.0.0.1)への自動登録

        sed -i '1s/ip-.*//g' /etc/hosts
        sed -i "1s/$/ $(hostname)/g" /etc/hosts
        # cassandra.yamlへのlisten_addressの自動登録
        sed -i '/^listen_address:/d' /usr/local/cassandra/conf/cassandra.yaml
        echo "listen_address: `curl http://169.254.169.254/latest/meta-data/local-ipv4`" >> /usr/local/cassandra/conf/cassandra.yaml

        $CASS_BIN -p $CASS_PID
        echo "Running Cassandra"
        ;;
    stop)
        kill `cat $CASS_PID`
        rm -f $CASS_PID
        echo "Stopped Cassandra"
        ;;
    *)
        echo "Usage: $0 {start|stop}"
        exit 1
esac
exit 0

cassandra.yamlのauto_bootstrapについては、UserData→cloud-initなどで自動設定もできますが、今回は固定でseedと非seed用でtrue, falseに固定して、それぞれの状態でseed用と非seed用のAMIを作成しておきます。


セキュリティグループ




セキュリティグループを設定します。
cassandraは以下のポートを利用します。
  • 7000:ノード間の接続
  • 7199:nodetoolなどツールが使用するJMX
  • 9160:Thrift API
これらと、作業用のsshポートなどを開放します。

http
  • 80 0.0.0.0/0
ssh
  • 22 10.0.0.0/16
nat
  • 22 10.0.0.0/16
  • 22 作業者のIP
cassandra
  • 7000 10.0.0.0/16
  • 7199 10.0.0.0/16
  • 9160 10.0.0.0/16

それぞれのインスタンスには以下を割り当てます。
  • app:http
  • nat:ssh


起動と確認


ここまでできたら、cassandraのAMIを起動します。
まず、seed用のAMIから起動します。
subnetはprivate用の10.0.1.0/24を指定し、privateIPに10.0.1.10を指定します。
セキュリティグループは以下を割り当てます。
  • seed, cluster*:ssh, cassandra
次に、cluster用のAMIから同様に2台起動します。privateIPは特に指定しません。


sshで10.0.1.10に入ります。

そこでcassandra-cliで前回と同じようにkeyspaceやcolumn familyを作成します。
# /usr/local/cassandra/bin/cassanra-cli
[default@unknown]  create keyspace Hogebook;
[default@unknown]  use Hogebook;
[default@Hogebook] create column family User with comparator = UTF8Type and 
default_validation_class=UTF8Type and key_validation_class=UTF8Type and column_metadata =[
{column_name: email, validation_class: UTF8Type},
{column_name: gender, validation_class: UTF8Type, index_type: KEYS}];
[default@Hogebook] set User['memorycraft']['email'] = 'memorycraft@gmail.com';
UnavailableException

エラーが発生しました。。
cassandraは他ノードへのデータのレプリカ数と、設定された一貫性保証レベルによって書き込みの際にエラーになったりするようです。これについては別記事で触れたいと思います。

ひとまずここでは、以下のようにして、3台すべてにデータレプリケーションされるように設定しておきます。
[default@Hogebook] update keyspace Hogebook with placement_strategy = 'org.apache.cassandra.locator.NetworkTopologyStrategy' and strategy_options = {ap-northeast:3};
[default@Hogebook] set User['memorycraft']['email'] = 'memorycraft@gmail.com';
[default@Hogebook] set User['memorycraft']['gender'] = 'male';
[default@Hogebook] set User['memorycraftgirl']['gender'] = 'female';
[default@Hogebook] set User['memorycraftgirl']['email'] = 'memorycraft+girl@gmail.com';
[default@Hogebook] get User where gender = 'male';
[default@Hogebook] get User where gender = 'female';


そして、クラスタの状態を見てみます。
クラスタの管理はcassandraのインストールディレクトリに入っているnodetoolを利用します。
ringコマンドは、クラスタの状態をみることのできるコマンドです。

# /usr/local/cassandra/bin/nodetool ring
Datacenter: ap-northeast

==========
Replicas: 3

Address         Rack        Status State   Load            Owns                Token                                       
                                                                               4159756940621079776                         
10.0.1.227      1a          Up     Normal  70.45 KB        100.00%             8650976588742297378                         
10.0.1.176      1a          Up     Normal  80.79 KB        100.00%             -7564491331177403445                        
10.0.1.10       1a          Up     Normal  90.42 KB        100.00%             4159756940621079776 

Ownsが100%になっているので、3台にすべてデータがレプリケーションされている状態です。


また、非seedであるclusterインスタンスをみてみます。
# cat /etc/hosts
127.0.0.1       localhost.localdomain localhost ip-10-0-1-176

# cat /usr/local/cassandra/conf/cassandra.yaml

....
auto_bootstrap: true 
listen_address: 10.0.1.176

設定ファイルの自動登録も旨く行っているようです。
これなら何台でも同じAMIから起動できます。


APIアクセス


また、appインスタンスに前回の記事と同じように、phpcassaをインストールします。
そして、以下のようなスクリプトで10.0.1.10に対してデータを連続投入してみます。

~/app/test.php 
---
<?php
    require(dirname(__FILE__).'/lib/autoload.php');

    use phpcassa\ColumnFamily;
    use phpcassa\ColumnSlice;
    use phpcassa\Connection\ConnectionPool;

    try{
        $servers = array('10.0.1.10:9160');
        $pool = new ConnectionPool('Hogebook', $servers);
        $user = new ColumnFamily($pool, 'User');

        //データの挿入
 while(1){
          $id = md5(uniqid(rand(),1));
   echo $id."\n";
   $user->insert($id,
            array(
                'email' => uniqid().'@gmail.com',
                'gender' => 'female',
            )
          );
   sleep(1);
 }
        //$pool->close();
    }
    catch(Exception $e){
        echo 'ERROR : ' . print_r($e, true);
    }
?>
----

本来コネクションプールは閉じないと行けませんが、ここでは無限ループさせてみます

$ php test.php 
5b3e62154f94a2a669e6b77298f22272
131b1d417165fea802c1a24afbfc2e7e
b3a731d17a301c1fa4f82d89500a1f68
86d568f544470595ab72483657b04352
ab4a15d2346440cc0e089235f68032ae
553ce1a57ad18a108ccba94224c98c4c
c2cf1319c7e099d16f84ecc2802f7b43
61c1d722b1d5d6442706fd5c4e5c0077
7067cfc415def987c5f5ffa12db9879e
a21f80887c9c2d163218045cf8ba321c
d71602cdd32f2cbd0a054aebacaa3764
f4f752c1dd8edde587d6d4a77ae30ec2
12901e29685ab6c3581a4944207c2e55
394f9c09e93dd33b1d378a9b299d1573
7d547f9682592945ddc0ddf1218e1c7e
f6fa6f5c8a80130abee396c438b6a8ac
fec78b484fa4f3975e60c86c082fe9fe
b629d87134a38d487e1750b3fa631d4c
72f9e4965084f39f158853761f0c62c5
e216af506ddae295346d94292e62fb7d
1631160e0977fdd6c4264555006c84ef
aa7f18a38ff739ce4a290d1f109e77f5
709a39189cef0cf82375ea124ef1d97c
0819cf50c7ed9f22b34c5a685f349f4f
5961358ecb3b4bb15ed2e6853357900d
1982683673d01a968a7f90dac8f6fe2c
3a4515cad2ee0ae803230aa7a5da7169
1d331dbd45cbee8038bb1c550039ae31
5d24a90282342d2786ce2dc25b398737
22194a119a48dd492cc42efc6f150b5a
e9040f305bccee5a5ae9fafa47e81820
f5dd73dfb1eda2de50a6ed03ce2e6de0
db86daa2bcb97bc269f846f3a78bb606
0c8a8793d8e71f29021917d7e0441519
bbf590273829d2efb99adba810ef7f13
a41e7ed89d14174f8b896bf887b7cfdc
bad960ca18793b57ca92974dce8bf248
6d4abcef26f8a9138034208df4bbc227
9fc5abb9d63e42a86c297835e9dad6bd
d1dbe39f50628cce099e2dcc3035392b


適当な処で終了して、seedインスタンス(10.0.1.10)でnodetoolを見てみます。
cfstatsコマンドではデータの統計情報がみれます。

# /usr/local/cassandra/bin/nodetool cfstats
.....
----------------
Keyspace: Hogebook
 Read Count: 18
 Read Latency: 0.16872222222222222 ms.
 Write Count: 338
 Write Latency: 0.5308639053254437 ms.
 Pending Tasks: 0
  Column Family: User
  SSTable count: 0
  Space used (live): 0
  Space used (total): 0
  Number of Keys (estimate): 0
  Memtable Columns Count: 667
  Memtable Data Size: 346140
  Memtable Switch Count: 0
  Read Count: 18
  Read Latency: 0.169 ms.
  Write Count: 338
  Write Latency: 0.531 ms.
  Pending Tasks: 0
  Bloom Filter False Positives: 0
  Bloom Filter False Ratio: 0.00000
  Bloom Filter Space Used: 0
  Compacted row minimum size: 0
  Compacted row maximum size: 0
  Compacted row mean size: 0

データの投入は成功しているようです。

またclusterインスタンス(10.0.1.176)で上記のPHPで出力されたキーを任意に選んで取得してみます。
# /usr/local/cassandra/bin/cassandra-cli

[default@unknown]  use Hogebook;
[default@Hogebook]  get User['d1dbe39f50628cce099e2dcc3035392b'];

=> (column=email, value=5119c2f9030a6@gmail.com, timestamp=1360642809012461)
=> (column=gender, value=female, timestamp=1360642809012461)
Returned 2 results.
Elapsed time: 19 msec(s).


取得も成功しました。

とりあえず基本的なクラスタリングの設定ができたようです。
次回は、もう少し詳しく見てみたいと思います。

2012年9月19日水曜日

Cassandraってなんじゃ?(API編)

Cassandraを利用するケースは、Webアプリケーションやバッチアプリケーションなど、プログラムからのアクセスが多いと思います。
そこで今回は、Cassandraにphpからアクセスしてみたいと思います。

CassandraにphpからアクセスするためのAPIライブラリとして以下のようなものがあるようです。
このうち、Thriftは、Cassandra専用でもPHP専用でもなく、複数の言語間でRPC通信を可能にする汎用フレームワークで、設定コードを元に各言語用のプログラムを生成します。他のライブラリはThriftで生成されたCassandraのlow-level APIをラップしたものになります。

もともとThriftを使おうと思っていましたが、Thriftの最新版 0.8.0は、Cassandra1.1.5では出力されたコードが不十分だったため、一度断念しました。機会があれば再度試してみたいと思います。

今回はphpcassaを試してみました。
phpcassaもThriftを使用しているので、Thriftが組み込まれ、すでにコード生成もされているので、インストールも非常に簡単です。

それでは早速インストールしてみます。
$ cd /usr/local/src
$ wget https://github.com/downloads/thobbs/phpcassa/phpcassa-1.0.a.5.tar.gz
$ tar xzvf phpcassa-1.0.a.5.tar.gz
$ cd phpcassa-1.0.a.5
$ phpize
$ ./configure
$ make
# make install

これでインストールは完了です。

次にアプリケーション用ディレクトリを作成し、phpcassaのライブラリをコピーします。
$ mkdir -p /home/memorycraft/app
$ cd /usr/local/src/phpcassa-1.0.a.5
$ cp -r lib /home/memorycraft/app/
$ cd /home/memorycraft/app/
$ touch test.php
$ tree -L 2
.
|-- lib
|   |-- autoload.php
|   |-- phpcassa
|   `-- thrift
`-- test.php


これでライブラリがそろったので、これらを利用してcassandraにアクセスするコードを書いてみます。
$ vim test.php
<?php
    require(dirname(__FILE__).'/lib/autoload.php');

    use phpcassa\ColumnFamily;
    use phpcassa\ColumnSlice;
    use phpcassa\Connection\ConnectionPool;

    try{
        $servers = array('localhost:9160');
        $pool = new ConnectionPool('Hogebook', $servers);
        $user = new ColumnFamily($pool, 'User');

        //データの挿入
        $user->insert('memorycraftgirl',
            array(
                'email' => 'ng@gmail.com',
                'gender' => 'female',
            )
        );

        //データの更新
        $user->insert('memorycraftgirl', array('email'=>'memorycraft+girl@gmail.com'));

        //キーで全カラムを取得  
        $girl = $user->get('memorycraftgirl');
        echo 'girl = ' . print_r($girl, true);

        //キーでカラムを指定して取得
        $email = $user->get('memorycraftgirl', null, array('email'));
        echo 'email = ' . print_r($email, true);

        //複数キーを指定して取得
        $users = $user->multiget(array('memorycraft', 'memorycraftgirl'));
        echo 'users = ' . print_r($users, true);


        //カラムの削除
        $user->remove('memorycraftgirl', array('email'));
        echo 'girl after remove column = ' . print_r($user->get('memorycraftgirl'), true);

        //行の削除
        $user->remove('memorycraftgirl');
        echo 'girl after remove row = ' . print_r($user->get('memorycraftgirl'), true);

        $pool->close();
    }
    catch(Exception $e){
        echo 'ERROR : ' . print_r($e, true);
    }
?>

このコードでは前回作成したHogebookのUserというColumn Familyに対して、挿入/更新/取得/削除を行い、その都度結果や、Column Familyの状態を表示するようにしました。

それでは実行してみます。
$ php test.php
girl = Array
(
    [email] => memorycraft+girl@gmail.com
    [gender] => female
)
email = Array
(
    [email] => memorycraft+girl@gmail.com
)
users = Array
(
    [memorycraft] => Array
        (
            [email] => memorycraft@gmail.com
            [gender] => male
        )

    [memorycraftgirl] => Array
        (
            [email] => memorycraft+girl@gmail.com
            [gender] => female
        )

)
girl after remove column = Array
(
    [gender] => female
)
ERROR : cassandra\NotFoundException Object
(
    [message:protected] => 
    [string:Exception:private] => 
    [code:protected] => 0
    [file:protected] => /home/memorycraft/app/lib/phpcassa/ColumnFamily.php
    [line:protected] => 308
    [trace:Exception:private] => Array
        (
            [0] => Array
                (
                    [file] => /home/memorycraft/app/lib/phpcassa/ColumnFamily.php
                    [line] => 299
                    [function] => _get
                    [class] => phpcassa\ColumnFamily
                    [type] => ->
                    [args] => Array
                        (
                            [0] => memorycraftgirl
                            [1] => cassandra\ColumnParent Object
                                (
                                    [column_family] => User
                                    [super_column] => 
                                )

                            [2] => cassandra\SlicePredicate Object
                                (
                                    [column_names] => 
                                    [slice_range] => phpcassa\ColumnSlice Object
                                        (
                                            [start] => 
                                            [finish] => 
                                            [reversed] => 
                                            [count] => 100
                                        )

                                )

                            [3] => 
                        )

                )

            [1] => Array
                (
                    [file] => /home/memorycraft/app/test.php
                    [line] => 43
                    [function] => get
                    [class] => phpcassa\ColumnFamily
                    [type] => ->
                    [args] => Array
                        (
                            [0] => memorycraftgirl
                        )

                )

        )

    [previous:Exception:private] => 
)

概ね期待通りの動作になっています。
また、存在しないキーを取得しようとすると例外が返る仕様のようです。

前回少し触れましたが、Cassandraはメモリテーブルを使用するため、このテストプログラムを実行したときの感覚としてはmemcacheへアクセスしたときと同じような体感速度でした。
今回は、localhostへのアクセスでしたが、クラスタを形成した場合のパフォーマンスなど、今後さらに調査してみたいと思います。

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らしい部分を調べてみます。