Redis---哈希与有序集合

常用哈希命令

Redis 的哈希允许用户将多个键值存储到一个 Redis 键中,使得哈希十分适合将一些相关的数据存储在一起。我们可以把这种数据看作是关系型数据库中的行。

常用的哈希命令包括之前介绍过的添加和删除域-值对命令、获取所有域-值对命令以及对域-值对的值进行自增/自减操作的命令:

命令用法说明
HMSETHMSET key field value [field value ...]同时将多个 field-value (域-值)对设置到哈希表 key 中
HMGETHMGET key field [field ...]返回哈希表 key 中,一或多个给定域的值
HDELHDEL key field [field ...]删除哈希表 key 中的一或多个指定域
HLENHLEN key返回哈希表 key 中域的数量

在上一个实训中,我们使用过 HMSET 命令来批量的存储域-值对信息,实际上 HMSETHMGET 命令既可以通过批量处理给用户带来便利,又减少了命令的调用次数,提升了客户端与 Redis 之间的通信次数,提高了 Redis 的性能:

>>> conn.hmset('hash', {'a': '1', 'b': '2', 'c': '3'})
True
>>> conn.hmget('hash', ['a', 'b'])
['1', '2']
>>> conn.hdel('hash', 'b', 'c')
2
>>> conn.hlen('hash')
1

在使用 HMGET 命令时,我们可以使用类似于上面数组形式传入参数,也可以类似于 HDEL 命令的多参数形式传入参数。而之前介绍的 HGETHSET 命令则分别是 HMGETHMSET 命令的单参数版本,每次执行时只能处理一个键值对。

Redis 哈希还支持一些更高级的批量操作:

命令用法说明
HEXISTSHEXISTS key field查看哈希表 key 中,给定域 field 是否存在
HKEYSHKEYS key返回哈希表 key 中所有域
HVALSHVALS key返回哈希表 key 中所有域的值
HINCRBYHINCRBY key field increment为哈希表 key 中的域 field 的值加上 increment

在哈希包含的值的体积都十分大时,我们应该使用 HKEYS 命令获取所有的域,再使用 HGET 一个个的从哈希中取出域的值,从而避免 Redis 因为一次性获取多个大体积的值而导致服务器阻塞。甚至,我们可以只获取必要的值来减少传输的数据量。

常用有序集合命令

有序集合与哈希类似,也存储着成员(member)和分值(score)之间的映射关系。Redis 为有序集合提供了分值处理命令,并能根据分值大小有序的排列成员:

命令用法说明
ZCARDZCARD key返回有序集合 key 的成员总数
ZCOUNTZCOUNT key min max返回有序集合 key 中, score 值在 min 和 max 之间的成员数量
ZRANKZRANK key member返回有序集合 key 中成员 member 的排名
ZSCOREZSCORE key member返回有序集合 key 中,成员 member 的分值

值得一提的是,之前提过的 ZADD 命令在 Redis 中的语法是:

  • 先输入分值,后输入成员。
  • 例如:ZADD sorted-set 100 member

而在 Python 客户端中执行 ZADD 命令组需要:

  • 先输入成员,后输入分值
  • 例如:conn.zadd('sorted-set', 'member', 100)

类似于集合,有序集合也有交集(ZINTERSTORE)和并集(ZUNIONSTORE)命令。我们通过一个示例来理解有序集合的交集和并集命令:

>>> conn.zadd('zset-1', 'a', 1, 'b', 2, 'c', 3)
>>> conn.zadd('zset-2', 'b', 4, 'c', 1, 'd', 0)
>>> conn.zinterstore('zset-i', ['zset-1', 'zset-2'])
2L
>>> conn.zrange('zset-i', 0, -1, withscores=True)
[('c', 4.0), ('b', 6.0)]
>>> conn.zunionstore('zset-u', ['zset-1', 'zset-2'], aggregate='min')
4L
>>> conn.zrange('zset-u', 0, -1, withscores=True)
[('d', 0.0), ('a', 1.0), ('c', 1.0), ('b', 2.0)]
>>> conn.sadd('set-1', 'a', 'd')
2
>>> conn.zunionstore('zset-u2', ['zset-1', 'zset-2', 'set-1'])
4L
>>> conn.zrange('zset-u2', 0, -1, withscores=True)
[('d', 1.0), ('a', 2.0), ('c', 4.0), ('b', 6.0)]

在执行交集和并集运算时,可以传入不同的聚合函数

  • sum,对相同成员的分值求和作为新分值。
  • min,取相同成员中最低的分值作为新分值。
  • max,取相同成员中最高的分值作为新分值。

在交集运算时,使用了默认的聚合函数sum,所以其交集运算过程如下:

而并集运算则不同,只要某个成员存在于一个输入有序集合中,那么这个成员就会包括在输出有序集合中。在执行并集运算时,我们使用 aggregate 参数指定了聚合函数是 min,所以其并集运算过程如下:

并集运算还可以在有序集合和集合之间进行,上面的示例中,我们将两个有序集合和一个集合组合成了一个新的有序集合:

在上述过程中,集合中的每个成员的分值都先被当作 0,然后再进行并集运算。

如何实现带优先级的队列系统

上一关中,我们实现了任务分配的后端处理逻辑,在学习了哈希和有序集合的知识后,我们为每个任务带上优先级,使得高优先级的任务优先分配,更加符合实际情况。

首先我们使用哈希存储任务状态,方便我们后续查询任务状态。任务与任务状态构成域-值对,存放在 task_status 键中:

conn.hset("task_status", task_id, "init")

接下来我们要开始构建任务队列了,由于任务具有优先级,所以可以使用有序集合来存储队列信息,其成员是任务 ID,分值是优先级。例如:任务 1 的优先级为 2 时:

conn.zadd('task_queue', '1', 2)

通过上述方法将任务放进任务队列,而在取任务时,则需要使用到有序集合的排序功能,找出优先级(分值)最高的成员:

task_list_by_priority = conn.zrevrange('task_queue', 0, -1)
current_task = task_list_by_priority[0]
conn.zrem('task_queue', current_task)

ZREVRANGE 命令有三个参数,依次为 keystartstop,其返回有序集合根据排名范围 startstop 中的成员,并按分值从大到小排列。

所以我们可以使用这个命令获取到整个有序集合按照分值从大到小顺序排列的结果,从当中取出第一个成员,就是我们所需要的优先级(分值)最高的成员(current_task)了。最后,别忘了将这个成员从有序集合中移除(使用ZREM 命令)。

因为我们使用了 task_status 哈希存储了任务状态,所以需要在任务从队列中取出,开始处理时更新这个状态:

conn.hset("task_status", current_task, "processing")

将上述步骤使用三个方法分别实现,代码如下:

# 初始化任务信息到 Redis 中
def set_task_info(task_id):
conn.hset("task_status", task_id, "init")

# 将任务添加至任务队列
def add_task_to_queue(task_id, priority):
conn.zadd("task_queue", task_id, int(priority))
set_task_info(task_id)

# 从任务队列中取出优先级最高的任务
def get_task():
task_list_by_priority = conn.zrevrange("task_queue", 0, -1)
current_task = task_list_by_priority[0]
conn.zrem('task_queue', current_task)
conn.hset("task_status", current_task, "processing")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nuhao_

谢谢你打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值