株式会社クリアコード > ククログ

ククログ


お知らせ: OSC2011.DBでgroongaストレージエンジンを紹介

今度の土曜日11/5に開催されるOSC2011.DBの10:35からのセッション「OSSDB MySQL」に少しおじゃましてgroongaストレージエンジンを紹介します。groongaストレージエンジンがイベントなどで紹介されるのは昨年の全文検索エンジンgroongaを囲む夕べ #1以来のはずなので約1年ぶりになります。その間に初のメジャーリリースである1.0.0がリリースされるなど、だいぶ成長しています。そのため、話題はたくさんあるのですが、その中から特に注目すべきところを選り抜いて紹介します。

groongaストレージエンジンについては1ヶ月後の全文検索エンジンgroongaを囲む夕べ 2でも詳しく紹介されますが、一足早く知りたい人はぜひ参加してください。(参加費用は無料ですが参加登録が必須なので注意してください。)

2011-11-01

Firefoxの技術書「Firefox Hacks Rebooted」

オライリーより、Firefoxの高度な使い方からアドオン開発のノウハウ、新しいWeb技術まで手広く解説・紹介する書籍「Firefox Hacks Rebooted」が、2011年10月26日に発売されました。弊社でMozillaサポート事業に従事している下田も執筆者の一人として名を連ねています。

Firefox Hacks Rebooted ―Mozillaテクノロジ徹底活用テクニック
浅井 智也/池田 譲治/小山田 昌史/五味渕 大賀/下田 洋志/寺田 真/松澤 太郎
オライリージャパン
¥ 16,482

内容が多岐に渡るため、「こういう人に読んでもらいたい!」という想定読者が章ごとにそれぞれ異なるのですが、全体を見た時の内容の充実度からは、Firefox用アドオンの開発に関心がある方に特にお薦めと言えるでしょう。そこでこの記事では、開発者視点から本書の見所をいくつか紹介します。

Vimperator vs KeySnail

目次を順番に眺めて目を引くのは、2章におけるVimperatorとKeySnailの解説でしょう。VimとEmacsといえば開発者が使う開発環境の二大巨頭で、よくエディタ戦争のネタにもされますが、本書の2章でも、Vimを模したVimperatorとEmacsを模したKeySnailの戦争が繰り広げられています。

……というのは冗談なのですが、Vimperator開発メンバーの一人であるteramakoさんがVimperatorを、KeySnailの作者であるmoozさんがKeySnailをそれぞれ解説し、最後に二人がそれぞれの設計思想の違いを解説するという構成になっていて、こと両アドオンの解説記事としてはこれ以上無い豪華な内容と言えるでしょう。開発者自らによる解説という事で、内容の信頼性の高さも折紙付です。

快適な開発生活を送る上では、効率の良い情報収集や情報発信の方法を知る事も大切です。また、情報は発信する人の所に集まるとも言います。最新の技術へのフォローを欠かす事ができない開発者の人達にとっても、この章の内容は役立つのではないでしょうか。

Add-on SDKでのアドオン開発

3章は、FireGesturesなどの作者としても知られるGomitaさんによるAdd-on SDKの解説です。基礎概念の解説に始まり、少しずつ機能を付け足しながら実際に1つのアドオンを完成させるまでの過程をチュートリアル形式で紹介する事により、Add-on SDKを使ったアドオン開発の一通りの流れを理解する事ができます。

Firefox 4以降のバージョンでは高速リリースが採用された事により、従来の形式のアドオンの開発スタイルだとFirefoxの更新に追従するだけでも大変という状況になっています。Add-on SDKはFirefoxのバージョン間の違いを吸収する層としての働きも備えているため、これから新たにアドオンを開発する場合はSDKを利用した方が良いと言えますが、本章はそのファーストステップとして最適でしょう。

また、後の章にはHTML5関連技術などの新しいWeb標準技術の解説もあります。それらを組み合わせる事によって、SDKが提供している機能を超えた様々な事が実現できるようになります。本書は話題が多岐に渡っていますので、全体を通読する中でそういった発展に繋がるヒントを得やすいのではないでしょうか。

Add-on SDKの枠を超えたアドオン開発

弊社所属の下田は、4章と6章の一部として、SDKに依らない・より低層の部分に関する技術情報を寄稿しています。

4章では、再起動のいらないアドオンを、これまでの一般的なアドオン開発の延長線上にある物として捉えた上で、これまで通りにできる事とそうでない事とを整理して、それらに対する対策となる様々なテクニックを紹介しています。

また、プロセス分離型の設計に移行していくにあたって、そもそもFirefoxではどのようにプロセスの分離が実現されているのかを理解し、プロセスが分離された環境ではどのような事に気をつけなくてはならないのかについても解説しています。

SDKの枠を超えた開発を行いたい時や、SDKに含まれている標準ライブラリの中で行われている事を理解したい時などには、各要素技術への理解が必要になってきます。そういった場合も、この章の情報が手がかりとなるかも知れません。

その他にも、非同期処理の記述を支援する軽量ライブラリ「JSDeferred」のFirefox上での活用事例や、CPUの使用率とメモリの使用量を表示するFirefoxアドオン「システムモニター」でも利用している、C言語で開発されたプラットフォームネイティブのライブラリをJavaScriptから透過的に利用する技術「js-ctypes」の解説(6章に収録)など、SDKベースでの開発にも応用可能な情報もあります。

Webでは断片的にしか得られない情報のまとめ&発展として

本書には、Web上にあるFirefoxの断片的な情報を取っ掛かりとして、それらをさらにもう1段階・2段階と掘り下げた情報が多数収録されています。自分で情報を探そうとして挫折した方、見つけた情報が中途半端で途方に暮れてしまった方など、表面的な情報よりももっと本質的な情報を求めている方にお薦めと言えるでしょう。

なお、本書の目次各章のサンプルがWebで公開されています。Firefoxのヘビーユーザーの方やアドオン開発者の方は、役立つトピックが含まれているかもしれませんので、興味を持たれた際には是非一度目を通してみて下さい。

タグ: Mozilla

この記事の続き

2011-11-02

すべてのMySQLユーザーに高速な全文検索機能を! - OSC2011.DB用資料

オープンソースカンファレンス2011 DBOSSDB MySQLセッションでgroongaストレージエンジンについて紹介してきました。

すべてのMySQLユーザーに高速な全文検索を!

内容はgroongaストレージエンジンが得意なシチュエーションについてベンチマークデータを紹介するというものです。どういうときにgroongaストレージエンジンが高速に動作するかがわかります。

groongaストレージエンジンが得意なシチュエーション

groongaストレージエンジンは以下のような処理が得意です。

  • 全文検索
  • 位置情報検索
  • リアルタイム更新

groongaストレージエンジンの性能特性を紹介するためにベンチマークデータを紹介しました。ベンチマークはこれらの得意な処理を実行するシチュエーション向けに複数のパターンで行いました。

高速な全文検索

groongaの全文検索処理の性能を示すためにtwitterから取得したデータを利用しました。測定する処理はフレーズ検索です。約100万件のtweetに対して「"facebook saved"」というような2単語でフレーズ検索します。このようなフレーズ検索を1万回(1万パターン)実行するためにかかった時間が縦軸になっており、グラフが短いほど高速に全文検索が実行されていることを示しています。

約100万件のtweetに対して1万回のフレーズ検索にかかった時間

groongaストレージエンジンの方がMySQLの開発版5.6.3-labs-innodb-ftsに含まれるInnoDBの全文検索機能よりも10倍程度高速で、MyISAMよりは2倍程度高速でした。

高速な位置情報検索

groongaの位置情報検索処理の性能を示すために国土交通相の位置参照情報ダウンロードサービスの2010年版のデータを利用しました。測定する処理は「MBRContains(GeomFromText('LineString(139.850124 38.718204, 140.447158 37.817489)'), location)」というように位置情報でレコードを絞り込み、「ORDER BY name」というように住所でソートする検索です。このような検索を千回(千パターン)実行するためにかかった時間が縦軸になっており、グラフが短いほど高速に位置情報検索が実行されていることを示しています。

約1100万件の番地データに対して千回のMBRContains + ORDER BY検索にかかった時間

groongaストレージエンジンのほうがMyISAMよりも40倍程度高速でした。

リアルタイム更新

groongaはリアルタイム更新が得意です。リアルタイムで更新するために以下の2点を重視しています。

  • 高速に更新できること。
  • 検索負荷が高いときでも更新性能が落ちないこと。

まず、更新性能が高いことを確認し、次に検索負荷が高いときの更新性能を確認します。

高速な更新

高速に更新できることを示すために、98万件のtweetが登録されたデータベースを用意します。このデータベースに対して、2万件のtweetを登録します。このとき1秒あたりに追加したレコード数が縦軸になっており、グラフが長いほど高速に登録されていることを示しています。

98万件のtweetが保存されている状態で2万件のtweetを追加したときの更新スループット

groongaストレージエンジンのほうがInnoDBよりも3倍程度高速、MyISAMよりも2倍程度高速、Sphinxよりも3倍程度高速でした。

参照ロックフリーな更新

検索負荷が高いときでも更新性能が落ちないことを示すために、98万件のtweetが登録されたデータベースを用意します。このデータベースに対して、検索負荷(クエリ数/秒)を変えながら2万件のtweetを登録します。このグラフはそれぞれのストレージエンジン毎に見ます。横軸が検索負荷を表していて、左側になるほど検索負荷が小さく、右側になるほど検索負荷が高いことを示しています。グラフが水平になっているほど検索負荷が高くなっても更新性能が落ちていないことを示しています。

98万件のtweetが保存されている状態で検索負荷を変えながら2万件のtweetを追加したときの更新スループット

groongaストレージエンジンとInnoDBは検索負荷が高くなっても更新性能はそれほど落ちておらず、MyISAMとSphinxは更新性能が落ちていました。

groongaストレージエンジンの機能制限

このように、高速に動作するgroongaストレージエンジンですが、以下のように機能制限があります。

  • トランザクションをサポートしていない。
  • 位置情報はPOINTしかサポートしていない。

そのため、どのようなケースにでも利用できるわけではありません。しかし、groongaストレージエンジンは他のストレージエンジンと組み合わせて使うことができるため、上記の機能制限の一部を解消することができます。

groongaストレージエンジンを他のストレージエンジンと使う場合は全文検索処理・位置情報検索処理のみをgroongaストレージエンジンが行い、それ以外の処理は連携した他のストレージエンジンが行います。そのため、groongaストレージエンジンとInnoDBを一緒に使うと、トランザクションはInnoDBの機能を用いて、全文検索はgroongaストレージエンジンを用いる、ということができます。この仕組みを使うと「トランザクションをサポートしていない」というgroongaストレージエンジンの機能制限を解消することができます。

groongaストレージエンジンと他のストレージエンジンを組み合わせて使う方法

ただし、更新性能は組み合わせて使うストレージエンジンの性能に依存するため、groongaストレージエンジンの得意な処理である「リアルタイム更新」性能は発揮できません。「高速な全文検索機能」と「高速な位置情報検索機能」のみ利用できます。

InnoDBと組み合わせて利用した場合のベンチマークデータを以下に示します。

まず、全文検索の性能です。

約100万件のtweetに対して1万回のフレーズ検索にかかった時間(InnoDBと組み合わせた場合のデータ付き)

groongaストレージエンジン単体で使った場合とほとんど同じ性能がでています。

次に、位置情報検索の性能です。

約1100万件の番地データに対して千回のMBRContains + ORDER BY検索にかかった時間(InnoDBと組み合わせた場合のデータ付き)

こちらもgroongaストレージエンジン単体で使った場合とほとんど同じ性能がでています。

次に、更新性能です。

98万件のtweetが保存されている状態で2万件のtweetを追加したときの更新スループット(InnoDBと組み合わせた場合のデータ付き)

groongaストレージエンジン単体で使った場合よりも大きく性能が落ちて、組み合わせて使っているInnoDBと同じ程度の性能になっています。

最後に検索負荷が高いときの更新性能です。

98万件のtweetが保存されている状態で検索負荷を変えながら2万件のtweetを追加したときの更新スループット(InnoDBと組み合わせた場合のデータ付き)

InnoDBが検索負荷が高くても更新性能がほとんど落ちないため、groongaストレージエンジンとInnoDBを組み合わせて使った場合でも更新性能がほとんど落ちていません。

まとめ

OSC2011.DBでgroongaストレージエンジンが得意なシチュエーションのベンチマーク結果を紹介してきました。もちろん、groongaストレージエンジンが得意ではないシチュエーションもあり、そのようなケースでは他のストレージエンジンの方が性能がよくなります。groongaストレージエンジンが苦手なケースについては今月末(2011/11/29)開催の全文検索エンジンgroongaを囲む夕べ 2で紹介する予定です。興味のある方はこちらに参加してみてください。すでに定員を超えていますが、前回の全文検索エンジンgroongaを囲む夕べ #1では最終的に35名のキャンセルになっていましたので、今からでもギリギリ参加できるのではないでしょうか。

groongaストレージエンジンのより詳しい情報についてはgroongaストレージエンジンのサイトも参照してください。

タグ: Groonga
2011-11-07

ApacheとPhusion PassengerでデプロイしたWebアプリケーションが不調になる問題と解決

ApacheとPhusion PassengerでWebアプリケーションをデプロイしている際に、たまにシステムが不安定になることがありました。調査したところ、原因はレスポンスの取得が遅いクライアントであるということが分かりました。この問題を発見し、原因を特定し、Phusion Passengerを修正するまでについて、紹介します。

発見の経緯

るりまサーチではApacheとPhusion Passengerを使っているのですが、たまに動作が不安定になることがありました。その不安定の原因を調査することにしました。

原因の特定

調査した結果、遅いクライアントがるりまサーチに接続するのが不安定になるトリガーになっていることが分かりました。

以下に、詳しく説明します。

Phusion Passengerは、遅いクライアントが接続していると、他のリクエストの処理を行いません。その間に、他のクライアントからのリクエストが処理待ちとして大量に溜まります。そして遅いクライアントがレスポンスを受け取り終わると、Phusion Passengerは、一斉に溜まった処理待ちのリクエストを処理し始めるため、システムのリソースを急激に消費します。そして、システムが不安定になります。

解決の方法

この問題の原因を解決するためには、遅いクライアントのリクエストを処理している際にも、他のリクエストの処理ができればいいということになります。

遅いクライアントの時の状況を詳しく説明します。遅いクライアントがレスポンスを受け取るのを待っている間は、サーバー側ではレスポンスの作成の処理は既に完了しているということになります。それにも関わらず他のリクエストの処理を行わないのです。つまり、その間、サーバーは実質的には何も処理を行っていません!

この時間は、無駄な時間と言えます。

なので、この無駄な時間をなくすようにする必要があります。具体的には、Webアプリケーションからの作成されたレスポンスを一旦Phusion Passengerがバッファに保存し、次のリクエストの処理に移ります。遅いクライアントには、そこのバッファから、ゆっくりレスポンスを返せばいいのです。

実は、このような挙動はNginxとPhusion Passengerの構成でデプロイされた場合には実現されています。ですが、ApacheとPhusion Passengerの構成の場合にはそうでは無いのです。

情報の収集

解決の方法が分かったので調べたところ、Phusion Passengerのバグトラッキングシステムにこの問題はすでに登録されていました。

さらにそこには、この問題の解決方法が記載されていました。コメント#3の情報によるとソースコードを書き換えればいいということが分かりました。ソースコードを書き換えることで、Nginxと同じように、Apacheでもレスポンスをバッファに保存する挙動に変更できます。

実際に、るりまサーチで検証したところ、問題は解決されました。遅いクライアントが接続してきた際にも、処理が止まらず、別のリクエストを処理するようになりました。

Phusion Passengerの修正

問題は解決されましたが、この問題を解決するためには、ソースコードを書き換えなければいけません。端的に言って、これは不便です。上のバグレポート内に書いてあるように設定ファイルからレスポンスをバッファに保存するかどうかを切り替えられると便利です。

なので、設定できるようにPhusion Passengerを修正したパッチを作成しました。そしてそのパッチをPhusion Passengerに取り込んでもらうように依頼をしました。

(追記: Fri Nov 25 03:05:28 2011 JSTにPhusion Passengerに無事に取り込んでもらえました。使えるようになるのは、まだリリースされていませんが次のリリースバージョンである、3.0.10からの予定です)

(追記: November 28th, 2011にこの修正が取り込まれたPhusion Passengerの3.0.11がリリースされました(3.0.10はバグがあったためにスキップされました)。

この修正によって、レスポンスをバッファに保存するかどうかを次のように設定することができるようになります。

<VirtualHost *:80>
  ServerName www.yourhost.com
  DocumentRoot /somewhere/public
  PassengerBufferResponse on
  # PassengerBufferResponse off # レスポンスのバッファへの保存をしたくない場合。
  <Directory /somewhere/public>
    AllowOverride all
    Options -MultiViews
  </Directory>
</VirtualHost>

レスポンスをバッファに保存する副作用

遅いクライアント問題はレスポンスをバッファに保存することで解決できます。半面、レスポンスをバッファに保存すると、次の副作用が発生するので注意が必要です。

  • レスポンスをストリーミングで返せない。
  • レスポンスが非常に大きいとメモリを逼迫する。

どちらも、Phusion Passengerがレスポンスをメモリ上のバッファに一旦保存してからクライアントに返すようになるためです。

まとめ

ApacheとPhusion PassengerでデプロイされたWebアプリケーションが不安定になるという問題を調査し、Phusion Passengerを修正しました。

同じような問題に遭遇している場合には、ぜひ試してみてください。

タグ: Ruby
2011-11-17

いらないキャッシュを消すとRubyスクリプトが速くなる

いらないキャッシュを消すことでRubyスクリプトが倍速で動作するようになった話です。

先日、るりまサーチRackspaceのクラウドサーバー(メモリ1GB)からさくらのVPS 512(メモリ512MB)に移行しました。理由はRackspaceのサーバーが遠くにあるのでレスポンスがもっさりするからです。最速検索がウリなのにこれでは遅いのでないかと誤解されてしまいます。さくらのVPSにしたら近くにあるためサクサクとレスポンスが返ってくるようになりました。しかも、リソース(主にメモリ量)はスケールダウンしているのにです。

るりまサーチはバックエンドの全文検索エンジンgroongaが高速に動作するのとそれほどアクセスがない(!)ため、CPUやI/Oがネックになることはありません。それよりも、ほとんどメモリを搭載していないマシンで動かしているため、ボトルネックになるとすればメモリです。実メモリ以上にメモリを使おうとしてスワップを使い始めると遅くなります。

るりまサーチに一番負荷がかかるのは1日1回のるりまデータの更新です。もう少し言うとBitClustでHTMLを生成する処理が一番負荷が高いのです。このとき、BitClustのHTML生成プロセスはCPUを100%使い*1、500MB程度のメモリを使用します*2。Rackspaceのときのように1GBのメモリが載っているサーバーであれば耐えられますが、さくらのVPS 512のように512MBしかメモリが載っていないサーバーでは致命的です。スワップを使いはじめてI/O待ちが多発し、システムの応答性が著しく悪化します。

ということで、BitClustがHTMLを生成するときのメモリ使用量を改善したのですが、そのときに2倍くらい高速に動作するようになりました。その高速化の方法を一言でいうと「いらないキャッシュを消す」です。具体的な変更点でいうとr4873(4行)とr4874(1行)です。

いらないキャッシュを消して高速化

キャッシュはメモリを通常より使って処理時間を短くする方法なので、キャッシュを消すことでメモリ使用量が減るのは納得できます。しかし、処理時間も短くなるのは意外ですよね。

なお、メモリ使用量と実行時間は以下のようになりました。Ruby 1.8.7の場合も1.9.3の場合もメモリ使用量は半分以下、実行時間はほぼ半分になっています。

RSSVSZ実行時間
変更前(Ruby 1.9.3)187MB237MB55秒
変更後(Ruby 1.9.3)84MB134MB38秒
変更前(Ruby 1.8.7)525MB558MB1分52秒
変更後(Ruby 1.8.7)132MB165MB59秒

メモリ使用量が減って速くなった場合はだいたいGCに関係していると相場が決まっています。では、こんなときはどうやって確認したらよいでしょうか。Ruby 1.9.2以降にはGC::Profilerという便利なモジュールが追加されているのでこれを使います。

GCがどんな感じで実行されているかを調べたい処理の前でGC::Profiler.enableをし、処理を実行した後にGC::Profiler.reportで結果を表示します。

1
2
3
GC::Profiler.enable
# ... GCについて調べたい処理 ...
GC::Profiler.report

GC::Profiler.reportは以下のようにGCの統計結果を表示してくれます。

GC 445 invokes.
Index    Invoke Time(sec)       Use Size(byte)     Total Size(byte)         Total Object                    GC Time(ms)
    1               0.092               964640              2257680                56442         0.00000000000000000000
...

まず、いらないキャッシュを消さない場合の結果を見てみましょう。

GC 341 invokes.
Index    Invoke Time(sec)       Use Size(byte)     Total Size(byte)         Total Object                    GC Time(ms)
    1               0.080               964600              2257680                56442         0.00000000000000000000
...
   27               0.348              1893800              4057280               101432         4.00100000000003230838
...
   50               0.764              3283520              7296560               182414         8.00100000000003674927
...
   73               1.588              6627960             13120720               328018        12.00099999999992839150
...
  106               3.720             11035000             23607480               590187        32.00199999999986744115
...
  124               5.532             19510200             42486920              1062173        56.00399999999972067144
...
  151               9.741             36667720             44008400              1100210        96.00600000000092393293
...
  187              16.541             50259800             62249800              1556245       128.00800000000123191057
...
  237              28.470             61845360             72409360              1810234       176.01100000000258205546
...
  328              53.351             63548120             80262160              2006554       148.00900000000183354132

オブジェクト数が増える毎にGCにかかる時間が増えています。

次に、いらないキャッシュを消した場合の結果を見てみましょう。

GC 445 invokes.
Index    Invoke Time(sec)       Use Size(byte)     Total Size(byte)         Total Object                    GC Time(ms)
    1               0.092               964640              2257680                56442         0.00000000000000000000
...
   27               0.376              1893800              4057280               101432         8.00099999999997990585
...
   50               0.824              3283520              7296560               182414        12.00100000000004030198
...
   73               1.644              6627400             13120720               328018        12.00099999999992839150
...
  106               3.928              6736400             13120720               328018        20.00100000000015754154
...
  256              14.061             10642920             23607480               590187        32.00199999999853162080
...
  302              18.561             18979640             42486920              1062173        52.00299999999913325155
...
  432              36.330             25030040             42486920              1062173        48.00300000000135014488

トータルのGC回数は増えていますが、オブジェクト数の最大値が増えていかないため、1回のGCにかかる時間が短くなっています。この差が全体の実行時間に効いてきているんですね。

まとめ

速くするためにやみくもにキャッシュしていると、生きているオブジェクト数が多くなるためにGC時間が長くなってしまい、逆に遅くなることがあります。キャッシュしたのに思ったより速くならなかった場合は必要なくなったキャッシュを持ち続けていないか確認し、いらないキャッシュを消すことを試してみましょう。ただし、ある程度のオブジェクト数をキャッシュしていない場合はあまり関係がないでしょう。

*1 2コアあるので1コアがフル稼働してしまってもシステム全体としては問題になりません。

*2 Ruby 1.8.7使用時

タグ: Ruby
2011-11-24

«前月 最新記事 翌月»
タグ:
年・日ごとに見る
2008|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|