Redis布教活動報告 ISUCON 編

最近 Test::RedisServer とかもろもろつくっててばれてるかもしれませんが、だいぶ Redis 期にありまして、最近の趣味は?っていう問いにはだいたいRedisのソースを読むことですってなくらいなのですが、 memcached とかシンプルな KVS と比べるとだいぶ機能が豊富なので使い方を迷ったりとかそういう事例もあり、周りにもう少し使える人を増やさなければ僕の書いたコードが属人化しててつらい感じになるなーっていうわけで、 布教活動をおこなっておりまして、その一環として ISUCON2 に参加してきましたのでその報告です。

livedoor Techブログ : #isucon2 リアルタイムフォトレポート 更新終了

前回の優勝チームに混ぜてもらった感じでだいぶついてる感じもしますが、見事連覇を果たせ、懇親会でも redis redis と連呼してきたのでだいぶ興味持った方も多いのではないでしょうか!

そんな布教活動視点から ISUCON を振り返ってみたいと思います。

Redis がつかえるかどうか

開始直後はチーム間でのコンフリクトをさけるために git push で rev サーバーに反映ってのだけは設定しましたが、そのあとはすぐボトルネックそうなところで Redis に置き換えられる物はないか探していました。

すぐに購入トランザクションが見つかります。Redis には Set 型という集合を表す型があり、それを使うと集合のなかから排他的に一つランダムでとりだして消すというのをとても効率的に実装することができます。 ISUCON ではこのランダム条件が(チェッカーがバグでスルーしていたみたいですけど)なかなかきびしかったところだと思いますが、そこもするっと解決です。 なお、順番に購入という条件であれば双方向リストの List 型を使うのが正解でしょう。こちらも先頭or末尾からの取得&削除を排他的に行うことができます。

ついでに購入履歴も Redis にもたせておけばそこから、サイドバー(recent_sold)とCSV吐き出しもどちらも行えそうだなと思いました。

ここまででとりあえずこの購入トランザクションだけでも Redis にうつしたら DB ロックする必要がないのでだいぶ良さそうだなと思い、redisブランチやりますとチームに宣言します。

書き換えの流れ

そしてもりもり書き換えました。

  • POST /admin でMySQLにデータを流し込んだあとに stock のデータをすべて redis にコピーする。(stock:{variation_id} というキーのSet)
  • POST /buy で対象の Set から一つ取り出し、order_history という List にアーティスト名などの付属情報込みな辞書情報をPush。
  • サイドバーは order_history から最新10件を取得するように変更
  • CSV書き出しは order_history をまるっと書き出す感じ
  • その他もろもろテンプレに渡してるデータをよしなに変更

といった感じですが、アプリのテストコードがないのでだいぶ変更が手探りな感じでつらかったです。とくにテンプレ周り。なおしてベンチ止まって、ベンチのエラー見て修正という…。 そんなつらいかんじや実際の作業内容は redis ブランチのコミットログを参照いただくと分かるかなと思います。

他になんかあるか?

コード見てるだけだとわからなかったので fujiwara さんにボトルネック探してもらいつつ、自分はとくにやることもないので非同期の実装を模索しておりました。 この時点ではforkモデルの限界なんじゃなかろうかと思っていて非同期にしてベンチ取ってみるか、みたいな感じでしたが、Perlで非同期にすると書き直しになるし、nodeの実装いじっててもうまく動かなくてすぐにあきらめてしまったんですが、それは fujiwara さんがテンプレのレンダリングボトルネックを見つけてくれて、そちらを直したら今までのアプリでも十分スループット(reverse proxy <-> app 間)が出ており、非同期全然意味ありませんでした。

まぁそうだよね、IOウェイトが問題になるような場合以外は非同期意味ないなーとそのとき思った次第です。

テンプレキャッシュ

組長が見つけてくれたチケットの座席表のレンダリングのボトルネック。これも Redis でキャッシュするようにしました。 Redis は単純なKVSとしてみても十分高速なのでこういう用途にも使えます。

データ永続化

これは本家ドキュメントよくまとまっております。

http://redis.io/topics/persistence

スナップショット(dump)と追記ログ(appendlog - MySQLのbinlog的な)の二種類の永続化方法があり、dumpはバックアップ用、復元をappendlogから、ってのがオススメな方法。

appendlog は基本的にはプロトコルログなんですが、大きくなるとバックグラウンドで最適化されて小さくなります。デフォルトでは fsync が1秒ごとに走るので最悪そこまでのデータは保全される。 書き込み毎に sync って言うのも設定でできますがパフォーマンス的につらいことになるでしょう。

これがISUCONのレギュレーションを満たしているかは定かではありませんがとりあえず fujiwara 組では1秒fsyncなappendlogでデータ永続化をしました。

また、メモリいっぱい使い切っているような大きなデータセットの場合に appendlog するは不安感ありますが、自分もまだ試したことがないので、近いうちに試したいと思っております。

まとめ

あんまりおすすめしまくっていると勘違いしちゃう人もいるかもしれないのでいちおう補足しておきますが、単純に MySQL を Redis に置き換えたら速くなる、っていうことはないです。 MySQLがいいところ、Redisがいいところ、(もちろんその他のツールも)どちらもあるので、 Pros and Cons やトレードオフを考えた上で選択するのが大事です。

僕自身も Redis つかっているといっても運用上のノウハウなどまだまだうちも足りていないのでそこはこれから精進していきたいと思います。

しかし、ISUCON楽しいですね。 #isucon のログによると

11:47 < typester> すでにだいぶたのしい

といっており、ここからずーーーっと終了までたのしかったですね。

あと珈琲ありがとうございました!

番外: Xslate の速さを実感

家帰って他の言語の実装をうごかしてみると、明らかに座席表のレンダリングが遅いんです。ブラウザで見てボトルネックって分かるレベル。 でも、Perl の実装をブラウザで見た人はあのページの遅さはテンプレートエンジンじゃなくてブラウザがレンダリングするのに時間かかってる、くらいにしか感じない気がする。 今回の罠は Xslate の速さと言っても良いのではないでしょうか。

これは gfx++ といわざるを得ませんね!

by typester / at 2012-11-07T08:46:00 / isucon · life · redis / Comment

Test::RedisServer ってのを書いた

最近 Redis ずいてるわけですが、redis-server

$ redis-server /path/to/redis.conf

or

$ cat redis.conf | redis-server -

みたいな形式でしか起動できず、これをがんばって exec してテスト用とかに使うコードを3回くらい書いたところで面倒になってモジュール化しました。

Daisuke Murase / Test-RedisServer - search.cpan.org

基本的に Test::mysqld のパクりなのでサクッとかけました。kazuho++

by typester / at 2012-07-31T00:09:00 / perl · redis / Comment