一、redis数据类型
1.1 list(数组)
- 队列,列表的子成员类型为string
lpush key value
rpush key value
linsert key after|before 指定元素 value
lindex key index
lrange key start stop
lset key index value
lrem key count value
1.1.1 添加子成员
# 在左侧(前)添加一条或多条数据
lpush key value1 value2 ...
# 在右侧(后)添加一条或多条数据
rpush key value1 value2 ...
# 在指定元素的左边(前)/右边(后)插入一个或多个数据
linsert key before 指定元素 value1 value2 ....
linsert key after 指定元素 value1 value2 ....
linsert根据已有的元素去添加,如果有相同元素(zhangsan),从左到右找第一个zhangsan匹配
- 从键为
brother
的列表左侧添加一个或多个数据liubei、guanyu、zhangfei
lpush brother liubei
# [liubei]
lpush brother guanyu zhangfei xiaoming
# [xiaoming,zhangfei,guanyu,liubei]
- 从键为brother的列表右侧添加一个或多个数据,
xiaohong,xiaobai,xiaohui
rpush brother xiaohong
# [xiaoming,zhangfei,guanyu,liubei,xiaohong]
rpush brother xiaobai xiaohui
# [xiaoming,zhangfei,guanyu,liubei,xiaohong,xiaobai,xiaohui]
- 从key=brother的xiaohong的列表位置左侧添加一个数据,
xiaoA,xiaoB
linsert brother before xiaohong xiaoA
# [xiaoming,zhangfei,guanyu,liubei,xiaoA,xiaohong,xiaobai,xiaohui]
linsert brother before xiaohong xiaoB
# [xiaoming,zhangfei,guanyu,liubei,xiaoA,xiaoB,xiaohong,xiaobai,xiaohui]
- 从key=brother,key=xiaohong的列表位置右侧添加一个数据,
xiaoC,xiaoD
linsert brother after xiaohong xiaoC
# [xiaoming,zhangfei,guanyu,liubei,xiaoA,xiaohong,xiaoC,xiaobai,xiaohui]
linsert brother after xiaohong xiaoD
# [xiaoming,zhangfei,guanyu,liubei,xiaoA,xiaohong,xiaoD,xiaoC,xiaobai,xiaohui]
注意:当列表如果存在多个成员值一致的情况下,默认只识别第一个。
127.0.0.1:6379> linsert brother before xiaoA xiaohong
# [xiaoming,zhangfei,guanyu,liubei,xiaohong,xiaoA,xiaohong,xiaoD,xiaoC,xiaobai,xiaohui]
127.0.0.1:6379> linsert brother before xiaohong xiaoE
# [xiaoming,zhangfei,guanyu,liubei,xiaoE,xiaohong,xiaoA,xiaohong,xiaoD,xiaoC,xiaobai,xiaohui]
127.0.0.1:6379> linsert brother after xiaohong xiaoF
# [xiaoming,zhangfei,guanyu,liubei,xiaoE,xiaohong,xiaoF,xiaoA,xiaohong,xiaoD,xiaoC,xiaobai,xiaohui]
1.1.2 基于索引获取列表成员
- 根据指定的索引(下标)获取成员的值,负数下标从右边-1开始,逐个递减
lindex key index
- 获取brother下标为2以及-2的成员
del brother
lpush brother guanyu zhangfei xiaoming
lindex brother 2
# "guanyu"
lindex brother -2
# "zhangfei"
1.1.3 获取列表的切片
- 闭区间[包括stop]
lrange key start stop
操作:
del brother
rpush brother liubei guanyu zhangfei xiaoming xaiohong
# 获取btother的全部成员
lrange brother 0 -1
# 获取brother的前2个成员
lrange brother 0 1
# 获取brother的后2个成员
lrange brother -2 -1
1.1.4 获取列表的长度
llen key
- 获取brother列表的成员个数
llen brother
1.1.5 按索引设置值
lset key index value
# 注意:
# redis的列表也有索引,从左往右,从0开始,逐一递增,第1个元素下标为0
# 索引可以是负数,表示尾部开始计数,如`-1`表示最后1个元素
修改键为brother
的列表中下标为4
的元素值为xiaohongmao
lset brother 4 xiaohonghong
1.1.6 删除指定成员
- 移除并获取列表的第一个成员或最后一个成员。
lpop删除按照索引删除
lpop key # 第一个成员出列
rpop key # 最后一个成员出列
- 获取并移除brother中的第一个成员
lpop brother
# 开发中往往使用rpush和lpop实现队列的数据结构->`实现入列和出列(先入先出)`
lrem
删除方法—按照元素内容删除
lrem key count value
# 注意:
# count表示删除的数量,value表示要删除的成员。该命令默认表示将列表从左侧前count个value的元素移除
# count==0,表示删除列表所有值为value的成员
# count >0,表示删除列表左侧开始的前count个value成员
# count <0,表示删除列表右侧开始的前count个value成员
del brother
rpush brother A B A C A
lrem brother 0 A
["B","C"]
del brother
rpush brother A B A C A
lrem brother -2 A
["A","B","C"]
del brother
rpush brother A B A C A
lrem brother 2 A
["B","C","A"]
1.2 hash(哈希)
redis = {
"info":{
"name":"rain"
"age":22
}
}
对应的值不是普通值而是字典值。"name"、"age"着两个键是属于哈希值的键,"info"键是属于redis的键
结构:
键key:{
域field: 值value,(字段值)
域field: 值value,
域field: 值value,
}
hset key field value
hget key field
hgetall info
hmget key field1 field2 …
hincrby key field number
专门用于结构化的数据信息。对应的就是map/结构体
1.2.1 设置指定键的属性/域
- 设置指定键的单个属性。
hset key field value
- 设置键
user_1
的属性name
为`xiaoming
127.0.0.1:6379> hset user_1 name xiaoming # user_1没有会自动创建
(integer) 1
127.0.0.1:6379> hset user_1 name xiaohei # user_1中重复的属性会被修改
(integer) 0
127.0.0.1:6379> hset user_1 age 16 # user_1中不存在的属性会被新增
(integer) 1
127.0.0.1:6379> hset user:1 name xiaohui # user:1会在redis界面操作中以:作为目录分隔符
(integer) 1
127.0.0.1:6379> hset user:1 age 15
(integer) 1
127.0.0.1:6379> hset user:2 name xiaohong age 16 # 一次性添加或修改多个属性
1.2.2 获取指定键的域/属性的值
- 获取指定键所有的域/属性
hkeys key
- 获取键user的所有域/属性
127.0.0.1:6379> hkeys user:2
1) "name"
2) "age"
127.0.0.1:6379> hkeys user:3
1) "name"
2) "age"
3) "sex"
- 获取指定键的单个域/属性的值
hget key field
- 获取键
user:3
属性name
的值
127.0.0.1:6379> hget user:3 name
"xiaohong"
- 获取指定键的多个域/属性的值-----这个方法处于
hget
和hgetall
中间---->hmget
hmget key field1 field2 ...
- 获取指定键的多个域/属性的值
hmget key field1 field2 ...
- 获取键
user:2
属性name
、age
的值
127.0.0.1:6379> hmget user:2 name age
1) "xiaohong"
2) "16"
- 获取指定键的所有值
hvals key
- 获取指定键的所有域值对
127.0.0.1:6379> hvals user:3
1) "xiaohong"
2) "17"
3) "1"
1.2.3 获取hash的所有域值对
127.0.0.1:6379> hset user:1 name xiaoming age 16 sex 1
(integer) 3
127.0.0.1:6379> hgetall user:1
1) "name"
2) "xiaoming"
3) "age"
4) "16"
5) "sex"
6) "1"
1.2.4 删除指定键的域/属性
hdel key field1 field2 ...
- 删除键
user:3
的属性sex/age/name
,当键中的hash数据没有任何属性,则当前键会被redis删除
hdel user:3 sex age name
1.2.5 判断指定属性/域是否存在于当前键对应的hash中
hexists key field
- 判断user:2中是否存在age属性
127.0.0.1:6379> hexists user:3 age
(integer) 0
127.0.0.1:6379> hexists user:2 age
(integer) 1
127.0.0.1:6379>
1.2.6 属性值自增自减
hincrby key field number
- 自加
1
—hincr
,自加多------hincrby
- 给user:2的age属性在原值基础上+/-10,然后在age现有值的基础上-2
# 按指定数值自增
127.0.0.1:6379> hincrby user:2 age 10
(integer) 77
127.0.0.1:6379> hincrby user:2 age 10
(integer) 87
# 按指定数值自减
127.0.0.1:6379> hincrby user:2 age -10
(integer) 77
127.0.0.1:6379> hincrby user:2 age -10
1.3 set(集合)
无序集合,重点就是去重
和无序
。
1.3.1 添加元素
sadd key member1 member2 ...
- 向键
authors
的集合中添加元素zhangsan
、lisi
、wangwu
sadd authors zhangsan lisi wangwu
1.3.2 获取集合的所有的成员
smembers key
- 获取键
authors
的集合中所有元素
smembers authors
1.3.3 获取集合的长度
scard keys
- 获取s2集合的长度
sadd s2 a b c d e
127.0.0.1:6379> scard s2
(integer) 5
1.3.4 随机抽取一个或多个元素
- 抽取出来的成员被删除掉
spop key [count=1]
# 注意:
# count为可选参数,不填则默认一个。被提取成员会从集合中被删除掉
- 随机获取s2集合的成员
sadd s2 a c d e
127.0.0.1:6379> spop s2
"d"
127.0.0.1:6379> spop s2
"c"
1.3.5 删除指定元素
srem key value
- 删除键
authors
的集合中元素wangwu
srem authors wangwu
1.3.6 交集、差集和并集
推荐、(协同过滤,基于用户、基于物品)
sinter key1 key2 key3 .... # 交集、比较多个集合中共同存在的成员
sdiff key1 key2 key3 .... # 差集、比较多个集合中不同的成员
sunion key1 key2 key3 .... # 并集、合并所有集合的成员,并去重
del user:1 user:2 user:3 user:4
sadd user:1 1 2 3 4 # user:1 = {1,2,3,4}
sadd user:2 1 3 4 5 # user:2 = {1,3,4,5}
sadd user:3 1 3 5 6 # user:3 = {1,3,5,6}
sadd user:4 2 3 4 # user:4 = {2,3,4}
# 交集
127.0.0.1:6379> sinter user:1 user:2
1) "1"
2) "3"
3) "4"
127.0.0.1:6379> sinter user:1 user:3
1) "1"
2) "3"
127.0.0.1:6379> sinter user:1 user:4
1) "2"
2) "3"
3) "4"
127.0.0.1:6379> sinter user:2 user:4
1) "3"
2) "4"
# 并集
127.0.0.1:6379> sunion user:1 user:2 user:4
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
# 差集
127.0.0.1:6379> sdiff user:2 user:3
1) "4" # 此时可以给user:3推荐4
127.0.0.1:6379> sdiff user:3 user:2
1) "6" # 此时可以给user:2推荐6
127.0.0.1:6379> sdiff user:1 user:3
1) "2"
2) "4"
1.4 zset(有序集合)
有序集合(score/value),去重并且根据score权重值来进行排序的
。score从小到大排列。每一个value都带着一个自身的权重值
1.4.1 添加成员
zadd key score1 member1 score2 member2 score3 member3 ....
# score必须是数字,数字是成员(member)的权重,是一对
- 设置榜单achievements,设置成绩和用户名作为achievements的成员
127.0.0.1:6379> zadd achievements 61 xiaoming 62 xiaohong 83 xiaobai 78 xiaohei 87 xiaohui 99 xiaolan # xiaoming成绩61分
(integer) 6
127.0.0.1:6379> zadd achievements 85 xiaohuang
(integer) 1
127.0.0.1:6379> zadd achievements 54 xiaoqing
1.4.2 获取score在指定区间的所有成员
zrangebyscore key min max # 按score进行从低往高排序获取指定score区间
zrevrangebyscore key max min # 按score进行从高往低排序获取指定score区间
zrange key start stop # 按scoer进行从低往高排序获取指定索引区间
zrevrange key start stop # 按scoer进行从高往低排序获取指定索引区间
1.4.3 获取集合长度
zcard key
- 获取users的长度
zcard achievements
1.4.4 获取指定成员的权重值
zscore key member
- 获取users中xiaoming的成绩
127.0.0.1:6379> zscore achievements xiaobai
"93"
127.0.0.1:6379> zscore achievements xiaohong
"62"
127.0.0.1:6379> zscore achievements xiaoming
"61"
1.4.5 获取指定成员在集合中的排名
- 排名从0开始计算
zrank key member # score从小到大的排名
zrevrank key member # score从大到小的排名
- 获取achievements中xiaohei的分数排名,从大到小
127.0.0.1:6379> zrevrank achievements xiaohei
(integer) 4
1.4.6 获取score在指定区间的所有成员数量
zcount key min max
- 获取achievements从0~60分之间的人数[闭区间]
127.0.0.1:6379> zcount achievements 0 60
(integer) 2
127.0.0.1:6379> zcount achievements 54 60
(integer) 2
1.4.7 给指定成员增加增加权重值
zincrby key score member
1.4.8 删除成员
zrem key member1 member2 member3 ....
- 从achievements中删除xiaoming的数据
zrem achievements xiaoming
1.4.9 删除指定数量的成员
# 删除指定数量的成员,从最低score开始删除
zpopmin key [count]
# 删除指定数量的成员,从最高score开始删除
zpopmax key [count]
例子:
# 从achievements中提取并删除成绩最低的2个数据
127.0.0.1:6379> zpopmin achievements 2
1) "xiaoqing"
2) "54"
3) "xiaolv"
4) "60"
# 从achievements中提取并删除成绩最高的2个数据
127.0.0.1:6379> zpopmax achievements 2
1) "xiaolan"
2) "99"
3) "xiaobai"
4) "93"
二、Python操作redis
2.1 python对redis基本操作
2.1.1 连接redis
# 方式1
import redis
r = redis.Redis(host='xxx.x.x.1', port=6379)
r.set('foo', 'Bar')
print(r.get('foo'))
# 方式2
import redis
pool = redis.ConnectionPool(host='xxx.x.x.1', port=6379)
r = redis.Redis(connection_pool=pool)
r.set('bar', 'Foo')
print(r.get('bar'))
通常情况下, 当我们需要做redis操作时, 会创建一个连接, 并基于这个连接进行redis操作, 操作完成后, 释放连接,一般情况下, 这是没问题的, 但当并发量比较高的时候, 频繁的连接创建和释放对性能会有较高的影响。于是, 连接池就发挥作用了。连接池的原理是, 通过预先创建多个连接, 当进行redis操作时, 直接获取已经创建的连接进行操作, 而且操作完成后, 不会释放, 用于后续的其他redis操作。这样就达到了避免频繁的redis连接创建和释放的目的, 从而提高性能。
2.1.2 数据类型操作
import redis
pool = redis.ConnectionPool(host='xxx.x.x.1', port=6379, db=0, decode_responses=True)
r = redis.Redis(connection_pool=pool)
# (1)字符串操作:不允许对已经存在的键设置值
ret = r.setnx("name", "yuan")
print(ret) # False
# (2)字符串操作:设置键有效期
r.setex("good_1001", 10, "2")
# (3)字符串操作:自增自减
r.set("age", 20)
r.incrby("age", 2)
print(r.get("age")) # b'22'
# (4)hash操作:设置hash
r.hset("info", "name", "rain")
print(r.hget("info", "name")) # b'rain'
r.hmset("info", {"gedner": "male", "age": 22})
print(r.hgetall("info")) # {b'name': b'rain', b'gender': b'male', b'age': b'22'}
# (5)list操作:设置list
r.rpush("scores", "100", "90", "80")
r.rpush("scores", "70")
r.lpush("scores", "120")
print(r.lrange("scores", 0, -1)) # ['120', '100', '90', '80', '70']
r.linsert("scores", "AFTER", "100", 95)
print(r.lrange("scores", 0, -1)) # ['120', '100', '95', '90', '80', '70']
print(r.lpop("scores")) # 120
print(r.rpop("scores")) # 70
print(r.lindex("scores", 1)) # '95'
# (6)集合操作
# key对应的集合中添加元素
r.sadd("name_set", "zhangsan", "lisi", "wangwu")
# 获取key对应的集合的所有成员
print(r.smembers("name_set")) # {'lisi', 'zhangsan', 'wangwu'}
# 从key对应的集合中随机获取 numbers 个元素
print(r.srandmember("name_set", 2))
r.srem("name_set", "lisi")
print(r.smembers("name_set")) # {'wangwu', 'zhangsan'}
# (7)有序集合操作
# 在key对应的有序集合中添加元素
r.zadd("jifenbang", {"yuan": 78, "rain": 20, "alvin": 89, "eric": 45})
# 按照索引范围获取key对应的有序集合的元素
# zrange( name, start, end, desc=False, withscores=False, score_cast_func=float)
print(r.zrange("jifenbang", 0, -1)) # ['rain', 'eric', 'yuan', 'alvin']
print(r.zrange("jifenbang", 0, -1, withscores=True)) # ['rain', 'eric', 'yuan', 'alvin']
# withscores:把这个参数设成Ture,就可以拿到名字加分数
print(r.zrevrange("jifenbang", 0, -1, withscores=True)) # ['rain', 'eric', 'yuan', 'alvin']
print(r.zrangebyscore("jifenbang", 0, 100))
print(r.zrangebyscore("jifenbang", 0, 100, start=0, num=1))
#排序完再根据索引取,start=0, num=1从0开始取1个
# 删除key对应的有序集合中值是values的成员
print(r.zrem("jifenbang", "yuan")) # 删除成功返回1
print(r.zrange("jifenbang", 0, -1)) # ['rain', 'eric', 'alvin']
# (8)键操作
r.delete("scores")
print(r.exists("scores")) # 判断是否存在
print(r.keys("*"))
r.expire("name",10) # 设置过期时间
2.2 关于redis的实战案例
2.2.1 案例1:KV缓存
第1个是最基础也是最常?的就是KV功能,我们可以用Redis来缓存用户信息、会话信息、商品信息等等。下面这段代码就是通过缓存读取逻辑。
import redis
pool = redis.ConnectionPool(host='xxx.x.x.1', port=6379, db=6, decode_responses=True)
r = redis.Redis(connection_pool=pool)
def get_user(user_id):
user = r.get(user_id) # 从redis里面取
if not user:
user = UserInfo.objects.get(pk=user_id)
r.setex(user_id, 3600, user) # 如果在redis里面不存在或者已经过期了,我们可以在数据库(MysQl)取,再写入redis加上有效期。MySQL起到一个备份作用
return user
2.2.2 案例2:分布式锁
- 什么是分布式锁
分布式锁其实就是,控制分布式系统不同进程共同访问共享资源的一种锁的实现。如果不同的系统或同一个系统的不同主机之间共享了某个临界资源,往往需要互斥来防止彼此干扰,以保证一致性。
提到Redis的分布式锁,很多小伙伴马上就会想到setnx
+ expire
命令(对某一些商品,当我的某一个线程操作它的时候,其他的线程不允许访问,加锁用到setnx+ expire)。即先用setnx
来抢锁,如果抢到之后,再用expire
给锁设置一个过期时间,防止锁忘记了释放。
SETNX 是SET IF NOT EXISTS的简写.日常命令格式是SETNX key value,如果 key不存在,则SETNX成功返回1,如果这个key已经存在了,则返回0。
- 方案1
import redis
pool = redis.ConnectionPool(host='xxx.x.x.1')
r = redis.Redis(connection_pool=pool)
ret = r.setnx("key_resource_id", "ok") # 设置资源的ID
if ret:
r.expire("key_resource_id", 5) # 设置过期时间
print("抢购成功!")
r.delete("key_resource_id") # 释放资源,不然其他的线程永远拿不到resource_id的权限
else:
print("抢购失败!")
但是这个方案中,setnx
和expire
两个命令分开了,「不是原子操作」(原子性:要处于同一个事务中,而上述先设置键值在设置过期时间所以不符合)。如果执行完setnx
加锁,正要执行expire
设置过期时间时,进程crash或者要重启维护了,那么这个锁就“长生不老”了,「别的线程永远获取不到锁啦」。
- 方案2:SETNX + value值是(系统时间+过期时间)
为了解决方案一,「发生异常锁得不到释放的场景」,可以把过期时间放到setnx
的value值里面。如果加锁失败,再拿出value值校验一下即可。加锁代码如下:
import time
def foo():
expiresTime = time.time() + 10
ret = r.setnx("key_resource_id", expiresTime)
if ret:
print("当前锁不存在,加锁成功")
return True
oldExpiresTime = r.get("key_resource_id")
if float(oldExpiresTime) < time.time(): # 如果获取到的过期时间,小于系统当前时间,表示已经过期
# 锁已过期,获取上一个锁的过期时间,并设置现在锁的过期时间
newExpiresTime = r.getset("key_resource_id", expiresTime)
if oldExpiresTime == newExpiresTime:
# 考虑多线程并发的情况,只有一个线程的设置值和当前值相同,它才可以加锁
return True # 加锁成功
return False # 其余情况加锁皆失败
foo()
- 方案3:加事务变成一个原子性操作
实际上,我们还可以使用Py的redis模块中的set函数来保证原子性(包含setnx和expire两条指令)代码如下
r.set("key_resource_id", "1", nx=True, ex=10) # 在设置的时候redis它自己的set本质上只能做键值,不能设置有效期,nx等。但是redis的这个set它的分装性是比较强的,在这里面通过参数来形成nx,有效期的设置,所以说是一个高度分装的一个方法
2.2.3 案例3:延迟队列(基于zset有序集合)
延时队列可以通过Redis的zset(有序列表)来实现。我们将消息序列化为一个字符串作为zset的值。这个消息的到期时间处理时间作为score,然后用多个线程轮询zset获取到期的任务进行处理,多线程时为了保障可用性,万一挂了一个线程还有其他线程可以继续处理。因为有多个线程,所有需要考虑并发争抢任务,确保任务不能被多次执行。
import time
import uuid # 生成一个随机字符串
import threading
import redis
pool = redis.ConnectionPool(host='xxx.x.x.1', port=6379, decode_responses=True)
r = redis.Redis(connection_pool=pool)
def delay_task(task_name, delay_time): # 生产者:往延迟队列里面加任务 参数:名称,延迟时间
# 保证value唯一
task_id = task_name + str(uuid.uuid4()) # uuid.uuid4() 生成一个随机字符串
# task_id:成员,retry_ts:权重
retry_ts = time.time() + delay_time # 当前时间戳+延迟多少=要执行时间戳
r.zadd("delay-queue", {task_id: retry_ts}) #写入Redis,以delay-queue(延迟队列)为键,值是一个有序集合:有序集合以当前task_id任务为键,以要执行的时间戳为值
def loop(): # 消费者,从延迟队列里面吧任务取出来加以执行
print("循环监听中...")
while True:
# 最多取1条
task_list = r.zrangebyscore("delay-queue", 0, time.time(), start=0, num=1) # 基于权重排序
if not task_list:
# 延时队列空的,休息1s
print("cost 1秒钟")
time.sleep(1)
continue
task_id = task_list[0]
success = r.zrem("delay-queue", task_id) # 考虑并发的一个意义操作,zrem删除
if success:
# 处理消息逻辑函数
handle_msg(task_id)
def handle_msg(msg):
"""消息处理逻辑"""
print(f"消息{msg}已经被处理完成!")
t = threading.Thread(target=loop) # 单开一个子线程做监听
t.start()
delay_task("任务1延迟5", 5)
delay_task("任务2延迟2", 2)
delay_task("任务3延迟3", 3)
delay_task("任务4延迟10", 10)
redis的zrem方法是对多线程争抢任务的关键,它的返回值决定了当前实例有没有抢到任务,因为loop方法可能会被多个线程、多个进程调用, 同一个任务可能会被多个进程线程抢到,通过zrem来决定唯一的属主。同时,一定要对handle_msg进行异常捕获, 避免因为个别任务处理问题导致的循环异常退出。
2.2.4 案例4:发布订阅
subscribe channel # 订阅
publish channel mes # 发布消息
2.2.4.1 基于redis的发布订阅的聊天案例
import threading
import redis
r = redis.Redis(host='xxx.x.x.1')
def recv_msg(): # 订阅消费者
pub = r.pubsub()
pub.subscribe("fm104.5") # 订阅,这里订阅本身就有一个响应
pub.parse_response() # 响应
while 1:
msg = pub.parse_response()
print(">>>", msg)
def send_msg(): # 订阅生产者
msg = input(">>>")
r.publish("fm104.5", msg)
t = threading.Thread(target=send_msg)
t.start()
recv_msg()
2.2.5 案例5:定时任务
利用 Redis 也能实现订单30分钟自动取消。
用户下单之后,在规定时间内如果不完成付款,订单自动取消,并且释放库存使用技术:Redis键空间通知(过期回调)用户下单之后将订单id作为key,任意值作为值存入redis中,给这条数据设置过期时间,也就是订单超时的时间启用键空间通知
- 开启过期key监听
from redis import StrictRedis
redis = StrictRedis(host='localhost', port=6379)
# 监听所有事件
# pubsub = redis.pubsub()
# pubsub.psubscribe('__keyspace@0__:*')
#
# print('Starting message loop')
# while True:
# message = pubsub.get_message()
# if message:
# print(message)
# 监听过期key
def event_handler(msg):
print("sss",msg)
thread.stop()
pubsub = redis.pubsub()
pubsub.psubscribe(**{'__keyevent@0__:expired': event_handler})
thread = pubsub.run_in_thread(sleep_time=0.01)