NoSQLデータベースを試してみる

第3回 様々なデータ型を扱えるTokyoTyrant

この記事を読むのに必要な時間:およそ 4 分

memcached互換プロトコル

もちろんTokyoTyrant独自プロトコルを使って利用することも可能ですが,ハッシュデータベースであればmemcached互換プロトコルが利用できます。前回memcachedを利用したときのコードの一部を以下のように変えるだけです。

コンストラクタの引数を変えるだけで利用できる

def do_init
  @cache = MemCache.new(
-   ['localhost:11211', 'localhost:11212', 'localhost:11213'], # memcached
+   ['localhost:1978', 'localhost:1979', 'localhost:1980'],    # TokyoTyrant
    :logger => Logger.new(STDOUT)
   )
end

memcached互換プロトコルを利用する場合,Consistent Hashingによって処理を分散させるのも非常に簡単です。実際試してみると,別々のポートで立てたTokyoTyrantサーバに分散してデータの保存/読み出しが行われていることが確認できます。

プロンプト2 コンソールログ

# key4は1978に,key2, key3は1979に,key1は1980に保存されている
set key1 to <MemCache::Server: localhost:1980 [1] (CONNECTED)>: 5
set key2 to <MemCache::Server: localhost:1979 [1] (CONNECTED)>: 19
set key3 to <MemCache::Server: localhost:1979 [1] (CONNECTED)>: 16
set key4 to <MemCache::Server: localhost:1978 [1] (CONNECTED)>: 19

テーブルデータベース

次に,もう一つの用途として考えられるテーブルデータベースを扱ってみましょう。テーブルデータベースではmemcached互換プロトコルは使えないため,TokyoTyrantの独自プロトコルを利用します。

まず,テーブルデータベース用のTokyoTyrantサーバを立ち上げます。先ほどとは保存先の拡張子が違うことに注意してください。

プロンプト3 テーブルデータベース用のTokyoTyrantサーバをマスター/スレーブ構成で起動する

sudo ttserver -port 1981 -ulog /var/ttserver/ulog_master -sid 1 -pid /var/ttserver/pid_1981 -dmn /var/ttserver/ttcache_1981.tct
sudo ttserver -port 1982 -ulog /var/ttserver/ulog_slave_1982 -sid 2 -mhost localhost -mport 1981 -rts /var/ttserver/3.rts -pid /var/ttserver/pid_1982 -dmn /var/ttserver/ttcache_1982.tct
sudo ttserver -port 1983 -ulog /var/ttserver/ulog_slave_1983 -sid 3 -mhost localhost -mport 1981 -rts /var/ttserver/3.rts -pid /var/ttserver/pid_1983 -dmn /var/ttserver/ttcache_1983.tct

TokyoTyrantはmemcachedと同じようにクライアント側の実装によって処理を分散させる必要があります。ただし,Consistent Hashingのような仕組みはなく,レプリケーションで処理(主に読み込み部分)を分散させるようです。-mhost, -mportでマスターのホストとポートを指定し,-rtsでレプリケーションタイムスタンプの記録を,-ulogでレプリケーション用のログを出力しています※4⁠。

※4
あらかじめログ出力用のディレクトリを作成しておく必要があります。

テーブルデータベースを利用した具体的なコードは以下のようになります。簡単な実装ですが,set_connectionメソッドでマスター,スレーブを設定し,do_initメソッドで書き込みはマスターに,読み込みはスレーブに処理を振り分けています。

リスト2 テーブルデータベースを利用した具体的なコード例

class TokyotyrantTdbController < ApplicationController
  require 'tokyotyrant'
  include TokyoTyrant

  before_filter :set_connection, :do_init
  after_filter :do_finalize

  def set
    @rdb['1'] = {"name" => "山田花子", "sex" => "female", "birthday" => "20050321"}
    @rdb['2'] = {"name" => "鈴木太郎", "sex" => "male", "birthday" => "20060601"}
    @rdb['3'] = {"name" => "佐藤洋子", "sex" => "female", "birthday" => "20070311"}
    @rdb['4'] = {"name" => "山田健一", "sex" => "male", "birthday"=> "20070523"}
  end
  
  def get
    qry = RDBQRY::new(@rdb)
    qry.addcond("name", RDBQRY::QCSTRBW, "山田")
    qry.setorder("birthday", RDBQRY::QONUMDESC)
    res = qry.search()

    p res # ["4", "1"] (keyが返ってくる)
    p @rdb.get(res[0]) # {"name" => "山田健一", "sex" => "male", "birthday"=> "20070523"} 
    p @rdb.get(res[1]) # {"name" => "山田花子", "sex" => "female", "birthday" => "20050321"}
  end

  private

  def set_connection
    @connection = {
      :master => ['localhost', 1981],
      :slave => [
        ['localhost', 1982],
        ['localhost', 1983]
      ]
    }
  end

  def do_init
    @rdb = RDBTBL::new() # テーブルデータベース

    case action_name
    when 'set'
      _con = @connection[:master]
      @rdb.open(_con[0], _con[1])
      logger.info "write to master: #{_con[0]}:#{_con[1]}"
    when 'get'
      _con = @connection[:slave].sort_by{rand}.first
      @rdb.open(_con[0], _con[1])
      logger.info "read from slave: #{_con[0]}:#{_con[1]}"
    end

    @rdb.setindex("name", RDBTBL::ITLEXICAL) # インデックス
  end

  def do_finalize
    @rdb.close()
  end
end

例では,名前が⁠山田⁠で始まるデータを誕生日の降順で検索してみました。このようにテーブルデータベースではRDBMSのように検索条件を利用することができます。ただ,実際に使うとなると独自の定数がたくさん出てくるため,扱いにくいなぁというのが正直なところです(強調部分⁠⁠。紙面の都合上,詳しくは扱いませんが,MiyazakiResistanceActiveTokyoCabinetを使うとテーブルデータベースをActiveRecordのように扱えます。

まとめ

今回はTokyoTyrantについて見てみました。memcachedは何といっても手軽なのですが,どうしてもデータ消失のリスクがあります。TokyoTyrantをmemcached互換プロトコルで利用すれば手軽にデータの永続性が保証できるため,非常に便利ではないでしょうか。また,テーブルデータベースは導入の敷居は高いですが,RDBMSのように扱えて高速に処理できるというのは魅力的です。

著者プロフィール

佐々木達也(ささきたつや)

普段は主にHadoopを用いた大規模データ解析を行っている象使いエンジニア。最近はNoSQLデータベースにも興味を持っている。Ruby, Railsはもちろん、広島カープも大好き。

bloghttp://blog.livedoor.jp/sasata299/

twitterhttp://twitter.com/sasata299/