Entries tagged “Cassandra”

Cassandra修行 その2

written by shn, on Mar 21, 2010 7:06:00 PM.

「Cassandraというものがなんだかよく分からない」 〜 NoSQLについて、鳩山由紀夫

週末プロジェクトになりつつあるCassandra修行ですが、しょっぱなから躓いている。 (経験から言えば、躓いたらほっておいて先進めばいいだけなので、躓くのを言い訳に止まっている)

さて、

あるユーザーサービスを考えてみる。ユーザー毎にページをもたせたい(http://hogehoge.com/shn/ みたいに) また認証はパスワードとか面倒なのでOpenIDにしてみよう。

というのをCassandra(とLazyboy)でやるにはどうしたらいいのかしら。

MySQLだと

MySQLでやるならこんな感じのテーブルを用意しているはずだ。

CREATE TABLE Accounts (
  account_id INTEGER NOT NULL PRIMARY KEY AUTO INCREMENT,
  account VARCHAR(64) NOT NULL UNIQUE,
  openid_identifier VARCHAR(255) NOT NULL UNIQUE
);

アカウント情報について、「アカウントから」と「OpenIDから」の2系統からアクセスされるので、その二つをUNIQUE INDEXにした。

Cassandraだと

Cassandraの場合はひとつのColumnFamilyについて一つの順序しか持てない。今回のアプリの場合、http://hogehoge.com/shn/からshnを引っ張ってくるケースが多そうなので、とりあえずアカウント名でソートされたColumnFamilyを用意するのか

<Keyspace Name="UserData">
  <ColumnFamily CompareWith="BytesType" Name="Accounts"/>
</Keyspace>

{ // Accounts
  "shn" : {
      "openid_identifier": "http://glucose.jp/shn"
  }, ...
}

て感じだろうか。さてOpenIDからアカウントを引っ張ってくる場合はどうするのだろう。新しくColumnFamilyを定義するか、

<Keyspace Name="UserData">
  <ColumnFamily CompareWith="BytesType" Name="Accounts"/>
  <ColumnFamily CompareWith="BytesType" Name="Identifiers"/>
</Keyspace>

{ // Identifiers
  "http://glucose.jp/shn" : {
      "account": "shn"
  }, ...
}

Accountsの中に特殊なRow Key(__identifiers__的な)を定義してその中に突っ込むかの2通りが思いつく。

{ // Accounts
  "shn" : {
      "openid_identifier": "http://glucose.jp/shn"
  },

  "__identifiers__" : {
      "http://glucose.jp/shn": "shn"
  }, ...
}

どっちが良いのかな。API的にはどっちもgetを2回呼ぶ事になるのだと思うけど、データはどのノードに配置されるのだろうか。

他のものを見てみるとtwissandraの場合前者の方法だ。こっちのBlogの例題の場合後者に近い(ちょっと用途違うけど)。 Delicious clone in cassandraの例はどうやってデータひっぱってくるのかさっぱりわからない。

DataModel - Cassandraw Wikiによると

The row key is what determines what machine data is stored on. Thus, for each key you can have data from multiple column families associated with it. However, these are logically distinct, which is why the Thrift interface is oriented around accessing one ColumnFamily per key at a time. (TODO given this, is the following JSON more confusing than helpful?)

keyによってどのノードにデータが保持されるか決まるそうなので、後者の方法だと特定のマシンにOpenIDのindex情報が固まる事になる。ここらへん、例えば「インデックス用のCFは全ノードに配置したい!」とか邪な事を考えた場合に、カスタマイズしたReplicationStorategyを書けば良さそうなところがCassandraの強みかもしれん。

Lazyboyさん

PythonのCassandra高級インタフェースであるところのLazyboyさんはViewという仕組みがあるようだ。

Lazyboy's view classes provide a way to build secondary indexes into a column family. For example, you might create a view that has only Lazyboy authors from the Users table:

というわけで、IdentifiersというViewを定義してそこのappendしたら良いかな?とやってみたら

{ // Identifiers
    "shn": "shn"
}

というCFができて泣いた。ViewのKeyを決定する関数が

def _record_key(self, record=None):
    """Return the column name for a given record."""
    return record.key.key if record else str(uuid.uuid1())

となっているため、User(key=shn)を突っ込むとViewのkeyもshnになっちゃう… ソース読んでて思ったのだけど、このViewはタイムライン的な奴を実装するためにあるようなので、僕のやりたい事とはちょっとずれているな。

というわけで

というわけで、玉虫色のエントリになったCassandra修行二回目。 その他参考にしたりしたページ

Cassandra修行

written by shn, on Mar 15, 2010 11:10:00 PM.

日曜大工はPython3kにしようか > Python3kはMySQLドライバ無いのか > じゃあCassandra使ってみるか > thriftもPython3k無いじゃん > じゃあ2.6でCassandraか、という経路によりCassandraにトライしてみることにしました。

修行中の身である故、情報の正確性は無担保。 ためしたCassandraは0.5.1

Cassandraとは

Cassandraとは、もともとFacebookが開発して、現在はApacheでホスティングされている分散データベース。最近、TwitterがMySQLから移行したことで話題になった。

いわゆるNoSQL(この呼び方は好きじゃないけど)のジャンルに属するデータベースで、データモデルの表現力的には DataStore(GAE) > Cassandra > Key-Value Storeぐらいのものなんじゃないのかなと思う。

動かしてみる

DLしてみたらJavaだった。Javaなんて大学のレポートいらいですね。あれもJythonで書いてJavaコードにコンパイルして出した気がする。とりあえずシングルノードで動かしてみようと思って

bin/cassandra -f

してみたら、いくつかライブラリが足りなかった(主にログ関係?)。 slf4j-log4j12-1.5.11.jar, slf4j-api-1.5.11.jar, commons-collections-3.2.1.jar, google-collect-1.0.jar, log4j-1.2.15.jarを足してもっかい起動。拍子抜け 。-fはフォアグラウンドで起動するオプション。

Cassandraのデータモデル

CassandraのデータモデルはプロジェクトWikiの他に、PythonのライブラリであるLazyboyや、その作者のブログに詳しい。

特に三つ目のがわかりやすいので、いいんじゃないでしょうか。

Cassandra用語

CassandraのDataModelはKeyspace > (Super)ColumnFamily > Columnという構成になっている。 RDBMSで言うと、Keyspaceは"データベース"、ColumnFamilyは"テーブル"ってぐらいのポジションではないかと。

Column

Columは最小のデータ単位。name, value, timestampのタプル。

{
  "name": "emailAddress",
  "value": "foo@bar.com",
  "timestamp": 123456789
}

timestampはとりあえずスルーしておいていいらしい。name, valueについてはバイト列として扱う。

ColumnFamily

ColumnFamilyは、keyで識別されるRowの固まり。各Rowの中身はColumnのマップになっている。Rowは常にソート済みで保持されている。 ColumnFamilyの定義 - Row keyの評価法(バイト列として評価するか、utf8文字列として評価するか)や、レプリケーション - はstorage-conf.xmlで定義する。

ColumnFamilyのソート順

インデックスが無い代わりに、ColumnFamilyが常にソートされているっていうのがCassandraのユニークな点ではないだろうか。RSS Reader/SNS/Blogのような、昨今のウェブサービス市場を占める「なんかのタイムラインを表示する」アプリケーションに特化している(まだ実際にさわってないからようわからんけど)

ColumnFamilyのソート順はBytesType, UTF8Type, LexicalUUIDType, TimeUUIDType, AsciiType, LongTypeがある模様。 BytesTypeAsciiTypeの違いは後で調べる。 LongTypeは64bit integerと扱うようだ。64bit以上の値とかを入れたらどうなるんだろう。バイト列としてセットするのだろうか。

LexicalUUIDTypeは128bitのUUIDとして評価するのかな。多分、想像だけどLazyboyなんかのライブラリがオブジェクトを追加するときにUUIDを自動生成したりして、keyを決めるのがめんどいときに便利なんじゃないだろうか。

TimeUUIDTypeはTime UUID(ver1のUUIDのことかしら。ようわからん)として評価する? LongTypeと比べたときの利点は、timestampが被っても大丈夫ってあたりなんだろうか? 上にあげたブログ記事に、ブログ記事のタグごとのタイムラインをTimeUUIDTypeで管理する例が載っている。

SuperColumnFamily

SuperColumnFamilyはColumnFamilyのSuperColumn版。SuperColumnとはValueの代わりにColumnのリストが入っている。

TODO

だんだん書いていて飽きてきた。ただ、書くと再確認になってよいですね。ColumnFamilyのどこがソートされているのかについてアヤフヤだったのがわかった。

日本語の情報あまりないなーと思っていたら、id:dannさんが書いていた。幅広く解説してある。紹介されていた、Twitterのデータ構造をCassandraでやってみる例(Twissandra)は後で見よう。

TODOとしては

  • Twissandra見る
  • コード書いてみる
  • Replication, Partitioning, Consistencyあたりを理解する。

あたりだろうか