针对于高并发的设计,对于服务的不同阶段,都有不同的设计实现方案,下面是针对部分场景的一些实现
一些概念性知识如下
秒杀的方式
限价、限量限价、限量限时限价
秒杀的业务特点
瞬时高并发、库存少、流程短、预热
购物流程
选择商品=>加入购物车=>确认订单=>提交订单=>支付成功
秒杀的时候一般会减少流程,没有加入购物车和确认订单
关键性思想(redis的卓越性能,一分钟百万请求)
秒杀实现的必备性思想,能够进行异步处理的一律异步处理,能够减少的步骤绝不冗余,最好针对洪峰流量都采用类似redis的内存数据库进行临时数据存储,最后在慢慢更新到关系型数据库,
秒杀开始前准备项
将一些秒杀商品实现存入redis内存数据库,比如存库数量,商品必要信息等
秒杀实现流程
消费者提交订单==>订单服务器接口利用redis进行库存加减操作,当库存不够了之后,所有的请求就直接进行返回,告诉消费者商品已售完===>针对已经跳过计数器获取到库存的===>将订单信息写入到redis订单队列里面去,依旧不进行mysql数据库操作====>写完订单队列之后跳转到订单支付界面====>到redis中查询订单状态信息====>然后进行支付,支付完成后=== 将支付信息保存到更新订单状态队列(后续异步处理)===保存后直接返回界面,抢购成功
详情实现图
单项目部署,可以直接采用redis字符串的计数器,每次请求,存库都减少一个,判断库存已经卖完,后续请求全部返回
set num 1
incr key: 将key中储存的数字值增1,并返回值
decr key: 将key中储存的数字值减1,并返回值
但在分布式部署的情况下,可能会出现一些极端的情况,比如A机器在减数的时候,B机器判断还有库存,超卖了
超卖解决:
利用redis的事务监听机制,解决超卖问题
大致解决是,在事务开始之前将秒杀商品设置到到redis数据库 比如(set order_ clothes 10000)
每次前端请求到后端时,判断redis还有没有库存,没有直接返回已卖完或请重试等内容
一旦redis还有库存,就采用redis的watch机制进行处理,防止在分布式环境下出现的问题
下面是redis官方的一段代码,以供参考
import redis
def key_for(user_id):
return "account_{}".format(user_id)
def double_account(client, user_id):
key = key_for(user_id)
while True:
client.watch(key)
value = int(client.get(key))
value *= 2 # 加倍
pipe = client.pipeline(transaction=True)
pipe.multi()
pipe.set(key, value)
try:
pipe.execute()
break # 总算成功了
except redis.WatchError:
continue # 事务被打断了,重试
return int(client.get(key)) # 重新获取余额
user_id = "abc"
client.setnx(key_for(user_id), 5) # setnx 做初始化
print double_account(client, user_id)
利用监听机制更高级的问题
假如A客户端在master拿到了锁,但是加锁的key还没有来得及同步到slave,master发生故障,此时B客户端获得了锁,造成问题
Redis更加高级分布式实现redlock或者redisson实现
问题汇总
如何防止程序员自己写代码去进行秒杀抢购 URL动态化
恶意请求(黄牛)........ 针对这一项,一般会在nginx层面就做一些限制,比如某一请求量太过夸张,直接拉黑
如何区分普通商品与秒杀商品,可能某一普通商品一下变得非常热门
秒杀架构越大,所要涉及的东西也就越多
kafka消息队列
canal实时数据同步
Apache Druid实时数据分析
并发那些事:https://blog.csdn.net/xiao_xia_ming/article/details/106202458
redis必备知识点:https://blog.csdn.net/xiao_xia_ming/article/details/106040376