-
Notifications
You must be signed in to change notification settings - Fork 9
Advanced memcache
多くの製品版のウェブサイトで主力となるソリューションであるにも関わらず、
Memcacheはしばしば完全な潜在能力を引き出されないまま使われていることがあります。
ほとんどの開発者はget
,set
,そしてdelete
のオペレーションについてしか知りません。
しかしながら、Memcacheは開発者により進んだアプリケーションをより少ないコードと
更なるパフォーマンス改善とともに構築することを助ける事が出来る、多くのオペレーションセット
を持っています。
この記事では一連の現実世界のユースケースとともに、より使いやすい、進んだMemcacheのオペレーション についての概要に触れ、アプリケーションの実装とパフォーマンスに対する影響を 見せたいと思います。
この記事は、以下の物を持っている事を仮定しています:
- Herokuアカウント。登録は無料ですぐに 行えます
- インストールされたHeroku Toolbelt
- Memcacheに関する基礎知識
以下のアプリケーション をHeroku上に、MemCachierアドオン と、進んだMemcacheのオペレーションが自分自身で実行できるようなコマンドラインが 完全にそろった状態になるように準備してください。直接コマンドを入力することは、 これからの説明を読んで、シンタックスを括弧な物にし、よりあなたのMemcacheに対する親和性が 高まることへ取って代わるでしょう。
この[ワンクリック Memcache 環境設定](http://cloner.heroku.com/apps/memcache-rails-sandbox)を つかって、実際のMemcacheのHeroku上でのオペレーションを試してみてください。
今、あなたのHerokuアカウントにデプロイされているテストアプリケーションのコピーを持っているはずです。
あなたのアプリケーションのURLをメモしてください。http://serene-mesa-2821.herokuapp.com/
の用な形をしていると思います、serene-mesa-2821
はHeroku上でのあなたのアプリケーションの名前です。
シェルでのやり取りを確立するために、この名前が必要になってきます。
あなたのターミナルからheroku run console
とあなたのアプリケーション名を使って、
Herokuで実行中のアプリケーションのインスタンスに接続します。Memcacheのクライアント
のライブラリをロードして、set
やget
の基本コマンドを実行して設定が行われたか
確認してください。
:::ruby
$ heroku run console -a app-name
irb> cache = Dalli::Client.new
irb> cache.set("foo", "bar")
=> true
irb> cache.get("foo")
=> "bar"
この例では、インタラクティブなRubyのシェルを、Dalliクライアントと共に使い、Memcacheを 読み込み、やり取りを行います。これは単にデモの目的であって、多くの言語のmemcacheドライバは 似たようなコマンドをサポートしているはずです。
このガイドのこれからは、、あなたがこのシェルを実行していて、クライアントライブラリが ロードされていることを前提とします。
Memcacheを使っているときに最もチャレンジングなことは、きれいなコードを書いている段階で キャッシュの腐敗を防ぐ事です。ほとんどの開発者はMemcacheにデータを保存し、 それに変更が合った場合に更新したり削除したりします。この戦略はとても早く散らかる傾向にあります。 Memcacheのコードはアプリケーション上のあらゆるところで一杯になります。 RailsのSweepers はこの問題に対して役立ちますが、他の言語やフレームワークは似たような代替案がありません。
コードが複雑にならないようにするための一つのシンプルな戦略は、データをMemcacheに追加するときに 有効期限を設けることです。有効期限付きのデータは自動的に期限か来ると破棄されます。 ほとんどのアプリケーションは、静的なアセット、ヘッダ、フッタ、ブログのポストなどの ほとんど変更しない内容について時間ベースのキャッシュ有効期限によって利点を得る事が出来ます。
サンドボックスのシェルでは、以下のコマンドを実行する事で10秒後に有効期限がくるように設定できます。
:::ruby
irb> cache.set("expires", "bar", ttl=10.seconds)
irb> cache.get("expires")
=> "bar"
.. wait 10 seconds ..
irb> cache.get("expires")
=> nil
与えられた内容に対して失効させるために明示的なアクションが
必要ないのがわかると思います。ttl
の値の分だけ時間が過ぎたあと、
そのキーのget
の値は単にnilを返すようになります。
有効期限を30日かそれ以上で秒数で指定した場合、Memcacheは有効期限を Unixエポック時間に変換し、絶対時間として扱います。 気をつけてください、40日を秒数で指定する事は 有効期限を1970年に設定する事に なってしまい、不明な結果を生成する事になります。
開発者はしばしばキャッシュの戦略を、新しいキャッシュのコードを書く度に
何度か変更することがあります。頻繁にキャッシュの戦略を変更する事は、
キャッシュを汚し、デバッグを困難な物にします。開発中にキャッシュの
戦略が変更される場合はいつでも、キャッシュの全ての値をクリアするために
flush
コマンドが実行されるべきです。
サンドボックスのコンソールの中で、flushを実験するために、以下のコマンドを します :
:::ruby
irb> cache.set("foo", "bar")
irb> cache.get("foo")
=> "bar"
irb> cache.flush
irb> cache.get("foo")
=> nil
flush
はまた、プロダクション環境にデプロイするときも使われます。しかし気をつけてください。
アプリケーションがキャッシュフラッシュに耐えられないかもしれません。
大きいキャッシュと多くのトラフィックがあるアプリケーションでは、プロダクション環境で
はフラッシュを実行しない、もしくはトラフィックがキャッシュなしでもコントロール可能な
くらい少ない状態の時に実行するようにしましょう。
MemCachier Heroku アドオンは フラッシュが実行できる Web のダッシュボードを持っています。あなたのHerokuのダッシュボードで アドオンをクリックするか、`heroku addons:open memcachier`をコマンドライン から実行する事で MemCachier のダッシュボードにアクセスできます。
Memcacheに保持されている軽量なカウンタは、あなたのアプリケーションの パフォーマンスを下げる事なく、あるイベントについての発生回数を細くするのに 役立つでしょう。カウンターはデバッグ、プロファイリング、そしてトラッキングに 使われます。
例えば、サードパーティ製のAPIに頼っているアプリケーションの場合、何回その APIが使えなかったり、悪いデータを返しているのかを知りたい事があります。 Memcacheのカウンターは、ページの読み込みにほとんど影響なく、データベースも 追加での読み込みを行わないため、理想的な解決策と言えるでしょう。
サンドボックスコンソールでは、以下のコマンドでincr
(increment)とdecr
(decrement)
の実験ができます :
:::ruby
irb> cache.incr("my_counter", 1, nil, 0)
irb> cache.get("my_counter")
=> "0"
irb> cache.incr("my_counter")
irb> cache.get("my_counter")
=> "1"
irb> cache.incr("my_counter", amt=5)
irb> cache.get("my_counter")
=> "6"
irb> cache.decr("my_counter")
irb> cache.get("my_counter")
=> "5"
incr
とdecr
は手動のget
やset
による変更より使われるべきです。なぜなら、
incr
やdecr
はスレッドセーフであり、TCPのRTTがより短く設定されているためです。
incr
の属性については、Dalliのドキュメント.
を確認してください。
Memcache内のシンプルなリストの保持は、非正規化されている関係を維持するのに
役立ちます。例えば、E-コマースのサイトは最近の購入履歴の小さなテーブルが
必要になるでしょう。シリアライズされたリストをMemcache内に保持し、
新しい購入が合った場合に再計算するよりも、append
とprepend
は
データベースのクエリをさけながら、非正規化されたデータを保持するのに
使う事が出来ます。
顧客の最近の購入履歴のリストを更新するのに、かつてのset
のオペレーション
を使うかわりに :
:::ruby
cache.set("user_1_recent_purchases", Purchases.recent)
prepend
をただ新しいデータを使うだけで、同じ効果を得ながら使う事が出来ます:
:::ruby
cache.prepend("user_1_recent_purchases", product.name + "||")
このやり方は、Memcacheの占有領域をより小さくし、データベースのクエリが すべてのユーザの最近の購入履歴を取得することを防ぎます。
サンドボックスコンソール内で、append
とprepend
を以下のコマンドを実行する事で
実験できます。:
:::ruby
irb> cache.set("my_list", "foo", ttl=0, options={:raw => true})
irb> cache.get("my_list")
=> "foo"
irb> cache.prepend("my_list", "bar||")
irb> cache.get("my_list")
=> "bar||foo"
irb> cache.append("my_list", "||baz")
irb> cache.get("my_list")
=> "bar||foo||baz"
この例は、任意の区切り文字(`||`)を使っています。この区切り文字は、 リストのアイテムが区切り文字を決して含まないように、リスト内の 予想されるそれぞれのアイテムの値から決定されるべきです。 加えて、文字列の長さが決まった状態の実装がアプリケーションに されたほうが良いでしょう。さらなるMemcacheのリストに関する情報は [こちら](http://code.google.com/p/Memcached/wiki/NewProgrammingTricks#Managing_lists_with_append_/_prepend) をご覧ください。
append
とprepend
はget
とset
での手動の変更よりも使われるべきでしょう。
なぜならappend
とprepend
はスレッドセーフだからです。
Memcacheは最大で1MBしかサポートされていません。許されている値の大きさよりも 大きく育つようなリストの作成には気をつけてください。Dalliを含む、 いくつかのクライアントは圧縮をサポートしています。Dalliの場合は、 Dalliに接続時、`:compress`オプションを`true`に設定します。
Memcache内で保存されているシンプルなJSONハッシュは、頻繁にサクセスされる 設定の維持に役立ちます。例えば、ウェブサイトが現在どの機能が有効になっているのかを トラッキンングしたい場合、またはどちらのABテストが実行されているかなどです。 しばしばこれらの設定は、便利なように一緒にJSONハッシュの中に 保持されています。
append
とprepend
はJSONハッシュとの関連はありません、なぜならハッシュは
整列されていないからです。そしてset
は、二つのJSONハッシュに対する変更が同時に発生した場合、
一つの変更が失われるかもしれないからです。
比較(Compare)と取り替え(Swap)、つまりcas
というオペレーションを使って、
元の値と新しい値の比較を行い、古い値が別の人によって変更されていない場合に限り
値を取り替えます。言い換えるならば、cas
はスレッドセーフな
set
です。
サンドボックスのコンソール内では、以下のコマンドを実行するとcas
の実験ができます :
この例の中の`cache.cas`はブロックを必要としていますが、これは現在使っている RubyのMemcacheクライアントであるDalliが必要としています。他のクライアント はこの方法に従っているとは限りません。
:::ruby
irb> cache.set("my_json", "{}")
irb> cache.get("my_json")
=> "{}"
irb> cache.cas("my_json") { {key: "val"}.to_json }
irb> cache.get("my_json")
=> "{\"key\":\"val\"}"
Memcacheプロトコルは直接`cas`オペレーションを実装しているわけではありません。
そのかわり、バージョン番号付きの`set`をサポートしています。`set`が、
もしバージョン番号がMemcacheに保持されているキーのバージョンと一致しているときに
限り実行されます。`cas`が実行されているのに対してバージョンを直接プロトコル内で
使う事は、[ABA問題](http://en.wikipedia.org/wiki/ABA_problem)の
対策になります。
いくつかのクライアントは`cas`を`set`のバージョンを渡しながら手動で実装しなければ
行けなくなるでしょう。
ここで議論されているそれぞれのMemcacheオペレーションは、いかにクイックリファレンス として並べています。APIは詳細まで書いていません。なぜなら、それぞれのクライアントと 言語は別のAPIを持つだろうためです。
オペレーション | 説明 |
---|---|
有効期限付きset | キーとバリューに秒で書かれた有効期限を儲けます。 一度キーの有効期限が失効されたら、キャッシュから削除されます。30日間までの有効期限は現在からの時間の差で計算され、 逆に30日間以上の有効期限は絶対的なUnix時間として計算されます。 |
flush | キャッシュから全てのデータを削除します。プロダクション環境でのこのコマンドの使用は注意してください。-- プロダクション環境のアプリケーションでは、もしキャッシュがない事がDBに対して負荷をかける場合に、ダウンタイムに陥る可能性があります。 |
incr | 整数の値を指定した量だけ増やします。スレッドセーフです。 |
decr | 整数の値を指定した量だけ減らします。スレッドセーフです |
append | 値の最後に付加します. 元の値はクライアントで生のバイト値として保持されている必要があります。スレッドセーフです。 |
prepend | 値の始めに表れます. 元の値はクライアントで生のバイト値として保持されている必要があります。スレッドセーフです。 |
cas (もしくはバージョンを使ったset) | 他のプロセスによって値が変更されていなかった場合のみ、新しい値をセットします。スレッドセーフです。 |