当前位置: 首页 > 科技观察

Redis与秒杀系统完美解密!!!

时间:2023-03-14 22:07:09 科技观察

后台秒杀活动是大多数电商选择的低价促销和品牌推广方式。不仅可以为平台带来用户,还可以增加平台的知名度。一个好的秒杀系统可以提高平台系统的稳定性和公平性,获得更好的用户体验,提高平台的美誉度,从而增加秒杀活动的最大价值。本文讨论高并发秒杀系统的Redis缓存设计。秒杀的特点秒杀活动对稀缺或特殊商品进行定期定量销售,吸引大量消费者抢购,但只有小部分消费者能够成功下单。因此,秒杀活动会在短时间内产生比平时大几十倍、上百倍的页面访问流量和订单请求流量。秒杀活动分为3个阶段:秒杀前:用户不断刷新商品详情页,页面请求达到瞬时峰值。秒杀开始:用户点击秒杀按钮,订单请求达到瞬时峰值。秒杀后:部分下单成功的用户不断刷新订单或产生退款,大部分用户继续刷新商品详情页面等待退货机会。当消费者提交订单时,一般的方法是使用数据库的行级锁。只有抢到锁的请求才能进行库存查询和下单操作。但是,在高并发的情况下,数据库无法承受这么大的请求,往往会导致整个服务被阻塞,在消费者看来就是服务器宕机了。秒杀系统虽然秒杀系统的流量很高,但实际有效流量非常有限。利用系统的层次结构,在每个阶段提前检查,拦截无效流量,可以减少大量无效流量涌入数据库。使用浏览器缓存和CDN抵御静态页面流量尖峰,用户不断刷新商品详情页面,导致页面请求量大。因此,我们需要将秒杀商品详情页与普通商品详情页分开。对于秒杀商品详情页,尽量对可以静态化的元素进行静态化处理。除了秒杀按钮需要服务器动态判断外,其他静态数据可以缓存在浏览器和CDN中。这样,只有一小部分秒杀进入服务器前刷新页面造成的流量。使用读写分离Redis缓存拦截流量CDN是一级流量拦截,二级流量拦截我们使用支持读写分离的Redis。现阶段我们主要是读取数据,Redis可以支持60万以上的qps读写分离,完全可以支撑需求。首先通过数据控制模块,将闪购商品提前缓存到读写分离的Redis中,并设置闪购开始标记如下:"goodsId\_count":100//总数"goodsId\_start":0//startmark"goodsId_access":0//接受订单号1,秒杀启动前,服务集群读取goodsId_Start为0,直接返回notstarted。2、数据控制模块将goodsId_start改为1,标志秒杀开始。3、服务集群缓存开始标志,开始接受请求,并在redis中记录为goodsId_access,剩余商品数量为(goodsId_count-goodsId_access)。4、当接受订单数达到goodsId_count时,继续拦截所有请求,此时商品剩余数量为0。可见只有少量成功参与下单的请求才能被接受。在高并发的情况下,允许稍微多一点的流量进入。因此,可以控制接受订单的比例。使用主从版本的Redis缓存,加速库存扣除。参与下单成功后,进入下层服务开始订单信息校验和库存扣减。为了避免直接访问数据库,我们使用Redis的主从版本进行库存扣减。Redis的主从版本提供10万级别的QPS。使用Redis优化库存查询,提前拦截失败的闪购请求,将大大提高系统的整体吞吐量。库存通过数据控制模块预先存储在Redis中,每个闪购商品在Redis中以哈希结构表示。"goodsId":{"Total":100"Booked":100}扣除金额后,服务端通过以下lua脚本请求Redis获取下单资格。由于Redis是单线程模型,lua可以保证多个命令的原子性。性别。localn=tonumber(ARGV\[1\])ifnotnorn==0thenreturn0endlocalvals=redis.call("HMGET",KEYS\[1\],"Total","Booked");localtotal=tonumber(vals\[1\])localblocked=tonumber(vals\[2\])ifnottotalornotblockedthenreturn0endifblocked+n<=totalthenredis.call("HINCRBY",KEYS\[1\],"Booked",n)returnn;endreturn0FirstuseSCRIPTLOADtocachetheluascriptinadvance在Redis,然后调用EVALSHA调用脚本,比直接调用EVAL节省网络带宽:redis127.0.0.1:6379>SCRIPTLOAD"luacode""438dd755f3fe0d32771753eb57f075b18fed7716"redis127.0.0.1:6379>EVAL438dd755f3fe0d32771753eb57f075b18fed77161goodsId1秒杀服务通过判断Redis是否返回抢购个Countntoknowwhetherthedeductionoftherequestissuccessful.Usethemaster-slaveversionofRedistoimplementasimplemessagequeueforasynchronousorderingandwarehousing.Afterthedeductioniscompleted,theorderneedstobeputintothewarehouse.Ifthenumberofcommoditiesissmall,youcandirectlyoperatethedatabase.Iftheflashsaleproductis10,000oreven100,000,thenthedatabaselockconflictwillbringabigperformancebottleneck.Therefore,usingthemessagequeuecomponent,whentheseckillservicewritestheorderinformationintothemessagequeue,itcanbeconsideredthattheorderiscompleted,avoidingdirectoperationofthedatabase.1.ThemessagequeuecomponentcanstillbeimplementedusingRedis,whichisrepresentedbyalistdatastructureinR2.orderList{\[0\]={ordercontent}\[1\]={ordercontent}\[2\]={ordercontent}...}2.WritetheordercontenttoRedis:LPUSHorderList{ordercontent}3.TheasynchronousorderplacingmodulesequentiallyobtainsorderinformationfromRedisandwritestheorderintothedatabase.BRPOPorderList0使用Redis作为消息队列,异步处理订单入库,有效提升用户订单完成速度。数据控制模块管理秒杀数据同步。一开始是用Redis做读写分离来限流的,这样只有部分流量可以进单。对于订单验证失败、订单返回等情况,需要允许更多的流量进入。因此,数据控制模块需要周期性地对数据库中的数据进行一定的计算,同步到主从版本的Redis,以及同时同步到读写分离的Redis,让更多的流量进来。