JPA L2キャッシュに関する辛み #javaee

これはJava EE Advent Calendar 2013の21日目
昨日は[twitter:@Hachiro808]さんのThymeleafを業務で使いたいでした。


皆様はJPAのL2キャッシュというものをご存知でしょうか。

L2キャッシュとは

エンティティをキャッシュし、複数のスレッド間でエンティティを使ってデータをやりとりする場合にいちいちデータベースにアクセスしなくて良くするためのキャッシュです。
普通にエンティティのキャッシュです。ちなみにL1キャッシュというのもありまして、そちらは同じスレッド内で同じエンティティを取得する場合に毎回DBにアクセスしに行かなくて良くするためのキャッシュです。

詳しくはOracleの人が書いた資料があったのでそちらを参照してください。
JPAのキャッシュを使ったアプリケーション高速化手法

超クセモノなL2キャッシュ

とまあ、字面だけ見ると非常に良いものな感じがするのですが、そんなうまくは行かないわけで。
実はJPAの仕様(JSR-000338)の中ではL2キャッシュに関する記述は3ページしかありません。
しかもそのうち半分は使用できるインターフェースに関する記述で、もう半分はキャッシュを利用できるということと、キャッシュを利用するためにはどうすればよいか程度の記述。
つまり内容がない。

キャッシュの時間や、キャッシュの量をどうするか等はすべてJPAプロバイダの方に任されております。
なので、L2キャッシュを使用した段階でポータビリティは諦めたほうが良いのではないでしょうか。
なので、L2キャッシュを実際にどう使えるのかの設定はプロバイダ毎に違いますので、ご注意下さい。

L2キャッシュを使用する上での注意

これで終わらせるのもアレなので、L2キャッシュを使用する上での注意点を。

複数台構成でまともに使用するためには基本的にJPAプロバイダ以上の何かが必要

例えばOracle CoherenceやInfinispanやら。
Eclipse LinkやHibernateに付いているキャッシュ機能だと一つのJVM内でしかキャッシュしてくれないので、クライアントがどこのサーバーにつなぐのかわからない状態では変更した情報をキャッシュしたのにアクセスできたりできなかったり。
更新したものの次のアクセスでは情報が元に戻ってたりが発生します。
それを防ぐためには複数台構成の間でキャッシュをやりとりするための方法が必要で、エンタープライズエンタープライズと唱えたくなるような冗長な仕組みが。
OracleさんがL2キャッシュの有効性をよく訴えているのを見るのはCoherenceを売りたくて売りたくてしょうがないのかもしれません。*1

キャッシュする情報がユーザーごとに独立しておりかつ頻繁に更新させるようなものならロードバランサーの方でユーザーを識別させて一度アクセスしたサーバーにずっとアクセスさせるだけでも良いかもしれません。
ただ、その場合もそういった機能を持ったロードバランサー製品を選定しなければなりませんね。

一台構成でも注意点がある

一大構成なら心配がないかと言われると困る箇所が。
エンティティ単位でキャッシュされるため、JPQLでnew句を使ってアクセスした場合はキャッシュにヒットしません。*2
エンティティ単位でのキャッシュ機能なので、そりゃそーだと言われてしまえばそのとおりなのですが、一覧の表示でサマリーを作りたい場合にnew句は多用するので結構辛い。
適切なエンティティ設計がされていないとキャッシュにアクセスするために大量のデータをキャッシュしなければならずメモリー周りで爆死するということになりかねません。
メモリが足りない?ならoracle exalogicを(以下自主規制)

DBに直接するアクセスはキャッシュにヒットしない

これも当然ですね。
バッチ処理か何かでshellでSQL叩いた場合にキャッシュと情報が同期されません。
常にオンラインなシステムだとそれも懸念点としてあがり、バッチをJavaEEサーバーに組み込んでバッチの起動をhttpリクエストで行ったりします。
そうすることで、オンラインシステムと同じJVM上でバッチを起動させることができるようになり、同じキャッシュを参照させることができるようになります。
ただ、ウェブシステムとバッチが同じJVM上で動くようになりメモリがたくさん必要になってoracle exa(以下略)

読み取り専用だったらメソッドキャッシュとかの使用もご検討ください

エンティティの形式でしかキャッシュができないので、もっとViewで使いやすい形式(DTO)に変換した上でキャッシュさせるのが良いのではないでしょうか。
ehcacheとかよく使われるものもありますし。

まとめ

投げやりでごめんなさい。
Eclipse LinkはデフォルトでL2キャッシュが有効になっているのでご注意くださいな。

*1:DBアクセスがネックになっている場合は実際に有効なのですが。

*2:new句でアクセスした場合もキャッシュしてくれるプロバイダあったら情報ください