memcashed,Slab Allocator
memcashed
多くのWebアプリケーションは,RDBMSにデータを格納し,アプリケーションサーバでそのデータを引き出してブラウザ等に表示させている。しかしデータが大量になったり,アクセスが集中すると,RDBMSの負荷があがり,データベースのレスポンスが悪化し,Webサイトの表示が遅延するなど大きな影響がでてしまう。
memcachedは高性能な分散メモリキャッシュサーバである。通常,データベースへの問い合わせ結果を一時的にキャッシュすることで,データベースへのアクセス回数を減らし,動的なウェブアプリケーションの高速化やスケーラビリティの向上のために利用されている。
キャッシュサーバ
キャッシュ サーバとは、インターネット上のWebサイト等で提供されているコンテンツの複製を保存し、ユーザからの要求があった場合に、本来コンテンツを提供すべきサーバ(コンテンツ サーバ)に代わってコンテンツを配信するサーバのことである。
memcachedの特徴
memcachedは非常に高速に動作する分散キャッシュサーバで,特徴としては以下のような点が上げられる。
1.シンプルなプロトコル
2.libeventによるイベントハンドリング
3.内蔵のオンメモリストレージ
4.memcached同士での通信は行わない分散方式
シンプルなプロトコル
memcachedのサーバとクライアントの通信は,XMLなどの複雑なフォーマットは利用せず,シンプルな行ベースのプロトコルによって行われる。行ベースのプロトコルなのでtelnetでmemcachedにデータを保存して,保存したデータを取得することもできる。
$ telnet localhost 11211 Trying 127.0.0.1... Connected to localhost.localdomain (127.0.0.1). Escape character is '^]'. set foo 0 0 3 (保存コマンド) bar (データ) STORED (結果) get foo (取得コマンド) VALUE foo 0 3 (データ) bar (データ)
libeventによるイベントハンドリング
libeventとは,Linuxではepoll,BSD系のOSではkqueueなどのイベントハンドリング機能(特定のイベントに反応して特定の処理を行う機能)をラップして,サーバに対するコネクション数の増加に対してもO(1)のパフォーマンスを発揮する共通仕様で利用できるようにしたライブラリである。memcachedはこのlibevetのライブラリを利用しているので、高いパフォーマンスをLinux,BSD,SolarisなどのOS上で発揮することができる。
内蔵のオンメモリストレージ
memcachedに保存したデータは,パフォーマンスを向上させるように設計されたmemcached内蔵のメモリストレージに貯められる。データはすべてメモリ上にのみ存在するので,memcachedを再起動したり,OSを再起動するとすべてのデータが消えることになる。またメモリが指定された容量に達すると,LRU(Least Recently Used)に基づいて利用されないキャッシュから自動的に削除されていく。memcached自体キャッシュのためのサーバとして設計されているので,データの永続化についてはあまり考慮されていない。
memcached同士での通信は行わない分散方式
memcachedは「分散」キャッシュサーバだが,分散に関しての機能はサーバ側には備わっていない。memcached同士で通信を行って情報をシェアすることはない。どのようにして分散を行うかというと,すべてクライアント側の実装に依存するようになっている。
メモリを整理して再利用するSlab Allocationメカニズム
昨今のmemcachedはデフォルトでSlab Allocatorというメカニズムを使ってメモリの確保・管理を行っている。このメカニズムが登場する以前のメモリ確保の戦略は,単純にすべてのレコードに対してmallocとfreeを行うといったものだった。しがしながら,このアプローチではメモリにフラグメンテーション(断片化)を発生させてしまい,OSのメモリマネージャに負荷をかけ,最悪の場合だとmemcachedのプロセスよりも重くなってしまうという問題が発覚した。この問題を克服するために生まれたのがSlab Allocatorである。
Slab Allocatorは、確保したメモリをあらかじめ決められたクラスサイズに応じた固定長の固まりに分けて,フラグメンテーション問題を完全に克服する。Slab Allocationの仕組みは簡素で,確保したメモリをさまざまなサイズの固まり(chunk)に分けて,同じサイズの固まりをクラス(chunkの集合,またはchunkのサイズを定めるクラス)に整理する。
また,slab allocatorには「確保したメモリは再利用する」という目標もある。したがって,memcachedは一度確保したメモリは解放せず,常にchunkを再利用する。
Slab Allocatorの主な用語
・Page
デフォルトで1MB確保され,Slabに割り当てられるメモリ領域。Slabに割り当てられた後に,slabのサイズに応じたchunkに切り分けられる。
・Chunk
レコードをキャッシュするためのメモリ領域。
・Slab Class
特定のサイズのchunkをまとめるクラス。
Slabにレコードをキャッシュする仕組み
memcachedは受けとったデータのサイズを参照し,データサイズにもっとも適したslabを選ぶ。memcachedはslab内の使用可能なchunkのフリーリストを保持しているので,このリストを基にchunkを選び,そこにキャッシュする。
Slab Allocatorの弱点
Slab Allocatorの開発により,当初の課題であったフラグメンテーション問題は解消されたが,新たなメカニズムにより新しい課題がmemcachedに生まれた。その問題は,固定長なメモリ確保のアプローチにより,確保したメモリを有効活用できないということである。例えば100バイトのデータを128バイトのchunkにキャッシュすると,余りの28バイトが無駄になる。
クライアントが送ってくるデータの共通サイズがあらかじめ解っている,もしくは同じサイズのデータしかキャッシュしないユースケースであれば,そのサイズに適したクラスのリストを使い,無駄を抑えることが可能である。ただし残念なことに,現状ではこのチューニングを行うことはできず,将来の課題として残っている。しかしながら,slab classたちのサイズ差をチューニングすることは可能である。
Growth Factorを使ったチューニング
memcachedはスタートアップ時にGrowth Factorという因子を指定して(-f オプション),slab間のサイズをある程度制御することが可能である。デフォルト値は1.25だが,このオプションが開発される以前は“powers of 2”戦略といって,2が固定だった。
Lazy Expiration
memcachedは内部的にレコードがexpireしたかの監視を行わない。替わりにgetする際にレコードのtimestampを見ることで,そのレコードがexpireしたかをチェックする。このテクニックをlazy(なまけた)expirationと呼ぶ。したがって,memcachedはexpireの監視にCPUタイムを消費しない。