※というタイトルは正確ではなくて、以下の用途も実際にはRedisの特性の一部を利用します。
Redis自体は主にインメモリデータベースとしての高速性を生かしてWEBアプリケーションのセッションやトークン、RDBクエリのキャッシュとして使われることが多いものの、データ型や付属機能を利用することでランキングの生成やロックマネージャ等に応用できる。
リアルタイムランキング
SortedSetの機能を使うことで、リアルタイムランキングを作ることができる。
スコア(点数)を伴ったレコードを挿入するだけで、バッチ等の集計処理を挟まなくとも自動的にソートされたランキングデータが生成される。
例えば以下のようなランキングを作りたいとすると
順位 | 名前 (value) | 点数 (score) |
---|---|---|
1 | smith | 50.0 |
2 | paul | 20.0 |
3 | john | 10.0 |
SortedSetの機能を利用して以下のようにランキングを作成することができる。
import redis
client = redis.Redis()
RANKING_NAME = 'TEST_RANK'
# ランキングデータを挿入
# zadd(key, value, score)
client.zadd(RANKING_NAME, 'john', 10.0)
client.zadd(RANKING_NAME, 'smith', 50.0)
client.zadd(RANKING_NAME, 'paul', 20.0)
# ランキングを降順で取得
# zrevrange(key, start, end)
client.zrevrange(RANKING_NAME, 0, -1) # ['smith', 'paul', 'john']
# 新しいデータを挿入後も自動でソートされる
client.zadd(RANKING_NAME, 'george', 100.0)
client.zadd(RANKING_NAME, 'dave', 5.0)
client.zrevrange(RANKING_NAME, 0, -1) # ['george', 'smith', 'paul', 'john', 'dave']
# 順位を取得
# zrevrank(key, value)
# 0スタートのインデックスが返るので、順位を表示するときは +1をする等
# 昇順はzrank(key, value)
client.zrevrank(RANKING_NAME, 'george') # 0
client.zrevrank(RANKING_NAME, 'paul') # 2
# 点数を取得
client.zscore(RANKING_NAME, 'paul') # 20.0
client.zscore(RANKING_NAME, 'john') # 10.0
※zrank/zrevrankで取得できる順位は昇降順に並び替えたリストの添字で、同点の順位を考慮出来ないので、アプリケーション側で適宜調整する必要があります。
分散ロック
setnxの機能を使ってリソースの排他制御を行える。
データベース内であればトランザクションが、アプリケーションサーバ内のプロセスであればmutexが排他制御に利用できるものの、サーバを跨いだプロセス間で制御を行うのは難しい。
Redisのsetnxには同じキーを登録できないという特性があり、登録に成功した場合は1を、失敗した場合は0を返すので、これをロックの獲得状態と見なしてリソースの排他制御に利用できる。
import redis
client = redis.Redis()
# 'LOCK_NAME'をキーに登録
client.setnx('LOCK_NAME', 'hoge') # True
# 確認
client.get('LOCK_NAME') # 'hoge'
# 同じキーで登録を試みると失敗する
client.setnx('LOCK_NAME', 'fuga') # False
# 解放する場合はdelete
client.delete('LOCK_NAME')
このままでは明示的にdeleteするまではロックが残り続けてしまうので、登録に成功した場合は併せてexpireを設定したり、valueにロック獲得時間を設定して次回参照時に超過した場合はdeleteする。
Pubsub
redisを介して1:n n:nのクライアントでメッセージのリアルタイム送受信を行える。
チャネルという単位を通じて送信者(publisher)と購読者(subscriber)の間でメッセージを交換する。
subscriberがpublisherを兼ねることもできれば、対象のチャネルを購読している限りではpublisherが何人ものsubscriberに向けてメッセージを送信することもできる。
簡易なチャットアプリ等で利用できる。
送信側(Publisher)
import redis
client = redis.Redis()
# "test"チャネル に "hoge"メッセージ他を送信
client.publish('test', 'hoge')
client.publish('test', 'fuga')
client.publish('test', 'exit')
受信側(Subscriber)
client = redis.Redis()
pubsub = client.pubsub()
# "test"チャネルを購読
pubsub.subscribe('test')
for message in pubsub.listen():
print message.get('data')
if message.get('data') == 'exit':
# 終了メッセージなら購読終了
pubsub.unsubscribe()