Redis除了用作缓存存储之外的用途
该标题并不准确,并且以下的用途实际上也是利用了Redis的一部分特性。Redis主要被用作内存数据库来加快WEB应用程序的会话、令牌和RDB查询的缓存,然而通过使用数据类型和附属功能,还可以应用于生成排名和锁管理器等方面。
リアルタイムランキング
SortedSetの機能を使うことで、リアルタイムランキングを作ることができる。
スコア(点数)を伴ったレコードを挿入するだけで、バッチ等の集計処理を挟まなくとも自動的にソートされたランキングデータが生成される。
例えば以下のようなランキングを作りたいとすると
可以利用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')
如果不明确删除,锁将一直保持下去,所以如果成功注册,可以同时设置过期时间或在值中设置锁定时间,在下次引用时超过时间则删除。
发布订阅
可以通过Redis实现在1:n和n:n的客户端之间进行实时消息的发送和接收。
通过通道作为单位,在发布者(publisher)和订阅者(subscriber)之间进行消息交流。
订阅者可以兼作发布者,只要他们订阅了目标通道,发布者就可以向多个订阅者发送消息。
可用于简易聊天应用等。
发布者
import redis
client = redis.Redis()
# "test"チャネル に "hoge"メッセージ他を送信
client.publish('test', 'hoge')
client.publish('test', 'fuga')
client.publish('test', 'exit')
订阅者
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()