闪购业务架构选型

闪购特点:

瞬时并发流量大。

业务模式:
用户下单扣相应sku库存,超过20分钟(或其它时间)未支付,则回收库存。

设计原则:
保证不超卖及不少卖的情况下,提升并发性能。
保证服务稳定性,做限流,做预案。
数据库是瓶颈,减少到数据库的请求。
分流,即业务层面,将商品分散。

方案:

方案一:
redis做购买车层的库存,数据库做基础库存,库存最终扣减以数据库为准。
这样能够保证不超卖。
相应透到数据库的流量就是实际sku库存的流量。

用户将sku放入购买车,减的是redis层的库存。

优点:
相应透到数据库的流量就是实际sku库存的流量。能够达到性能要求。

缺点:
需要保证缓存的高可用,及出现缓存整个挂掉后的风险预案。

预案:
a.缓存的高可用
使用redis3.0 cluster集群,多组master/slave,其中一组的master挂了,可选举slave成功master。
slave晋升为master的时间=cluster-node-timeout + 选举时间,一般设置cluster-node-timeout为:8秒。选举时间大约在5秒内,则不可用时间为:13秒。

这里在slave提升为master后会有个问题:
slave中的数据与挂掉的数据可能存在不一致,分两种情况:
某个sku的库存slave小于master,这种情况是少卖,解决的方法是每天凌晨1点同步数据库库存到缓存。
sku库存大于master,原因可能是用户取消了sku。这种情况会发生用户能够将sku放入购买车,但最终下单会失败。提示用户:商品太热门了,手慢了。

b.缓存全挂了后,如何恢复缓存
这里可以做降级,即库存展示及扣减在购买车这块不做操作。在下单时候直接做库存增减。
当然需要评估要不要做,缓存全挂概率基本不可能,在有多于5组master/slave的情况下。

而假设全挂了后,且不采用降级,这时就需要快速恢复redis,所有库存以数据库为主重新load到缓存。
这种方案下最坏情况是用户在购买车本身存在某个sku的库存,然后在下单的时候会提示用户下单失败。

当然如果不想扩大下单时库存不足的失败,可以将redis的库存操作:包括增减sku库存在操作完成后发送到queue中,做一个这个层面的库存操作备份。之后缓存全挂了则从这个库存备份中获取。当然也存在极端情况下的不一致,即发送mq失败。

方案二:
后台做异步队列,扣减库存从数据库扣,扣减时机在用户下单的时候。
难点在于异步队列的设计实现。

优点:
无缓存与数据库一致性问题。

缺点:
异步队列可能导致整个队列很长,如果扣减库存任务太慢,会发生雪蹦校应。
即后面进入的操作,由于队列还有很多未处理,导致这些新请求会被延时处理,用户感觉就是请求卡在那边。

具体方案实现:
a.单个队列,对于所有sku
实现简单,但缺点是单队列性能太差,不同sku本身存在资源竞争。

b.每个sku一个队列
实现略复杂,性能高。
会不会撑爆内存。1000000个sku就有1000000个队列。
需要做好容量评估。1000000个队列,算上头尾结点(每个结点算64位),也就是有1.6M。
加上每个请求放入队列,每个结点数据包含:对象32位,加上32位请求唯一标识,32位处理结果,也就是12字节。
100w用户假设都请求,那么也就是会占用内存:12m。

以skuid进行加锁处理。

底层设置cpu核数+1的线程池个数。
这里是单台16核,因此有17个线程池。每个线程池是单个线程。

业务流程如下:
1.用户请求购买sku id为1的sku
2.对sku id取模17,得到相应要放入的线程池
3.将请求封装成task提交给线程池,返回异步future
4.相应线程池会做库存的操作,如果成功则返回结果并设置future
5.业务池程调用future获取结果,如果成功则做下面的业务逻辑,否则返回【无库存】

还有种方案是每个商品是一个队列,当然这就要求每台服务器商品数不能过多。

猜你喜欢

转载自blog.csdn.net/zhaozhenzuo/article/details/77413218
今日推荐