如何设计秒杀系统?

Posted IT东方会

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何设计秒杀系统?相关的知识,希望对你有一定的参考价值。




如何设计秒杀系统?

设计一个秒杀系统

首先对API进行分类, 分成两种:
  1. 静态API:内容可被缓存, 并且信息公开访问的结果。
  2. 动态API:也就是具体的业务接口。比如下单,比如登录。

两种API的域名是不一样的。

针对静态API,完全推送到CDN上。 针对静态API有一个全局版本时间戳, 并有一个专门的动态接口来获取他的变更状态。

一定要尽量减少动态接口的数量, 所以很有必要把秒杀业务和日常商城销售在架构上进行拆分。 

在解决掉流量风险之后, 后面的重点在于减少对数据库的冲击, 没有线程池的mysql在超过128个并发任务就会造成性能的急剧下降。尤其在库存扣减与大量订单写入数据库都会造成锁表导致不可用。

库存服务:
我们首先要实现一个无锁的库存管理服务。这里要用到一个叫CAS的CPU指令, Compare-And-Swap.

功能就是字面意义. 先比较某个内存地址上的数值v, 如果是m(compare), 则交换v和n的值(swap).
实际实现思路就是写一个循环, 比如某进程做库存扣减:
  1. 读出来库存数量是n, 查看n的值是否大于0
  2. cas传入库存值的内存地址, compare是否仍为n,
  3. 如果是n, 则将库存设置为n-1
  4. 如果不是n.  说明被其他用户购买过程所修改, 则从 第一条 重复开始.

在Go的sync包里, 有许多多协程场景下的有用指令, CAS指令就在sync/atomic 里。

如何设计秒杀系统?

用这样的办法可以实现一个单节点单SKU每秒支撑几万次库存扣减的库存服务。

订单服务:
订单的创建速度会大于数据库写入速度,因此我们会引入MQ去降低对数据库的冲击。
订单写入之后马上就会有查询以及更新支付状态, 要保证未落盘的订单数据与系统已有订单有一致的查询与更新接口,以这个目标为核心创建一个服务对该结构进行封装。

一个关键点是订单号, 订单号通常有业务规则约定, 要有时间信息, 号码不能连续否则会存在安全问题, 要保证分布式系统下的唯一性等等。

在我的实践里比较简单粗暴,一个独立的服务进程, 每个批次生成几十万个满足要求的订单号推送到Redis里。然后该进程定时检测队列中的已有ID数量, 当已有数量小于间隔时间预计下单量的两倍时, 再产生几十万个订单ID。 
其他进程从Redis里获取即可。

还需要创建一个粘合层, 用来将上面的库存服务与订单服务组装在一起。
核心原则是要保证任何时间断电,一个库存扣减一定要对应一笔订单。

如何对系统进行调优

  • 可以用Strace / Callgrind查看所有的系统调用, 如果是Malloc的瓶颈可以实现一个内存池。
  • 来自网卡的中断默认会打到CPU0上, 如果是Numa架构,可以用Numactl将进程绑定到其他核, 也能提升CPU的缓存利用率。
  • BSD的网卡支持Polling 减少中断的冲击,  Linux下支持Bounding提高可靠性。如果用云,就不用考虑这个问题了。

系统设计的原则

  • 尽量把流控环节前置,前端逻辑,CDN实在没办法了再去动后端。
  • 将分散的瓶颈点进行合并, 比如将API分组后, 接口问题就收敛到API的版本查询.  而基于版本与前端配合还可以做内容预加载。
  • 对于MQ, 核心原则是小消息大运算, 如果堆积消息超过内存, 就会用到磁盘,性能会急剧下降。

    ▼  更多精彩文章 ▼






以上是关于如何设计秒杀系统?的主要内容,如果未能解决你的问题,请参考以下文章

超详细:如何设计出健壮的秒杀系统?

进阶:秒杀系统是如何设计的?

面试官:如何设计出骚气的秒杀系统?

秒杀系统设计原理

秒杀系统设计最全攻略!!

带你详细了解秒杀系统设计思路