問題
如何使用 Redis 實(shí)現(xiàn)一個(gè)優(yōu)先級(jí)隊(duì)列呢辆脸?
sorted set 簡介
Sorted set 是 Redis 提供的一種數(shù)據(jù)結(jié)構(gòu)類型铃将,兼具 set 和 hash 的特點(diǎn)瞬浓。首先,sorted set 中的每個(gè)元素是唯一的牢撼;其次淮逻,sorted set 中的每個(gè)元素都關(guān)聯(lián)一個(gè)浮點(diǎn)值(叫做 score)。除此之外皱埠,sorted set 中的元素是有序的肮帐,這些元素依照如下規(guī)則排序:
- 如果A和B是兩個(gè) score 不一樣的元素,則當(dāng)且僅當(dāng) A.score > B.score 的時(shí)候边器,A > B
- 如果A和B的 score 一樣训枢,則當(dāng)且僅當(dāng)A的字典序大于B的時(shí)候,A > B
sorted set 操作
- ZADD:向 sorted set 中添加元素
- ZCOUNT: sorted set 中 score 等于指定值的元素有多少個(gè)
- ZSCORE:sorted set 中指定元素的 score 是多少
- ZCARD: sorted set 中總共有多少個(gè)元素
- ZREM:刪除 sorted set 中的指定元素
- ZREVRANGE:按照從大到小的順序返回指定索引區(qū)間內(nèi)的元素
- ZRANGE: 按照從小到大的順序返回指定索引區(qū)間內(nèi)的元素
sorted set 支持的操作還有很多忘巧,上面只是列舉了一些對(duì)實(shí)現(xiàn)優(yōu)先級(jí)隊(duì)列有用的操作恒界。
優(yōu)先級(jí)隊(duì)列實(shí)現(xiàn)
基于 sorted set,可以實(shí)現(xiàn)一個(gè)簡單的優(yōu)先級(jí)隊(duì)列砚嘴。代碼如下:
class PriorityQueue(object):
def __init__(self, redis_client, queue, namespace='priority-queue'):
self.client = redis_client
self.key = '%s:%s' % (namespace, queue)
def enqueue(self, score, member):
return self.client.zadd(self.key, score, member)
def dequeue(self):
"""注意:該方法不是線程安全的"""
score = None
member = None
result = self.client.zrevrange(self.key, 0, 0, withscores=True)
# [( member, score), (member, score), ...]
if result:
member, score = result[0]
ret = self.client.zrem(self.key, member)
assert ret == 1
return score, member
def card(self):
return self.client.zcard(self.key)
def __repr__(self):
return u"PriorityQueue<%s>" % self.key
優(yōu)先級(jí)隊(duì)列主要操作是入隊(duì)和出隊(duì)十酣,sorted set 根據(jù)元素的 score 維護(hù)了優(yōu)先級(jí)順序涩拙。需要注意的是,上述代碼中的出隊(duì)操作不是線程安全的耸采,因?yàn)槿?yōu)先級(jí)最高的元素以及刪除這個(gè)元素是兩次操作兴泥,不是原子性的。