memcachedの代わりにMongoDBを使用します。

「memcachedの代わりにMongoDBを使用する」というトピックに関する多くの成功事例があります。 アイデアがうまく機能する幅広いクラスのタスクがあるようです。まず第一に、これらはキャッシュのタグ付けが頻繁に使用されるプロジェクトです。 しかし、試してみると、MongoDBには、最も頻繁に読み取られないキャッシュレコード(LRU-Least Recent Used)から削除する機能が欠けていることがわかります。 合理的な制限内でキャッシュサイズを維持する方法は? ところで、LRUはmemcachedの「馬」です。 キャッシュがオーバーフローすることを心配することなく、memcachedに書き込むことができます。 しかし、MongoDBはどうですか?



これを考えて、Pythonで小さなユーティリティCacheLRUdを作成しました(GitHubに投稿)。 これは、さまざまなDBMS(もちろん、主にMongoDB)のレコードのLRU削除をサポートするためのデーモンです。 そのようなデーモンのファーム(各MongoDBレプリカに1つ)は、読み取り頻度が最も低いレコードを定期的に削除することにより、コレクションのサイズを監視します。 特定のキャッシュエントリの読み取りの事実の追跡は、UDPベースのプロトコルを使用して分散化されます(単一障害点なし)。マスターベースが別のデータセンターにある場合)。 以下の詳細をお読みください。



しかし、なぜですか?


memcachedをMongoDBに置き換える必要があるのはなぜですか? それを理解してみましょう。 「キャッシュ」の概念には、2種類の使用方法があります。



  1. キャッシュは、管理されなくなったデータベース(または他のサブシステム)の負荷を減らすために使用されます。 たとえば、リソースを読み取るために毎秒100リクエストがあるとします。 キャッシュを有効にし、短いキャッシュエージングタイム(1秒など)を設定することにより、データベースの負荷を100倍削減します。現在、100のクエリのうち1つだけがDBMSに到達しています。 また、ユーザーに古いデータが表示されることを恐れる必要はほとんどありません。結局、エージングタイムは非常に短いからです。
  2. 別のタイプのキャッシュがあります。これは、ページの静的な部分(またはページ全体)のキャッシュであり、ページ(まれにアクセスするページを含む)の形成にかかる時間を短縮するために使用されます。 最初のキャッシュとは異なり、キャッシュされたレコードの有効期間が長い(数時間または数日)ため、質問全体が発生します。つまり、キャッシュに最新のデータが含まれていることを保証する方法、それをクリーンアップする方法は? このために、 タグが使用されます。一部の大きなリソースXに関連するキャッシュ内の各データには、特定のタグがタグ付けされます。 リソースXを変更するときは、コマンド「clear tag X」を指定します。


最初のユースケースでは、memcachedより優れたものは発明されていないようです。 しかし、2番目のmemcachedスリップについては、ここで「memcachedの代わりにMongoDB」というアイデアが役立つかもしれません。 キャッシュが次の場合は、おそらくこれがあなたのケースです:





MongoDBと、自動フェールオーバーウィザードを使用した複製(後者が「死んだ」ときにレプリカをマスターに変える)は、特定のタグのクリーニングの信頼性を保証できます。 Memcachedには問題があります。memcachedサーバーは互いに独立しており、タグを削除するには、クリーンアップコマンドを使用して各サーバーに「移動」する必要があります。 しかし、その時点でmemcachedサーバーの1つが利用できない場合はどうでしょうか? 彼はクリーンアップコマンドを「失い」、古いデータを返し始めます。 MongoDBはこの問題を解決します。



そして最後に、MongoDBは読み取り操作が非常に高速です。これは、接続とメモリマップファイルの操作にイベント指向のメカニズムを使用しているためです。 読み取りは、ディスクからではなく、十分な量のRAMから直接行われます。 (多くの人は、MongoDBはmemcachedと同じくらい速いと書いていますが、私はそうは思いません。ただ、ネットワークの遅延の中で大きな差をつけた彼らの違いがtheれているだけです。)



あまりロードされていないプロジェクトでCacheLRUdが動作した結果は次のようになります。 キャッシュを含むコレクションのサイズは、設定で指定された1Gレベルで実際に一定に保たれていることがわかります。







CacheLRUdをインストールする


##各MongoDBノードにサービスをインストールします。

cd / opt

git clone git@github.com:DmitryKoterov / cachelrud.git

ln -s /opt/cachelrud/bin/cachelrud.init /etc/init.d/cachelrud



##設定:

cp /opt/cachelrud/cachelrud.conf /etc/cachelrud.conf#そして編集



## RHEL(RedHat、CentOS)の場合:

chkconfig --cachelrudを追加

chkconfig cachelrud on



## ...またはDebian / Ubuntuの場合:

update-rc.d cachelrudのデフォルト



悪魔の仕組み


奇跡はありません。アプリケーションは、CacheLRUdデーモン(デーモンファーム)にどのキャッシュエントリを読み込むかを通知する必要があります。 アプリケーションは、明らかに、これを同期モードで実行できません(たとえば、MongoDBマスターデータベースのキャッシュドキュメントのlast_read_atフィールドを更新します)。 TCPプロトコルを使用します。これは、通信が不安定な場合にクライアントのタイムアウトと「フリーズ」を引き起こす恐れがあります。c)読み取りごとに書き込む価値はありません。これは分散システムでは機能しません。



UDPプロトコルは、問題を解決するために使用されます。アプリケーションは、最近読み取られたキーのリストを含むUDPパケットを1つまたは別のCacheLRUdデーモンに送信します。 どれ-負荷に応じて、自分で決めることができます:





詳細はドキュメントに記載されています



他に便利なもの


CacheLRUdWrapper :これは、PHPのアプリケーションコードからCacheLRUdと通信するためのシンプルなクラスで、標準のZend_Cache_Backendをラップします(ただし、このクラスはZend Framework 1用です。ZF2または他の言語用に書き換える場合は、リクエストをプルすることができます)。



Zend_Cache_Backend_Mongo :これは、隣接するGitHubリポジトリからのMongoDBのZend_Cache_Bachend実装です。 CacheLRUdWrapperでこのクラスのオブジェクトをラップし、ZF1のスタイルでMongoDBのLRUキャッシュを操作するためのインターフェイスを取得します。



 $ collection = $ mongoClient-> yourDatabase-> cacheCollection;
 $ collection-> w = 0;
 $ collection-> setReadPreference(MongoClient :: RP_NEAREST);  //マスターからの読み取りも許可します
 $ primaryHost = null;
 foreach($ mongoClient-> getConnections()as $ info){
     if(in_array($ info ['connection'] ['connection_type_desc']、array( "STANDALONE"、 "PRIMARY"))){
         $ primaryHost = $ info ['server'] ['host'];
     }
 }
 $ backend = new Zend_Cache_Backend_Mongo(array( 'collection' => $ collection));
 if($ primaryHost){
     //プライマリがあります(フェイルオーバーが進行中でないなど)-それを使用します。
     $ backend = new Zend_Cache_Backend_CacheLRUdWrapper(
         $バックエンド
         $ collection-> getName()、
         $ primaryHost、
        ヌル
        配列($ yourLoggerClass、 'yourLoggerFunctionName')
     );
 }
 //この行の下で$バックエンドを使用できます。


コメントで共有してください:あなたはこのすべてについてどう思いますか?



All Articles