redis-objectsがなくてもプロジェクトは回るが
あるととても便利だと思います
はじめに
よろしくお願いします。
https://github.com/nateware/redis-objects
特徴
・ORMではない
・Redisのデータ型(counter, value, list, hash, set, sorted-set)をサポート
・ロック機能も利用可能
好きなところ
・モデルの任意のプロパティをRedisに保存するような感覚で利用できる点。
・Redisのキー情報をシームレスに管理できる点。
・ARモデルの一部プロパティをRedisに移したりできるので、データストア単位でなく理想の単位でモデルを定義できる点。
This is not an ORM. People that are wrapping ORM’s around Redis are missing the point.作者の方はこのように言っており、本当にとても使いやすい形になっております。
(readmeより引用)
目次
2. List
3. Set
4. Hash
5. Sorted Set
6. global
7. Lock
8. redisオブジェクト
1. Counter
数値をアトミックにincrement/decrementする型です。
class User < ActiveRecord::Base include Redis::Objects counter :friend_count, :start => 0 # startは省略可 end @user = User.find(id) # インクリメント @user.friend_count.increment # 渡したブロックで例外発生もしくはnilが帰ったときは、decrementを発行して数値を戻す @user.friend_count.increment do |new_value| raise 'friend count is limited by 20' if 20 < new_value true end # Userを取得せずにアクセス friend_count = User.get_counter(:friend_count, user_id) User.increment_counter(:friend_count, user_id)Userを取得せずにアクセスできる点、オブジェクト経由で操作する時はuser_idを渡さないでよい点(キーがシームレス)が便利です。
2. List
配列をアトミックに操作できる型です。
class User < ActiveRecord::Base include Redis::Objects list :friend_ids, :maxlength => 20 # startは省略可 end @user = User.find(id) # rubyの配列のように使えます(Enumerable) # 追加 @user.friend_ids << 123 @user.friend_ids.push(123) # 変更 @user.friend_ids[2] = 123 # 削除 @user.friend_ids.delete(123) # 取得 @user.friend_ids[2] @user.friend_ids[2..4] @user.friend_ids.at(2) @user.friend_ids.first @user.friend_ids.each do |user_id| # hoge end @user.friend_ids.values
Userを取得せずに操作するメソッドは現在ありません。
しかし、⬇⬇⬇このようにしてUserオブジェクトなしにアクセスできます。
name = :friend_ids friend_ids = Redis::List.new(User.redis_field_key(name, user_id), User.redis, User.redis_objects[name]) friend_ids.values
3. Set
重複無効な配列をアトミックに操作できる型です。
class User < ActiveRecord::Base include Redis::Objects set :friend_ids end @user = User.find(id) # rubyの配列のように使えます(Enumerable) @user.friend_ids << 123 @user.friend_ids.each do |user_id| # hoge end # setなので、重複したものは無視 @user.friend_ids << 123 @user.friend_ids << 123 # 2回目は無視 @user.friend_ids.member?(me.id) # 共通の友達 @user.friend_ids & me.friend_ids # どちらかの友達 @user.friend_ids | me.friend_ids @user.friend_ids + me.friend_ids # 共通でない友達(差分) @user.friend_ids ^ me.friend_ids @user.friend_ids - me.friend_ids # Userを取得せずに操作するメソッドは現在ありませんが、List同様(前述)に取得できます
4. Hash
ハッシュをアトミックに操作できる型です。
class User < ActiveRecord::Base include Redis::Objects hash_key :item_count_map end @user = User.find(id) # rubyのHashのように使えます(Enumerable) @user.item_count_map[item_id] = 10 @user.item_count_map.each do |item_id, count| # hoge end # まとめて操作 @user.item_count_map.bulk_set(:a => 10, :b => 20, :c => 30) @user.item_count_map.bulk_get(:a, :b) # => {:a => 10, :b => 20} @user.item_count_map.bulk_values(:a, :b) # => [10, 20] # 個別にインクリメント @user.item_count_map.incr(:a, 50) @user.item_count_map[:a] # => 60 # Userを取得せずに操作するメソッドは現在ありませんが、List同様(前述)に取得できます
5. Sorted Set
ソート済みハッシュをアトミックに操作できる型です。
class User < ActiveRecord::Base include Redis::Objects sorted_set :article_rate end @user = User.find(id) # articleごとに評価スコアを登録 @user.article_rate[:a] = 30 @user.article_rate[:b] = 50 @user.article_rate[:c] = 10 @user.article_rate.score(:c) # => 10 # 順位 # 昇順 @user.article_rate.rank(:b) # => 2 # 降順 @user.article_rate.revrank(:b) # => 0 # スコアの範囲取得 @user.article_rate.rangebyscore(0, 100, :limit => 1) # => [:c] @user.article_rate.members(:with_scores => true) # => [[:c, 10], [:a, 30], [:b, 50]] # atomicなインクリメント @user.article_rate.incr(:c, 100) @user.article_rate.score(:c) # => 110 # Userを取得せずに操作するメソッドは現在ありませんが、List同様(前述)に取得できます
6. global
すべてのデータ型クラスでglobalオプションが使えます。
trueを指定すると、クラス単位でredisキーが共通になります。
※ 同モデルのオブジェクトすべてが、同じキャッシュを見るような状態です。
class User < ActiveRecord::Base include Redis::Objects sorted_set :article_rate sorted_set :event_point, :global => true end # これは@userごとのランキング @user.article_rate.rank(:a) # これは全ユーザーのランキング @user.event_point.rank(:a)
7. Lock
lockの実装としてはredisにフラグ値をsetし、そのフラグがある場合は処理を待ち合わせる作りです。
同じhoge_lockを使っている箇所とで排他制御にできます。
class User < ActiveRecord::Base include Redis::Objects lock :hoge, :expiration => 20.second, :timeout => 1.second # lockのオプション # timeout : # 指定された時間以上にロック解除を待った場合、例外を投げます。 # expiration : # 万が一ロックが解除されない状態になっても、指定された時間が経つとredis側でロック解除します。 end @user = User.find(id) # 処理Aと処理Bは排他的に動く @user.hoge_lock.lock do # 処理A end @user.hoge_lock.lock do # 処理B end
8. redisオブジェクト
redisオブジェクトはredis gemのオブジェクトで、redis-objectsで未実装のAPIもredisオブジェクト経由で呼び出せる場合があります。
class User < ActiveRecord::Base include Redis::Objects value :hoge end @user = User.find(id) # redisオブジェクトへは、下記のようにアクセスできます。 redis = User.redis redis = @user.redis redis.pipelined do redis.set "foo", "bar" redis.incr "baz" end
おわりに
本稿にお付き合い下さいましてありがとうございました。
この素晴らしいプロダクトを、みなさまが少しでもよいと思っていただければ幸いです。