Redis を触ってると楽しくてニヤニヤしてしまう今日この頃です、こんにちは。

読み書きのパフォーマンス的には memcached とよく似てる感じかなーと思いますが、データが消えない(永続化)、さまざまなデータ型を持っていて用途によって使い分けられる、データ操作がアトミック、など、Redis には魅力的な特徴があります。

今回は Redis を使ってちょっとしたランキング機能を実装してみる話でも書いてみます。


Redisの環境準備

まずは Redis をインストールします。Mac だったら homebrew を使うと良いでしょう。

$ brew update
$ brew install redis

Redis の起動自体は非常に簡単で、このコマンドを実行するだけです。

redis-server /usr/local/etc/redis.conf

設定ファイル (redis.conf) ではさまざまな設定をすることができます。例えばこんな記述があると思うんですが、

save 900 1    # 最低1回変更が発生すると、900秒後にディスクに書き込む
save 300 10   # 最低10回変更が発生すると、300秒後にディスクに書き 込む
save 60 10000 # 最低10,000回変更が発生すると、60秒後にディスクに書き込む

これはどういうタイミングでメモリ上からディスクに書き込むかの設定です。Redis は非同期にディスクに書き込む(毎回書き込むことも可能ですが、だいぶパフォーマンスが落ちます)のですが、書き込みのタイミングはこの設定で自由に変更できます。書き込みは非同期なので、場合によっては最新のデータが失われる可能性があります。

設定ファイルに関して詳しくは こちら をご覧ください。

ただ、Redis を毎回起動するのはめんどくさいので、インストール時に出てくる説明にしたがって自動起動を設定しておくと良いでしょう。これで Redis が自動で起動するようになります。

ln -sfv /usr/local/opt/redis/*.plist ~/Library/LaunchAgents
launchctl load ~/Library/LaunchAgents/homebrew.mxcl.redis.plist


コマンドラインから操作

Redis が起動していれば、redis-cli というコマンドでいろいろな操作をすることができます。

試しに簡単なデータの読み書きをしてみます。最初は key には値が入っていない(空)ですが、そのあと値をセットすると、その値が取り出せていることがわかると思います。

$ redis-cli

redis 127.0.0.1:6379> GET key
(nil)
redis 127.0.0.1:6379> SET key foobar
OK
redis 127.0.0.1:6379> GET key
"foobar"


Redisのデータ型

Redis は以下のようないくつかのデータ型が存在します。扱いたいデータによって適したデータ型を選択して利用すると大変便利です。

1) 文字列型
 最も基本的なデータ型

2) リスト型
 文字列型のリスト(配列)で、要素の追加、削除が可能
 リストの大きさに関わらずPUSH, POP操作が O(1) になることが保証されている

3) セット型
 文字列型の順不同の集合であり複数の要素を持つが、値が重複しないという特徴がある
 セット型では集合演算を行うコマンドが提供されている

4) ソート済みセット型
 セット型とよく似ているが、スコアを持ち、そのスコアによってソートされている

5) ハッシュ型
 文字列型のキーと値のマップでいわゆる連想配列のような形式

Redis には複数のデータ型がある のですが、今回はソート済みセット型を利用します。ランキングなのでスコアによって自動的にソートされているのが便利なのです。


Rubyから使ってみる

Redis を Ruby から使う場合には以下の gem を利用するのでインストールしてください。

gem install redis

これで準備が整いました。簡単なユーザのランキングだとこんな感じでしょうか。'ranking' っていう入れものに対してスコア順にユーザIDが並んで入っているみたいなイメージです。

class User < ActiveRecord::Base
  include Rankable
end

module Rankable
  def self.setup
    User.active.each do |user|
      redis.zadd('ranking', user.point, user.id)
    end
  end

  # 現在の順位
  def rank
    Rankable.redis.zrevrank('ranking', self.id) + 1
  end

  # 現在のスコア(この例だとユーザの保持するポイント)
  def score
    Rankable.redis.zscore('ranking', self.id)
  end

  # スコアの更新
  def update_score(got_point)
    Rankable.redis.zincrby('ranking', got_point, self.id)
  end

  private

  def self.redis
    @redis ||= Redis.new
  end
end

他にもたくさんのコマンドがある ので気になる方は見てみてください。

ソート済みセット型であればユーザの現在の順位が簡単にわかるの良いですね〜。もちろんスコアを更新したら自動的にソートされて最新の順位がわかるようになります。便利ですね!

参考URL:ニコニコ生放送に見る Redis 活用ノウハウ
---
このエントリーをはてなブックマークに追加