使用 Redis 字符串、列表和集合的常用命令完成任务分配的后端处理逻辑。
相关知识
你需要掌握:1.常用字符串命令,2.常用列表命令,3.常用集合命令。
常用字符串命令
Redis 的字符串可以存储三种类型的值:
- 整数
- 浮点数
- 字节串
取值范围说明
Redis 中整型数据的长度与系统字长一致(例如:32位系统,整型数据为
32位有符号整数)
Redis 中浮点数的取值范围与精度都与双精度浮点数(
double)一致
数值操作
所以针对存储整型和浮点型的字符串就有自增和自减操作。在需要的时候(例如下表的 INCRBYFLOAT 命令),Redis 还会将整数转换为浮点数。
对 Redis 字符串执行自增和自减的命令列表如下:
| 命令 | 用法 | 说明 |
|---|---|---|
INCR | INCR key | 将 key 存储的值加上 1 |
DECR | DECR key | 将 key 存储的值减去 1 |
INCRBY | INCRBY key increment | 将 key 存储的值加上 increment |
DECRBY | DECRBY key decrement | 将 key 存储的值减去 decrement |
INCRBYFLOAT | INCRBYFLOAT key increment | 将 key 存储的值加上浮点数 increment |
注意:
INCRBYFLOAT只能在Redis版本>= 2.6时可用
当用户将一个值存储到 Redis 字符串中,Redis 会检测这个值是否可以被解释(interpret)为十进制整数或者浮点数。如果可以,则允许用户对该字符串进行自增和自减操作。
在前面也提到过,如果用户对一个不存在的键或者一个保存了空串的键执行了自增或自减操作,Redis 都会:
- 先将该键的值置为
0 - 再对该键的值进行自增或自减操作
需要额外提到的是,Python 的 Redis 库在 incr(key, increment=1) 方法中同时实现了 INCR 和 INCRBY 命令,该方法的第二个参数 increment 是可选的,如果用户没有设置该值,就会使用其默认值 1。例如:
>>> conn = redis.Redis()>>> conn.set('key', '1')True>>> conn.incr('key', 10)11>>> conn.decr('key', 5)6>>> conn.incr('key')7
字节串操作
Redis 还可以对字节串的一部分内容进行读取/写入:
| 命令 | 用法 | 说明 |
|---|---|---|
APPEND | APPEND key value | 将 value 追加到 key 键存储的值的末尾 |
GETRANGE | GETRANGE key start end | 获取 start 到 end 间的子串 |
SETRANGE | SETRANGE key offset value | 从 start 偏移量开始,将与 value 长度一致的子串设置为 value |
在使用 GETRANGE 读取字符串时,超出字符串末尾的数据会被视为空串;而在使用 SETRANGE 对字符串进行写入时,如果字符串当前长度不能满足写入要求,Redis 则会自动使用空字节将字符串扩展至所需的长度,然后再执行写入/更新操作。
值得一提的是,Redis 现在的 GETRANGE 命令式以前的 SUBSTR 命令改名而来的,所以,Python 客户端仍然可以使用 substr() 方法获取子串,例如:
>>> conn.set('string', 'hello')True>>> conn.append('string', ' educoder')14L>>> conn.substr('string', 0, 4)'hello'>>> conn.setrange('string', 0, 'ByeBye')14>>> conn.get('string')'ByeByeeducoder'>>> conn.getrange('string', 6, -1)'educoder'
我们推荐使用 getrange() 方法来获取子串。在上述示例中,我们还将 end 下标传入了 -1 的值,这时 Redis 将会从起始偏移量读取到该字符串的末尾。
常用列表命令
Redis 提供了丰富的列表操作命令,从而使得列表的应用场景非常广泛,例如:存储任务队列,记录最近的操作/数据变化,作为日志收集器等。
首先我们介绍一些常用的列表命令:
| 命令 | 用法 | 说明 |
|---|---|---|
LPUSH | LPUSH key value [value ...] | 将一个或多个 value 推入到列表的左侧 |
RPUSH | RPUSH key value [value ...] | 将一个或多个 value 推入到列表的右侧 |
LLEN | LLEN key | 返回列表 key 的长度 |
LREM | LREM key count value | 根据参数 count 的值,移除列表中与参数 value 相等的元素 |
加上我们在上一个实训中已经介绍过的弹出、获取元素等命令,就构成了最为常用的列表命令。使用 Python 交互的示例如下:
>>> conn.lpush('list', 'a', 'b', 'c', 'd')4L>>> conn.llen('list')4>>> conn.rpush('list', 'a', 'b', 'c', 'd')8L>>> conn.lrange('list', 0, -1)['d', 'c', 'b', 'a', 'a', 'b', 'c', 'd']>>> conn.lrem('list', 'b', 2)>>> conn.lrange('list', 0, -1)['d', 'c', 'a', 'a', 'c', 'd']
我们发现 lrem() 方法与 LREM 命令在参数的顺序上不完全一致,lrem() 方法将 count 参数放至最后,在 Python 的 Redis 客户端中,大多数命令中的数值型参数都被放到了最后,如果弄不清某个方法的参数,你可以到 redis客户端主页 查看。
我们还可以在两个列表之间移动元素:
RPOPLPUSH source destination
RPOPLPUSH 命令在一个原子时间内,执行以下两个动作:
- 将列表
source中的最右侧元素弹出,并返回给客户端。 - 将
source弹出的元素推入到列表destination的最左侧
例如:
>>> conn.lpush('list2', '1', '2', '3')>>> conn.rpoplpush('list', 'list2')'d'>>> conn.lrange('list', 0, -1)['d', 'c', 'a', 'a', 'c']>>> conn.lrange('list2', 0, -1)['d', '3', '2', '1']
原子时间
不可再拆分的时间段
意指该操作执行时,不可被其他操作打断,也就是包含在一个原子时间内的若干操作要么都成功要么都失败
常用集合命令
与列表有序不同,Redis 中的集合以无序的方式存储多个互不相同的元素,用户可以快速的添加、删除和查找元素。Redis 提供了针对单个集合以及多集合间处理的命令:
| 命令 | 用法 | 说明 |
|---|---|---|
SCARD | SCARD key | 返回集合 key 中元素的数量 |
SRANDMEMBER | SRANDMEMBER key [count] | 返回集合中的 1 或 count 个随机元素 |
SPOP | SPOP key | 移除并返回集合中的一个随机元素 |
SMOVE | SMOVE source destination member | 将 member 元素从 source 集合移动到 destination 集合 |
我们通过一些示例来展示上述命令的用法:
>>> conn.sadd('set', 'a', 'b', 'c', 'a')>>> conn.scard('set')3>>> conn.srandmember('set')'a'>>> conn.spop('set')'b'>>> conn.smembers('set')set(['a', 'c'])>>> conn.smove('set', 'set2', 'a')>>> conn.smembers('set2')set(['a'])
Redis 中的许多命令都有着实际的应用场景,例如 SRANDMEMBER 命令从集合中随机选择一个元素并输出,在数据库层面就实现了随机数功能,避免用户将集合的全部成员取出后再随机选择,加快了效率,减少了开发人员的工作量。所以我们一直称 Redis 是基于实用主义的。
在 SMOVE 命令的示例中你也发现了,如果目的集合是不存在的,我们会先创建目的集合,再将成员从源集合中取出并放入目的集合。但如果指定的成员不存在于源集合中,则该命令不会继续执行。
Redis 集合还有更为强大的功能 —— 组合和关联多个集合:
| 命令 | 用法 | 说明 |
|---|---|---|
SDIFF | SDIFF key [key ...] | 返回所有给定集合之间的差集 |
SINTER | SINTER key [key ...] | 返回所有给定集合的交集 |
SUNION | SUNION key [key ...] | 返回所有给定集合的并集 |
上述三个命令是差集,交集,并集运算的“返回结果”版本,同时 Redis 还提供了“存储结果”版本,你可以参考 Redis 命令参考 中的 SDIFFSTORE,SINTERSTORE 和 SUNIONSTORE 命令。
编程要求
根据提示,在右侧Begin-End区域补充代码,完成任务分配的后端处理逻辑:
- 在
task_empty()方法中:- 从
Redis中获取列表task_list的长度,判断是否为0- 若为
0,则返回True
- 若为
- 从
- 若不为
0,则返回False - 在
get_task()方法中:- 从列表
task_list的最右侧弹出一个元素,赋值给task - 将
task的值设置到Redis的字符串键current_task中
- 从列表
- 在
get_unallocated_staff()方法中:- 从集合
unallocated_staff中随机返回一个元素,赋值给staff - 将上面的
staff从集合unallocated_staff移动到集合allocated_staff中 - 返回(
return)staff的值
- 从集合
- 在
allocate_task(staff)方法中:- 将参数
staff的值追加到Redis字符串键current_task的尾部,中间以:间隔 - 将追加后的字符串键
current_task从左侧推入列表task_queue - 将字符串键
current_task的值设置为"None"
- 将参数