使用 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"