コンピューティングにおいて、Memcached(発音は/mɛmkæʃˈdiː/ mem-cash-DEE ["dee/d" はデーモンを表す] または/ˈmɛmkæʃt/ MEM-cashed)は汎用分散メモリ キャッシュシステムです。これは、データベースや API などの外部データ ソースを読み込む回数を減らすために、データやオブジェクトをRAMにキャッシュすることで、動的なデータベース駆動型Web サイトの速度を向上させるためによく使用されます。Memcached は、 Revised BSD ライセンスの下でライセンスされた、無料のオープンソース ソフトウェアです。[ 2 ] Memcached は、Unix ライクなオペレーティングシステム ( LinuxおよびmacOS ) とMicrosoft Windows上で動作します。libevent ライブラリに依存しています。
Memcached のAPI は、複数のマシンに分散された大きなハッシュ テーブルを提供します。テーブルがいっぱいになると、後続の挿入によって、古いデータが使用頻度の低い順 (LRU) に削除されます。[ 3 ] [ 4 ] Memcached を使用するアプリケーションは通常、データベースなどの低速なバックエンド ストアにフォールバックする前に、リクエストと追加を RAM にレイヤー化します。
Memcachedには、発生する可能性のあるキャッシュミスを追跡する内部メカニズムはありません。ただし、一部のサードパーティ製ユーティリティはこの機能を提供しています。
Memcached は、2003 年 5 月 22 日にBrad Fitzpatrickが自身のウェブサイトLiveJournal用に初めて開発しました。 [ 5 ] [ 6 ]当初はPerlで書かれていましたが、後に当時 LiveJournal に勤務していた Anatoly Vorobey によってCで書き直されました。 [ 7 ] Memcached は現在、YouTube、[ 8 ] Reddit、[ 9 ] Facebook、[ 10 ] [ 11 ] Pinterest、[ 12 ] [ 13 ] Twitter、[ 14 ] Wikipedia、[ 15 ] Method Studios [ 16 ] など、他の多くのシステムで使用されています。Google App Engine、Google Cloud Platform、Microsoft Azure、IBM Bluemix、Amazon Web Servicesも API を介して Memcached サービスを提供しています。[ 17 ] [ 18 ] [ 19 ] [ 20 ]
このシステムはクライアント・サーバーアーキテクチャを採用しています。サーバーはキーと値の連想配列を保持し、クライアントはこの配列にデータを格納し、キーを使ってクエリを実行します。キーは最大250バイト、値は最大1メガバイトまでです。
クライアントはクライアント側のライブラリを使用してサーバーに接続します。サーバーはデフォルトでポート11211 でサービスを公開しています。TCP と UDP の両方がサポートされています。各クライアントはすべてのサーバーを認識しており、サーバー同士は通信しません。クライアントが特定のキーに対応する値を設定または読み取りたい場合、クライアントのライブラリはまずキーのハッシュを計算して使用するサーバーを決定します。これにより、サーバー間でシンプルな形式のシャーディングとスケーラブルな共有なしアーキテクチャが実現します。サーバーはキーの 2 番目のハッシュを計算して、対応する値をどこに保存または読み取るかを決定します。サーバーは値を RAM に保持します (バージョン 1.6.0 以降では、外部ストレージサーバーオプションを使用してディスク上の補助キャッシュにも保持します)。[ 21 ]サーバーの利用可能なメモリまたはディスクが不足すると、最も古い値が破棄されます。したがって、クライアントは Memcached を一時的なキャッシュとして扱う必要があります。必要なときに Memcached に保存されているデータがまだ存在すると想定することはできません。MemcacheDBやCouchbase Serverなどの他のデータベースは、Memcachedプロトコルとの互換性を維持しながら、永続的なストレージを提供します。
すべてのクライアントライブラリが同じハッシュアルゴリズムを使用してサーバーを特定する場合、クライアントは互いのキャッシュされたデータを読み取ることができます。
一般的なデプロイメントでは、複数のサーバーと多数のクライアントがあります。ただし、Memcached を単一のコンピューターで使用し、クライアントとサーバーの両方として同時に動作させることも可能です。ハッシュテーブルのサイズは、多くの場合非常に大きくなります。これは、データセンターのサーバー クラスター内のすべてのサーバーで使用可能なメモリに制限されます。大量の、広範囲のユーザーを対象とした Web パブリッシングが必要な場合、これは数ギガバイトに及ぶ可能性があります。Memcached は、コンテンツへのリクエスト数が多い場合、または特定のコンテンツを生成するコストが高い場合にも同様に価値があります。特にキャッシュの必要性が高いアプリケーションは、組み込みのプロキシを使用して複雑なクライアント サーバー ルートを定義および構成できます。[ 21 ]
Memcachedのほとんどの導入環境は、クライアントがどのサーバーにも自由に接続できる信頼できるネットワーク内です。しかし、Memcachedは信頼できないネットワークや、管理者が接続するクライアントを制御したい場合に導入されることがあります。このような場合、MemcachedはオプションのSASL認証サポートを有効にしてコンパイルできます。SASLサポートにはバイナリプロトコルが必要です。
BlackHat USA 2010でのプレゼンテーションでは、多くの大規模な公開ウェブサイトがMemcachedを検査、分析、取得、およびデータ変更に対して無防備な状態にしていたことが明らかになった。[ 22 ]
信頼できる組織内であっても、memcached のフラットな信頼モデルはセキュリティ上の影響を及ぼす可能性があります。効率的なシンプルさを優先するため、すべての Memcached 操作は平等に扱われます。キャッシュ内のセキュリティレベルの低いエントリへのアクセスを正当な理由で必要とするクライアントは、たとえそれがセキュリティレベルの高いエントリであっても、正当な理由なくキャッシュ内のすべてのエントリにアクセスできるようになります。キャッシュキーが予測、推測、または徹底的な検索によって見つかる場合、そのキャッシュエントリが取得される可能性があります。
大量のWebパブリッシングなどの状況では、設定と読み取りデータを分離する試みが行われる場合があります。外部に公開されているコンテンツサーバーのファームは、公開済みのページまたはページコンポーネントを含むmemcachedへの読み取りアクセス権を持ちますが、書き込みアクセス権はありません。新しいコンテンツが公開された場合(まだmemcachedに登録されていない場合)、代わりに、コンテンツユニットを作成してmemcachedに追加するために、外部からはアクセスできないコンテンツ生成サーバーにリクエストが送信されます。その後、コンテンツサーバーはそれを再度取得して外部に配信します。
2018年2月、CloudFlareは、設定ミスのあるmemcachedサーバーが大規模なDDoS攻撃に使用されたと報告した。 [ 23 ] UDP上のmemcachedプロトコルは、51000を超える巨大な増幅率を持つ。 [ 24 ] DDoS攻撃の被害者にはGitHubが含まれており、 ピーク時の受信トラフィックが1.35 Tbit/sに達した。[ 25 ]
この問題はMemcachedバージョン1.5.6で軽減され、UDPプロトコルがデフォルトで無効になった。[ 26 ]
このページに記載されている機能はすべて擬似コードです。Memcachedの呼び出し方法やプログラミング言語は、使用するAPIによって異なる場合がありますのでご注意ください。
データベースクエリやオブジェクト作成クエリをMemcachedを使用するように変換するのは簡単です。通常、通常のデータベースクエリを使用する場合、サンプルコードは次のようになります。
function get_foo ( int userid ) data = db_select ( "SELECT * FROM users WHERE userid = ?" , userid ) return dataMemcachedへの変換後、同じ呼び出しは次のようになるかもしれません。
function get_foo ( int userid ) /* まずキャッシュを試す */ data = memcached_fetch ( "userrow:" + userid ) if not data /* 見つからない場合:データベースを要求する */ data = db_select ( "SELECT * FROM users WHERE userid = ?" , userid ) /* 次に取得するまでキャッシュに保存する */ memcached_add ( "userrow:" + userid , data ) end戻りデータクライアントはまず、一意のキー「userrow:userid」を持つMemcached値が存在するかどうかを確認します。ここでuseridは数値です。存在しない場合は、通常どおりデータベースから選択し、Memcached APIのadd関数呼び出しを使用して一意のキーを設定します。
しかし、このAPI呼び出しだけを変更した場合、データベースの更新操作後にサーバーが誤ったデータを取得することになり、Memcachedのデータの「ビュー」が古くなってしまいます。そのため、「追加」呼び出しを作成するだけでなく、Memcachedのset関数を使用した更新呼び出しも必要になります。
function update_foo ( int userid , string dbUpdateString ) /* 最初のデータベース更新 */ result = db_execute ( dbUpdateString ) if result /* データベース更新が成功した場合: キャッシュに保存するデータを取得 */ data = db_select ( "SELECT * FROM users WHERE userid = ?" , userid ) /* 前の行は、data = createDataFromDBString(dbUpdateString) のようにも記述できます */ /* 次に取得するまでキャッシュに保存 */ memcached_set ( "userrow:" + userid , data )この呼び出しは、データベースクエリが成功した場合、現在キャッシュされているデータをデータベース内の新しいデータに合わせて更新します。別の方法としては、Memcachedのdelete関数を使用してキャッシュを無効化し、以降のフェッチでキャッシュミスが発生するようにする方法があります。データベースレコードが削除された場合にも、正しいキャッシュまたは不完全なキャッシュを維持するために、同様の処理が必要になります。
キャッシュ無効化の別の方法として、合意されたキャッシュエントリに乱数を格納し、その乱数を特定の種類のエントリを格納するために使用されるすべてのキーに組み込む方法があります。このようなエントリをすべて一度に無効化するには、乱数を変更します。既存のエントリ(古い乱数を使用して格納されていたもの)は参照されなくなり、最終的に期限切れになるか、再利用されます。
function store_xyz_entry ( int key , string value ) /* 乱数を取得します。まだ存在しない場合はゼロを使用します。 * ここで使用するキー名は任意です。 */ seed = memcached_fetch ( ":xyz_seed:" ) if not seed seed = 0 /* エントリを保存するために使用されるキーを作成し、それを保存します。 * ここで使用するキー名も任意です。「シード」とユーザーの「キー」は、 構築された hashKey 文字列の別々の部分として保存されることに注意してください。 ":xyz_data:(seed):(key)." * これは必須ではありませんが、推奨されます。 */ string hashKey = sprintf ( ":xyz_data:%d:%d" , seed , key ) memcached_set ( hashKey , value )/* 省略されている「fetch_entry」は、上記と全く同じロジックに従います。 */function invalidate_xyz_cache () existing_seed = memcached_fetch ( ":xyz_seed:" ) /* 別の乱数シードを生成します */ do seed = rand () until seed != existing_seed /* 合意された場所に保存します。今後のすべてのリクエストはこの番号を使用します。 * したがって、既存のエントリはすべて参照されなくなり、最終的に期限切れになります。 */ memcached_set ( ":xyz_seed:" , seed )