Zerobase Dev Blog

コードネームYentry

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 or KEYS *.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よりも遅くなる。

追記

『Redisによる透明な永続化層』の余談と没ネタ - Memorandum by zerobase