男女比はカレーと福神漬けと同じくらい

マサカリよろしくお願いします。

【ISUCON用】Redis Cheat Sheet

ISUCON予選が明日に迫ってきました。

Redisの操作が全く覚えられないのでまとめておきます。

セッティング

インストール

qiita.com

この辺とか見たらいい感じにできるのではないでしょうか(未確認)

使い方

# デフォルトは127.0.0.1:6379で繋がるはずなのでとりあえずこれで
def redis
   @redis ||= (Thread.current[:isu8_redis] ||= Redis.new(host: "127.0.0.1", port: 6379))
end

# keyはこんな感じでメソッドで隠してあげるとtypoが減ってよさそう
def redis_key_unread_count(channel_id)
  "isu7:unread_count:#{channel_id}"
end

# redisの操作もこんな感じでメソッドで隠してあげよう
def redis_increment_unread_count(channel_id)
  user_ids = redis_get_user_ids
  user_ids.each do |user_id|
    redis.hincrby(redis_key_unread_count(channel_id), user_id, 1)
  end
end

Redis豆知識

格納されているフォーマットについて

Redisには数値という概念がなく、一度全て文字列として格納されている。 (incrとかは文字列を数値に変換してから+1して文字列に戻すなどの操作をしているっぽい)

そのため、redis.get ...とかで取得した数値は毎回to_iする必要がある。

redis-cliでのデバッグ

  • とりあえず格納されているkeyを全部見たい
keys *
  • 一旦格納されてるデータ全部消したい
flushall

データ型

つらつら書いていこうかと思ったけどドキュメント見ればわかると思ったのでひとことに留めておきます。

String

標準的なkey value get, set, mget, mset, incr, incrby とかをよく使う気がします。

文字列型 — redis 2.0.3 documentation

# 使う可能性のあるコマンド一覧
SET(key, value)
GET(key) # なければnilを返す
GETSET(key, value)
MGET(key1, key2, ..., keyN)
SETNX(key, value)
MSET(key1, value1, key2, value2, ..., keyN, valueN)
MSETNX(key1, value1, key2, value2, ..., keyN, valueN)
INCR(key)
INCRBY(key, integer)
DECR(key, integer)
DECRBY(key, integer)

List

Listです。
両端からアクセスできる(rpush, lpush)けどisuconだとListは帯に短し襷に長し感があります。
Hashを使った方がいい場合の方が多そう。
idを保存しておいてllenとかでSQLのcountの擬似実装とかには使えるかも?

リスト型 — redis 2.0.3 documentation

# 使う可能性のあるコマンド一覧
RPUSH(key, string)
LPUSH(key, string)
LLEN(key)
LRANGE(key, start, end) # key, 0, -1 で全取得
LINDEX(key, index)
LSET(key, index, value)
LPOP(key)
RPOP(key)

Set

sorted setを使おう。setを使いたい場面ってisuconであんまり思いつかない。

セット型 — redis 2.0.3 documentation

Sorted Set

score付きのset。コマンド名に癖があるけどなんかすごい高機能。
DBのtableをそのままredisに載せようと思ったら

  1. Hashとしてそのままデータをredisに突っ込む
  2. Sorted Setでkeyをテーブル名、memberをhashのkey, scoreをDBのindexとして使う

みたいな感じになるんじゃないかな。(やったことない)
scoreの範囲を指定して取得とかできるので本当にtableと置き換えられる。
ただ、データの変形をせずにそのままテーブルの置き換えてパフォーマンスがどのくらい変わるのかはよく分かっていない。

ソート済みセット型 — redis 2.0.3 documentation

# 使う可能性のあるコマンド一覧
ZADD(key, score, member)
ZCARD(key) # memberの数を返す
ZSCORE(key, member) # 対応するmemberのscoreを返す
ZREM(key, member) # memberを削除
ZINCRBY(key, increment, member)
ZRANK(key, member) # 指定したmemberのスコア順にソートされた場合のindexが取得できる
ZREVRANK(key, member) # ZRANKの降順版
# indexで範囲指定してmemberを取得できる、WITHSCORESをつけると対応するscoreも取得できる
ZRANGE(key, start, end, [WITHSCORES])
ZRANGE(key, start, end, [WITHSCORES]) # ZRANGEの降順版
# scoreで範囲指定してmemberを取得できる。LIMIT, offsetも指定できるので実質SQL
ZRANGEBYSCORE(key, min, max, [LIMIT, offset, count, [WITHSCORES]])
ZCOUNT(key, min, max) # ZRANGEBYSCOREのCOUNT版

# 複数のsorted setから和集合、積集合を作る。共通するmemberのscoreは和をとる。
# ランキング計算とかするときにめちゃくちゃ便利なんだけど、複雑なので時間が余らないと使わない気がする?
ZUNIONSTORE(dstkey, N, k1, ..., kN, [WEIGHTS, w1, ..., wN], [AGGREGATE, SUM|MIN|MAX])
ZINTERSTORE(dstkey, N, k1, ..., kN, [WEIGHTS, w1, ..., wN], [AGGREGATE, SUM|MIN|MAX])

Hash

Hash。tableをそのままredisに持ってこようとするような荒技を使うときに使う。 操作方法自体はStringと大差ない。

ハッシュ型 — redis 2.0.3 documentation

# 使う可能性のあるコマンド一覧
HSET(key, field, value)
HGET(key, field)
HMSET(key, field1, value1, ..., fieldN, valueN)
HMGET(key, field1, ..., fieldN)
HINCRBY(key, field, value) # 負数を指定すればデクリメントもできる
HEXISTS(key, field)
HLEN(key)
HKEYS(key)
HVALS(key)
HGETALL(key)