Cassandra とは?
Cassandra は 2008年に Facebook によってオープンソース化された分散型KVSです。
Amazon Dynamo の作者の一人である Avinash Lakshman と、Facebook のエンジニアである Prashant Malik によって設計されました。
Cassandra は、高可用性(High Availability)を持ち、コモディティハードウェアやクラウド基盤上で線形スケーラビリティ(Linear Scalability)と耐障害性(Fault-Tolerance)を提供します。
誤解を恐れずに平たく言えば、
- ノードに障害が発生してもサービスを提供し続けることができ、
- ノードを追加することで線形に性能が向上し、
- 通信障害などによってメッセージが損失しても継続してサービスを提供する
ということです。
分散コンピューティングにおいて、CAP定理という定理がよく知られていますが、そのうちの AP を重視したアーキテクチャです。
Apache Cassandra
http://cassandra.apache.org/
カラム指向の利点・欠点
MySQL を始めとする行指向のデータベースでは、少数行の複数列からなるデータを扱うのに適していますが、大量の行に関して少数の列へ変更を加えるといった処理には適していません。一方で、列指向のデータベースはその逆の特性を持ちます。
例えば、サーバーログなどの大量のデータを扱いたいが、実際に使用するのは 2つか 3つの列であるというような場面では、理論上列指向のデータベースの方が優れていると言えます。
逆に、同様にしてサーバーログを扱う場面でも、複数列の結果を併せて使用したいという要件では、カラム指向は最適な選択とは言えないかもしれません(ただし、時系列データに関しては、RDB もあまり得意とする分野ではありません)。
Rails で Cassandra を使う
さて、実際に Cassandra を Rails から使用してみます。
今回は、MacBook上に 1ノードからなる Cassandraクラスタを構築して使用してみます。
Cassandra は、Apache Cassandra からダウンロードできますが、Mac の場合は homebrew から以下のコマンドを叩くだけでインストールできるので、そちらを使用します。
$ brew install cassandra
インストールが完了した後で、以下のコマンドを打つと、Cassandra がフォアグラウンドで起動します。
daemonモードで起動したい場合は、-f
を消してください。
$ cassandra -f
フォアグラウンドで起動すると terminal が 1画面占領されるので、新しく terminal を開くなり、tmux を使用するなりして別ウィンドウで起動してください。
次に、Rails で新しいアプリケーションを作成します。この際、ActiveRecord は使用しないため、skip します。
$ rails new CassandraRails --skip-active-record
ActiveRecord に似たインターフェースで Cassandra を操作するために、Gemfile に cequel を追加します。
gem 'cequel'
その後、いつものように bundle install
をして、必要な gem をインストールします。cequel と cassandra-driver がインストールされるはずです。
インストールが完了したら、設定を行います。
$ rails g cequel:configuration
これにより、config/cequel.yml
が作成されます。
中身はおおよそ Cassandraクラスタに関する情報です。
今回はとりあえず 開発環境として localhost上に 1ノードからなる Cassandraクラスタを構築するので、このままで大丈夫です。
最後に、Cassandra上に keyspace を作成します。RDB で言うところの DB に対応します。
$ rake cequel:keyspace:create
これで設定は完了です。試しに tweet の情報を格納する Tweetモデルを作成してみます。
class Tweet
include Cequel::Record
key :id, :text
column :username, :text
column :body, :text
column :retweeted, :int
column :favarited, :int
end
自動的に primary key を作成しないため、明示的に指定してあげる必要があります。
ここでは、key :id, :text
として指定しています。tweet_id をナチュラルキーとして用いる想定で作成しています。
今回は使用していませんが、belongs_to / has_many などが使えるようです。
ちなみに、Cassandra に対するクエリ言語である CQL で使用できるデータ型については CQL data typesに一覧があります。cequel でコレクション型(list, set, map)を使うためには、例えば以下のように model に記述します。
class Tweet
include Cequel::Record
(中略)
set :reply_to, :text
end
モデルの記述が終わったら、migration を実行します。
$ rake cequel:migrate
Synchronized schema for Tweet
migration を実行しても、migrationファイルが追加されることはありません。
以上で Cassandra を Rails から使用する準備が整いました。
実際に rails console からデータを入力してみます。
$ rails c
Loading development environment (Rails 4.2.1)
irb(main):001:0> t = Tweet.new(id: 't123')
=> #<Tweet id: "t123", username: nil, body: nil, retweeted: nil, favarited: nil, reply_to: #<Set: {}>>
irb(main):002:0> t.username = '_c_hase'
=> "_c_hase"
irb(main):003:0> t.reply_to << 't122'
=> #<Set: {"t122"}>
irb(main):004:0> t.save
=> true
という感じで、ActiveRecord を使用するのと同じように Cassandra にアクセスできています(見やすいように改行
を入れています)。
find は model中で指定したキーに対して実行可能です。
> t = Tweet.find('t123')
=> #<Tweet id: "t123", body: nil, favarited: nil, reply_to: #<Set: {"t122"}>, retweeted: nil, username: "_c_hase">
> t.username
=> "_c_hase"
ただし、RDB のように whereメソッドで絞り込もうとすると、ArgumentError となります。
> Tweet.where(username: '_c_hase')
ArgumentError: Can't scope by non-indexed column username
エラーコードにあるままなのですが、特定のカラムで絞り込みたい場合は、index を作成します。
$ git diff
diff --git a/app/models/tweet.rb b/app/models/tweet.rb
index 80d2400..be18e40 100644
--- a/app/models/tweet.rb
+++ b/app/models/tweet.rb
@@ -2,7 +2,7 @@ class Tweet
include Cequel::Record
key :id, :text
- column :username, :text
+ column :username, :text, :index => true
column :body, :text
column :retweeted, :int
column :favarited, :int
migration を実行したタイミングで index が貼られるので、$ rake cequel:migrate
を忘れずに実行してください。
> Tweet.where(username: '_c_hase')
=> [#<Tweet id: "t123", body: nil, favarited: nil, reply_to: #<Set: {"t122"}>, retweeted: nil, username: "_c_hase">]
まとめ
cequel を使用することで、ActiveRecord と似た感じで Cassandra を操作できることを確認しました。
まだ発展途上という感じがするのですが、時系列データを扱いたいときなどには手軽に使用できるので、検討してみてはいかがでしょうか。
次回予告
もう少し理論的なところから、Cassandra について実験・考察をしたいと思っています。
Cassandraメインの記事で、おまけとして Rails で使用するという感じになるかと思います。
Appendix
本件のサンプルプロジェクトは今後も加筆していく予定なので、コードは github にあげてあります(commit 2b93f1211ace3ffde4c0217a32b4d2a29ba6dcda
まで)。
https://github.com/chase0213/CassandraRailsSample