ロングキャッシュ

ロングキャッシュはリスクの多いキャッシュです。
中略
このようにロングキャッシュはいろいろ難しいことが多く、
まだこなれたものになっていません。
成熟しているRDBMSのキャッシュに任すのが無難で現実的でしょう。

とても現実的で妥当だと思います.とはいえ,ちょっと夢がない気も.(^^;
ということで,遠くない未来に向けてちょっとロングキャッシュへの期待を書いてみるテスト.


ここでいわれているロングキャッシュというのは,トランザクションをまたがって有効なキャッシュのことです.このようなキャッシュは,たいていの場合複数のプロセス (JVM) にまたがって有効なキャッシュを保持する必要があります.
そのため,これまで私の日記では「トランザクショナルな分散キャッシュ」という表現を使っていましたが,表しているものは同じだと思います.


そのロングキャッシュですが,指摘されているように DBMS の実装と重なる部分が非常に大きいです.そのため,ロングキャッシュは RDBMS に任せればいいということになるわけですが...
しかしながら,O/R マッピングで使用されるロングキャッシュと,RDBMS が保持するキャッシュとでは,キャッシングする対象が異なります.

RDBMS
オブジェクトの素になるデータをキャッシュする.
ロングキャッシュ
インスタンス化されたオブジェクトをキャッシュする.

ロングキャッシュの方が,より水準の高いところでキャッシングするわけです.よって,効果的に使えるのであれば,ロングキャッシュは結構魅力的な存在になるのではないでしょうか?


しかし,ロングキャッシュを効果的に使うのは決して簡単ではありません.
90年代に期待されながら,結局のところ普及したとは言い難い ODMBS (オブジェクトデータベース管理システム),特にその代表的な製品であった ObjectStore は,実はそのロングキャッシュそのものといえるアーキテクチャ (Cache-Forward Architecture) を採用することで,RDBMS よりも 1000 倍 (700 倍だったかな?) 高速であるなどと宣伝していました.しかしながら,売り文句通りの性能を発揮できるケースはほとんどなかったように思います.
ということで,久しぶりに ObjectStore キャッシュとロックについて書いてみるテスト.なお,私の ObjectStore に関する知識は主に V5.x のものなので,最新版とは異なっている部分もあるかもしれないのでご注意願います.根幹的な部分なので,大きく変わっていないとは思いますけれど.


ObjectStore は,極度にクライアント (ObjectStore から見たクライアント,通常はアプリケーションサーバ) サイドのキャッシュに依存したアーキテクチャになっています.
クライアントは,オブジェクトにアクセスすると必要に応じてサーバからオブジェクトを取得します.実際には,オブジェクトが配置された「ページ」を取得するのですが,ここでは無視.(^^;
その際,クライアントはサーバから共有ロックを獲得します (MVCC についても無視!).ObjectStore がおもしろいのは,トランザクションがコミットされても,そのロックを解放しないということです.キャッシュ上のオブジェクト (が載ったページ) も破棄しません.
このため,次のトランザクションで同じオブジェクトが必要になった場合,アプリケーションはサーバと通信することなく,即座にそのオブジェクトを使用することができます.共有ロックを持っているということは,そのオブジェクトは今も有効 (最新) ということが保証できるからです.
別のプロセスがオブジェクトを更新しようとすると,今度はサーバから排他ロックを獲得しようとします.この場合,サーバは該当オブジェクト (が載ったページ) の共有ロックを保持しているプロセスに対して,キャッシュを破棄して共有ロックを解放するように通知します.該当プロセスがトランザクション中でなければ,共有ロックは即座に解放されます.トランザクション中の場合は,トランザクションの終了を待って共有ロックが解放されます.共有ロックを持つ全てのプロセスがロックを解放すると,サーバは排他ロックを要求したプロセスに与えます.
更新トランザクションがコミットした後も,やはりそのプロセスは排他ロックを解放しません.このため,次のトランザクションで同じオブジェクトにアクセスすると,やはりサーバと通信することなく,オブジェクトを参照および更新することができます.
このようなアーキテクチャであるため,更新が少ないオブジェクトへの参照はきわめて効率的です.また,複数のプロセスで参照されていなければ,更新もまた十分に効率的です.しかしながら,複数のプロセスから参照されているオブジェクトの更新などは,きわめて非効率的です.
まとめると,次のようになります.

 複数プロセスからアクセスされない複数プロセスからアクセスされる
滅多に更新されない効果的効果的
頻繁に更新される効果的効果的でない


これはおそらく,多くのロングキャッシュの実装もある程度似たような傾向になると思われます.
つまり,2×2 の 4 象限のうち,3つのエリアでは効果が期待できるものの,右下のエリアではむしろパフォーマンスの足を引っ張ることになりそうということです.
足を引っ張る程度は,実装によって異なるかもしれません.例えば,オブジェクトが更新される場合に他のプロセスのキャッシュを無効にするのか,レプリケーションするのかなど.
実はこれ,Oracle でも似たようなもので,8i のパラレル・サーバーから 9i の RAC への進化 (キャッシュ・フュージョン) というのは,この部分の強化だったりするわけです.
ObjectStore などは,上記の表の右下のエリアのデメリットが,その他のエリアのメリットを上回ってしまうケースが多かったと私は考えています.実際に ObjectStore があまり普及しなかったのはパフォーマンス以外の要因が大きかったとは思うのですが,最大の売りだったパフォーマンスでなかなか好ましい結果を得にくかったのは致命的ではなかったかなぁ,と.


しかし,キャッシュとなると話は変わってきます.
Oracle の代わりに ObjectStore を使うというような場合だと,上記の 4 象限全てで好ましいパフォーマンスが得られないと困るわけですが,キャッシュであれば効果が期待できる部分だけに限って使うということもできるかもしれません.
複数のプロセスでインスタンスが共有されてかつ,頻繁に更新されるようなクラスはキャッシングせずに,滅多に更新されないクラスや,更新はあってもインスタンスごとに特定のプロセスでしか使われないようなクラスに限ってキャッシングするという選択もありだと思うのです (ここでのキャッシングとはロングキャッシュによるキャッシングのこと).
実際に Hibernate などでそのようなキャッシュの使い分けが出来るかどうかは知りませんが,将来の方向としては十分にありではないかと.


参考までに,@IT に「RDBのボトルネックを解消する「キャッシング技術」とは?」という PR 記事が掲載されています.ここでは ObjectStore のキャッシュ技術を切り出したという製品,ObjectCache を RDB のキャッシュとして使ったアマゾンの事例が紹介されています.
想像で書くと,例えば書籍などの商品情報は,多数のプロセスから参照されるけれども滅多に更新されない情報と考えられます.また,ショッピングカートなどの顧客に関する情報は,ロードバランサにより適切に振り分けができれば,特定のプロセスでしか参照および更新されない (共有されない) 情報と考えられます.このような状況に限れば,ObjectCache は効果的に働いているのかもしれません.
また,在庫情報などは共有されてかつ頻繁に更新されますが,厳密な意味で最新の情報を表示しなくてもいいはずなので,MVCC という技術 (Oracle でいうところのマルチ・バージョニングに似ているが,リード・オンリーなトランザクションでのみ利用できる) が有効に使えているかもしれません.
この記事を見る限り,ObjectCache という製品はキャッシュというには大がかりすぎる感じで,実際には ObjectStore と RDBMS との非同期レプリケーション技術という印象を受けます.そのため,個人的にはあまり魅力を感じないのですが,アマゾンほどのトランザクションヘビーなサイトでこのようなキャッシュ技術が使われているという事実は,非常に興味深いものがあります.


ということで,ロングキャッシュというのも使い方次第では結構な効果を得られる可能性はあると思います (簡単ではないとは思いますが).
そうなると,Hibernate のような O/R マッピングのパフォーマンスも相当に期待できるかもしれませんね.
07/21の「Hibernate 入門記」でも書いたように,Hibernate における効率的な DB アクセスは,旧来的な DB アクセスの考え方とは大きく異なり,効果的なロングキャッシュが前提になっていると私は考えています.
JBoss TreeCache や,この日記でも何度か取り上げている TANGOSOL Coherence など,ロングキャッシュの実装も複数選択できるようですし,結構おもしろい分野かもしれません.
Coherence はどこかでいろいろ遊んでみるつもりなので,そのうちレポート書くかもしれません.


それから,Hibernate できめ細かくロングキャッシュを活用できるかですが,リファレンスでのロングキャッシュの解説は「14.3. The Second Level Cache」まで待たないといけないようです.
入門記では先走らずに淡々と進むことにしているので,ここに到着するのは... 冬か? (^^;
残念!!!!