Redisによる透明な永続化層
『RedisとMongoDBについて調べた』(2013-02-03)について考え直した。Redisによる透明な永続化層のアーキテクチャは、下記のようにシンプルになる。『データベースを使った永続化への復讐』(2013-02-03)に書いた理想に少しずつ近づいている。
エンティティと値オブジェクト
- すべてがオブジェクトである。
- オブジェクトには二種類ある。エンティティと値オブジェクトである。
- エンティティは、ドメインで意味のあるIDを持つ(例:ISBN)。
- 値オブジェクトは、エンティティを記述する。アイデンティティ(ID)はない。
Redisによる永続化の方針
- エンティティだけを永続化する。
- エンティティは、シリアライズして、Redisのhashに保存する。
- 値オブジェクトは、その値とクラス情報のペアで表現する。
- 他エンティティへの参照は、エンティティIDとクラス情報のペアで表現する。
- エンティティと値オブジェクトの定義は、ソースコード上のアノテーションによってする。
- 全インスタンスのIDをsetで管理し、ガベージ・コレクション方式で物理削除する。
永続化コード不要
- リポジトリは自動生成する。永続化や復元の処理は書かなくてもよい。
- 抽象的なインターフェイスを通じてリポジトリを利用できる。
- リポジトリのユーザーは、リポジトリの実装(永続化にRedisを使っていることなど)を知らなくていい。環境設定がDependency Injectionされる。
普通なら、例えば、ドメインのEmployeeというクラスに対応したEmployeeRepositoryという抽象インターフェイスがあり、それを実装したEmployeeRepositoryForRedisがある。それらをすべて実装する。しかし、上述のように透明にしたい。
シリアル表現
クラス定義が
// Scala
class Employee (val name:String, val employer:Company)
のインスタンスは
// serialized entity
{ name:String = "Hideto Ishibashi", employer:Company = "123456789" } : Employee
のようになるイメージ。"123456789"
はCompany
のIDで、例えば会社法人等番号かもしれないし、システムへの登録時に自動生成されたシリアルナンバーかもしれない。
エンティティの永続化
エンティティをシリアライズしてhashに追加し、IDをsetに追加する。
- store/update:
HSET {class}.objects {objectID} {serializedObject}
(*HSET) - also:
SADD {class}.ids {objectID}
(*SADD)
オブジェクトの復元
- restore:
HGET {class}.objects {objectID}
(*HGET)
スキーマの問い合わせ
KEYS *.objects
orKEYS *.ids
(*KEYS)
ガベージ・コレクション
参照されていないIDをsetのdiffで求め、それらのエンティティをhashから削除し、IDをsetから削除する。
for each class:
- all referenced IDs:
SADD {class}.ref {objectID} {objectID} ...
(*SADD) - all unreferenced IDs:
SDIFFSTORE {class}.unref {class}.ids {class}.ref
(*SDIFFSTORE) - fetch {count} unreferenced IDs:
SRANDMEMBER {class}.unref {count}
(*SRANDMEMBER) - delete unreferenced objects:
HDEL {class}.objects {objectID} {objectID} ...
(*HDEL) - also:
SREM {class}.ids {objectID} {objectID} ...
(*SREM) - clean up:
DEL {class}.ref {class}.unref
(*DEL)
ふつうに実行するとすべてのオブジェクトが消えてしまう。絶対の参照元が必要。例えば、マスターデータとユーザー(users)エンティティは、GCの対象とせず、明示的にsemanticに削除する。それ以外の「マスターでないエンティティ」かつ「どのユーザーにも紐づかないエンティティ」はGCされる。
考察
- MongoDBと同じような使い方をすれば、同じように永続化できることがわかる。
- Redisのほうが高性能だろうけれど、機能性ではMongoDBのほうが優れている。簡単な検索機能は単体で実現できるなど。
- スケールアウトの観点では、どちらもshardingやclusteringに対応している。
- オブジェクト永続化の観点で、RedisはMongoDBに対してトレードオフと言えるほど善戦しうるのかどうか、まだ分からない。MongoDBのほうがいいのかもしれない。
『ももクロスライダーで測る Redis vs MongoDB on Heroku 第二弾 - from scratch』:
Redisはやっぱり高速でした。3点まとめです。
- ローカル環境の単純なアクセスではRedis は爆速。
- Heroku環境でもネットワークがボトルネックになるもののかなり高速を維持する。
- クライアントの同時接続数が増えるとRedis To Go アドオンのコネクション数が頭打ちになるため、場合によってはMongoよりも遅くなる。