如何设计秒杀系统?
Posted IT东方会
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何设计秒杀系统?相关的知识,希望对你有一定的参考价值。
-
静态API:内容可被缓存, 并且信息公开访问的结果。
-
动态API:也就是具体的业务接口。比如下单,比如登录。
针对静态API,完全推送到CDN上。 针对静态API有一个全局版本时间戳, 并有一个专门的动态接口来获取他的变更状态。
一定要尽量减少动态接口的数量, 所以很有必要把秒杀业务和日常商城销售在架构上进行拆分。
在解决掉流量风险之后, 后面的重点在于减少对数据库的冲击, 没有线程池的mysql在超过128个并发任务就会造成性能的急剧下降。尤其在库存扣减与大量订单写入数据库都会造成锁表导致不可用。
我们首先要实现一个无锁的库存管理服务。这里要用到一个叫CAS的CPU指令, Compare-And-Swap.
功能就是字面意义. 先比较某个内存地址上的数值v, 如果是m(compare), 则交换v和n的值(swap).
实际实现思路就是写一个循环, 比如某进程做库存扣减:
-
-
cas传入库存值的内存地址, compare是否仍为n,
-
-
如果不是n. 说明被其他用户购买过程所修改, 则从 第一条 重复开始.
在Go的sync包里, 有许多多协程场景下的有用指令, CAS指令就在sync/atomic 里。
用这样的办法可以实现一个单节点单SKU每秒支撑几万次库存扣减的库存服务。
订单的创建速度会大于数据库写入速度,因此我们会引入MQ去降低对数据库的冲击。
订单写入之后马上就会有查询以及更新支付状态, 要保证未落盘的订单数据与系统已有订单有一致的查询与更新接口,以这个目标为核心创建一个服务对该结构进行封装。
一个关键点是订单号, 订单号通常有业务规则约定, 要有时间信息, 号码不能连续否则会存在安全问题, 要保证分布式系统下的唯一性等等。
在我的实践里比较简单粗暴,一个独立的服务进程, 每个批次生成几十万个满足要求的订单号推送到Redis里。然后该进程定时检测队列中的已有ID数量, 当已有数量小于间隔时间预计下单量的两倍时, 再产生几十万个订单ID。
还需要创建一个粘合层, 用来将上面的库存服务与订单服务组装在一起。
核心原则是要保证任何时间断电,一个库存扣减一定要对应一笔订单。
-
可以用Strace / Callgrind查看所有的系统调用, 如果是Malloc的瓶颈可以实现一个内存池。
-
来自网卡的中断默认会打到CPU0上, 如果是Numa架构,可以用Numactl将进程绑定到其他核, 也能提升CPU的缓存利用率。
-
BSD的网卡支持Polling 减少中断的冲击, Linux下支持Bounding提高可靠性。如果用云,就不用考虑这个问题了。
-
尽量把流控环节前置,前端逻辑,CDN实在没办法了再去动后端。
-
将分散的瓶颈点进行合并, 比如将API分组后, 接口问题就收敛到API的版本查询. 而基于版本与前端配合还可以做内容预加载。
-
对于MQ, 核心原则是小消息大运算, 如果堆积消息超过内存, 就会用到磁盘,性能会急剧下降。
以上是关于如何设计秒杀系统?的主要内容,如果未能解决你的问题,请参考以下文章
超详细:如何设计出健壮的秒杀系统?
进阶:秒杀系统是如何设计的?
面试官:如何设计出骚气的秒杀系统?
秒杀系统设计原理
秒杀系统设计最全攻略!!
带你详细了解秒杀系统设计思路