ぱろっと・すたじお

技術メモなどをまったりと / my site : http://parrot-studio.com/

Tokyo TyrantをDBと比較してみる

あらかじめ書いておくと、最終的にごく当たり前の結論しか出てきませんΣ(・ω・ノ)ノ
ただ、その「当たり前の結論」を、自分で身をもって体感しておくのは、
意味があるとは思います



例によって前置きから入るわけですが、
昨年末あたりから新しいシステム開発の話が立ち上がりまして、
先月あたりからアーキテクチャの設計をしておりました


ちょいと特殊なWebシステムなのもあり、
分散システム的な凝ったのを考えていたのですが、
いくつかピースが欠けていたのです


そんなとき、本屋で見たのがこれです

Software Design (ソフトウェア デザイン) 2010年 02月号 [雑誌]

Software Design (ソフトウェア デザイン) 2010年 02月号 [雑誌]

この本で「Key-Value Store (KVS)」の特集をやっておりまして、
まさに「これだ!」と


欲しいのは「分散KVS」なのですが、まずはそれらの基盤となっている、
「Tokyo Cabinet」*1が、DBと比較してどんなもんなのか試してみました(`・ω・´)


なお、「Tokyo Cabinet」がファイルコンテナで、
Tokyo Tyrant」がそのネットワークインターフェースです
また、ほとんどの国産KVS*2が「Tokyo Cabinet」を利用しています

環境

ハードウェア
  • Intel(R) Xeon(R) 3.00GHz(DualCore)
  • メモリ4GB
  • HDDは2台がハードウェアRAID1
  • 他のシステムも動いている環境(社内向けサーバ テスト時の負荷は軽微)
ソフトウェア
  • TTのパラメータはデフォルト(チューニングなし)
  • MySQLはチューニング済み
  • どちらもlocalhostからオープン
テストデータ
  • アプリレベルでパーティショニングされた中の1テーブル
    • 実際にこの単位で使っている
  • レコード件数:約115万
  • カラム数は7
  • 格納データは文字列と数値のみで、マルチバイトなし

書き込みテスト

  • DBから全件読み込み
  • (コードレベルでは)fetchで1件ずつ取得
  • 取得データを{カラム => 値}でハッシュ化した後、テストごとに処理
  • TTのkey書式は「固定文字列_数値9桁」
WTest1(DB)
  • 取得したデータをDBの別スキーマにそのまま書き込み
  • コミットは最後に一回
WTest1'(DB)
  • WTest1と同じだが、コミットをレコード毎に行う
  • 20分待っても終わらなかったため、停止(´・ω・`)
WTest2(TT-RDB)
  • TTをハッシュデータベースで起動
  • 取得データを無視し、固定長の数値文字列をset
  • 取得自体は行うため、読み込みのコストは同じ
WTest3(TT-RDB)
  • TTをハッシュデータベースで起動
  • 取得したデータをYAML化して格納
WTest4(TT-TBL)
  • TTをテーブルデータベースで起動
  • 取得データのHashについて、Valueを文字列変換してから格納

読み込みテスト

  • TTはあらかじめ分かっているキーで順に全データを取得
  • DBは単純に全件取得し、WTestと同じロジックでHash化
  • 10000件ごとに取得結果を書き出し、中身が正常であることを確認
  • RTest[A]はWTest[A]の結果の全件取得テスト
    • RTest3のみ、取得後にYAML.loadでHashに復元

検索テスト

  • ある1カラムを指定して検索
    • DBはIndexあり
    • TTはIndexあり/なしで測定
  • そのカラムは二種類の値[1, 2]のみ存在
  • 一致検索ではなく、範囲検索(>=2)
  • 検索結果は読み込みテストと同じ処理
  • TTの場合、検索結果はkeyで返されるため、そのkeyで値を取得
検索テスト 内容
STest1 WTest1のDBから検索
STest2 WTest4の内容を検索/Indexなし
STest3 WTest4の内容を検索/Indexあり

結果

  • 測定は数回行い、最速の結果を採用
    • ただし、全てのテストで回ごとのぶれは最大でも2秒
  • 時間は分:秒で表記
テスト 対象 時間 備考
WTest1 DB 5:18
WTest2 TT-RDB 3:47 単純KV
WTest3 TT-RDB 9:52 YAML
WTest4 TT-TBL 5:49
RTest1 DB 2:13
RTest2 TT-RDB 1:01 単純KV
RTest3 TT-RDB 1:49 YAML
RTest4 TT-TBL 4:11
STest1 DB 0:39
STest2 TT-TBL 1:28 Index×
STest3 TT-TBL 1:25 Index○

考察

  • 単純な比較だとDBの方が速い
    • もちろん、チューニングの有無もある
  • DBの代替として使うには厳しい
    • DBの負荷を下げるキャッシュとしてならいけそう
    • TT-TBLは便利そうだけど、コストがでかい
  • Rubyがフロントエンドであることも影響?
    • 基本的には自前で文字列化が必要
      • YAMLとかJSONとか
      • SymbolかNumericでないと自動でto_sしてくれない
  • たぶんCならもっと速い?
  • Indexが効きづらいのは、データのせいかもしれない
    • もっと長い文字列ならあるいは・・・
  • 単純なKey-Valueの場合、DBよりもTTが上
  • to_yamlのコストは思ったよりでかい
  • YAML.loadのコストはそこそこだが、致命的なほどでもない
  • こうしてみると、GoogleのProtocol Bufferの威力がよくわかる

つまり、TTがDBに対してアドバンテージを持つのは、
小さなデータを大量に更新する場合であり、
まさにSNSのログイン情報とか、ECサイトの閲覧履歴のような処理です


既存のDBが十分に性能を発揮できる範囲では、
やはりKVSよりDBの方が高速であり、
この意味でKVSを導入する必要性は薄いと思われます


しかし、ご存じのように、DBはスケールコストが半端ではありません
この点では「分散KVS」に圧倒的なアドバンテージがあります
(性能/容量がリニアにスケールする)


・・・と、ここまで書いておきながら、
実はTC/TT自体は「分散KVS」ではありませんΣ(・ω・ノ)ノ


TTはMySQLと同じレプリケーション機構を持つものの、
完全なノードクラスタ構成にはなってません
なので、自立的に処理を分散することはできないのです


だからこそ、ROMAのような分散KVSとしてのフロントエンドが必要なわけですが、
データ格納にTCを使う(ことが多いらしい)ため、
本質的には今回のような性質を引き継ぐと思われます

結論

  • 単体で使うならTCよりはDBの方がいい
  • 分散KVSを導入する状況は以下の場合
    • 大量のサーバで分散処理したい
    • 大量のデータを分散して格納する必要がある
    • 小さなデータによるトランザクションが大量に発生する


ほら、当たり前の結論だ(´・ω・`)

*1:あの「Hyper Estraier」を作った方がmixiで開発

*2:kumofs / ROMA / Flare 詳細は雑誌参照