目录
一、秒杀系统需求分析
1-1 超卖问题
10个物品被20个用户抢到。
查询出对应商品的库存,看是否大于0,然后执行生成订单等操作,但是在判断库存是否大于0处,如果在高并发下就会有问题,导致库存量出现负数(线程安全)
解决思路:解决线程安全。
思路一、由于Redis 是单进程单线程,Redis 的所有基础命令都是原子性的,所以在单命令下线程安全。
1-2 超抢问题
对于同一个物品,单用户能抢到多个。
解决思路:判断用户是否抢到,需要记录抢购用户
二、基于 Redis 队列实现抢购
使用redis队列,因为pop操作是原子的,即使有很多用户同时到达,也是依次执行。
mysql事务在高并发下性能下降很厉害,文件锁的方式也是
并发思路:celery 异步任务(优化高并发场景) + redis 队列
2-0 思路总结
- key - value []
- 假设:商品A限购5个,初始化list在redis内
- 抢购商品A - [1,1,1,1,1]
- 开始抢购则进行pop操作,每来一个用户先判断list的长度是否非0,若非0,pop一个,并将用户名写入另一个list
- 抢购商品A - [1,1,1,1]
- 商品A已抢用户 - [用户1,]
- 若list(抢购商品A )已经pop空,则将返回用户失败信息。
2-1 初始版本
# ---------用于生成抢购库存列表----------------- li = [1 for i in range(50)] print(li) re = conn.lpush('goods_a', *li) print(re)
# API 接口,CBV class Seckill(Resource): def post(self): # 获取form表单内的username,唯一参数 username = request.form['username'] # redis 内list存储数据为byte类型 username = bytes(username, encoding='utf-8') # 获得当前redis内已购用户信息 users_list = redis_store0.lrange('a_users', 0, -1) if username in users_list: return '您已经抢到了,请勿重复请求。' # 判断库存 goods_count = redis_store0.llen('goods_a') if goods_count: res = redis_store0.lpop('goods_a') if res: redis_store0.lpush('a_users', username) return &