电商秒杀系统设计
【业务背景】
你作为一个电商创业公司的架构师,负责设计 6.18 大促秒杀系统的设计,你们的业务模式如下:
1. 你们挑选选品各大电商平台上畅销和好评的商品进行销售,每个品类不超过 20 个商品,目前做了 10 个品类;
2. 本次 6.18 秒杀选择了 1000 个充电宝,10 台 iPhone 12 作为秒杀商品;
3. 正常的日活大约 100 万用户;
4. 老板要求万无一失。
【技术背景】
1. 技术团队以 Java 为主,已经落地了微服务架构;
2. 主要渠道是自有的 App(包括 iOS 和 Android)和微信小程序,为了促进用户转化为 App 用户,只有下载 App 才能参加秒杀活动;
3. 目前只有单机房。
对前面的业务背景描述,进行核心业务场景假设,秒杀系统核心系统包括:
日活 100W 的系统,假设日活用户占 10%,注册用户有 100/10% = 1000W
正常售卖商品数:10 个品类,每个品类不超过 20 个商品,总商品数目为:10*20=200 个商品,假设定期会对这些热销和好评的商品进行更新,更新评率为每周更新所有 200 个商品,为满足未来 2 年的增长,200*52*2=20000+商品
由于秒杀系统仅 618 使用,因此无需隔离热点商品信息,如后续需要演进为每日秒杀的系统,则可以做单独的热点商品存储和正常商品隔离
假设每个上架的商品都是有库存的,库存记录数大致 20000+
秒杀库存,是否需要设计隔离的热点商品库存也看是否会演进每日秒杀的系统
假设该电商系统 100W 日活用户,实际每日下单的用户占 50%,每日产生 50W 订单,满足未来 2 年增长 365*2*50=3.65 亿订单数据
假设下单的用户都进行了支付,少量因为订单因为各种填写错误等原因未支付的订单,因此支付账单约 3.65 亿
数据特点分析:
根据上面的分析,存储架构设计包括
采用多级负载均衡架构
采用多级缓存架构设计
1.CDN 缓存静态商品信息,拦截大部分商品 QPS
2.APP 端缓存,秒杀预告预先放出了商品信息,可缓存部分资源到 APP 内部缓存
3.应用程序本地缓存,可存商品信息等
4.分布式缓存 RedisCluster 集群,存储用户信息,商品信息,秒杀限流任务队列
秒杀系统架构如何设计
这种高频系统需要考虑的因素很多。
如果在一分钟内会有上百万次请求, 那么1秒钟就要处理1万多次请求。 那么我们分析一下延迟:
网络延迟
系统IO延迟
内存延迟
缓存延迟
数据库延迟
对于网络延迟,没有很好的解决方法,这个跟用户的网络环境有关
对于系统IO, 不太推荐用多线程以及线程池模型。 多线程创建销毁都会有很大的额外开销, 线程池会有等待延迟。 推荐使用libevent这类多路io的框架, 可以在一个线程内完成IO非常轻量
对于内存延迟, 如果我们在短时间内要做大量的业务,建议使用slab这类内存对象方式分配内存,这样可以减少内存分配器带来的开销。 处理完的业务可以放在队列中,可以单独设计一个线程处理队列来给用户response(response延迟并不是那么重要)。另外有大量优化的地方, 例如排除cpu缓存伪共享,集成第三方高性能内存分配器等等手段, 如果有需求可以研究一下。
一般秒杀系统session数据会放在缓存中,例如redis。 如果请求多了, 那么流量会全部压到一个redis的server上,会造成轻微延迟(redis是单线程队列), 这时候可能需要做一个主从系统,不过公司的硬件环境不好有可能会有反效果, 一般情况下1s处理几万次请求还是没有多大问题的。
数据库不要动态写,肯定慢,秒杀结束后一次性把redis的transactions 同步进去。
处理IO建议不要直接用后台服务器, 建议做几个io服务器和客户端连接, 接到客户端请求后用rpc框架投到你的后台。 一个电脑的socket多了后性能下降很快。
(二)微信红包高并发系统设计方案(1)
2017年1月28日,正月初一,微信公布了用户在除夕当天收发微信红包的数量——142亿个,而其收发峰值也已达到76万每秒。百亿级别的红包,如何保障并发性能与资金安全?这给微信带来了超级挑战。面对挑战,微信红包在分析了业界“秒杀”系统解决方案的基础上,采用了 SET化、请求排队串行化、双维度分库表 等设计,形成了独特的高并发、资金安全系统解决方案。实践证明,该方案表现稳定,且实现了除夕夜系统零故障运行。概要:
一、业务 特点 :海量的并发要求;严格的安全级别
二、技术 难点 :并发请求抢锁;事务级操作量级大;事务性要求严格
三、解决高并发问题 通常 使用的 方案 :
1.使用内存操作替代实时的DB事务操作(优点:内存操作替代磁盘操作,提高了并发性能。)
2使用乐观锁替代悲观锁。应用于微信红包系统,则会存在下面三个问题:滚并返回失败;并发大失败,小成功。DB压力大。
四、微信 红包 系统的高并发解决 方案 :
1.系统垂直SET化,分而治之。
2.逻辑Server层将请求排队,解决DB并发问题。
3.双维度库表设计,保障系统性能稳定
类似“秒杀”活动,群里发一个红包=“秒杀”商品上架;抢红包的动作=“秒杀”的查询库存;拆红包=“秒杀”
同一时间有10万个群里的用户同时在发红包,那就相当于同一时间有10万个“秒杀”活动发布出去。10万个微信群里的用户同时抢红包,将产生海量的并发请求。
微信红包是微信支付的一个商户,提供资金流转服务。
用户发红包=购买一笔“钱”(在微信红包这个商户上),并且收货地址是微信群。当用户支付成功后,红包“发货”到微信群里,群里的用户拆开红包后,微信红包提供了将“钱”转入折红包用户微信零钱的服务。
资金交易业务比普通商品“秒杀”活动有更高的安全级别要求。普通的商品“秒杀”商品由商户提供,库存是商户预设的,“秒杀”时可以允许存在“超卖”、“少卖”的情况。但是对于微信红包,100元不可以被拆出101元;领取99元时,剩下的1元在24小时过期后要精确地退还给发红包用户,不能多也不能少。
在介绍微信红包系统的技术难点之前,先介绍下简单的、典型的商品“秒杀”系统的架构设计,如下图所示。
该系统由接入层、逻辑服务层、存储层与缓存构成。Proxy处理请求接入,Server承载主要的业务逻辑,Cache用于缓存库存数量、DB则用于数据持久化。
一个“秒杀”活动,对应DB中的一条库存记录。当用户进行商品“秒杀”时,系统的主要逻辑在于DB中库存的操作上。一般来说,对DB的操作流程有以下三步:
a. 锁库存
b. 插入“秒杀”记录
c. 更新库存
a.锁库存是为了 避免 并发请求时出现“ 超卖 ”情况。同时要求这 三步操作 需要在 一个事务 中完成(难点:并发请求抢锁)。
***个事务完成提交之前这个锁一直被***个请求占用,后面的所有请求需要 排队等待 。同时参与“秒杀”的用户越多,并发进DB的请求越多,请求 排队越严重 。
红包系统的设计上, 除了并发请求抢锁之外,还有以下两个突出难点 :
首先,事务级操作量级大 。上文介绍微信红包业务特点时提到,普遍情况下同时会有数以万计的微信群在发红包。这个业务特点映射到微信红包系统设计上,就是有数以万计的“并发请求抢锁”同时在进行。这使 得DB的压力 比普通单个商品“库存”被锁要大很多倍。
其次,事务性要求严格 。微信红包系统本质上是一个资金交易系统,相比普通商品“秒杀”系统有更高的事务级别要求。
普通商品“秒杀”活动系统,解决高并发问题的方案,大体有以下几种:
如图2所示,将“实时扣库存”的行为上移到 内存Cache中操作 ,内存Cache操作成功直接给Server返回成功,然后 异步落DB持久化 。
优点:提高了并发性能。
缺点: 在内存操作 成功 但 DB持久化失败 ,或者内存 Cache故障 的情况下,DB持久化会 丢数据 ,不适合微信红包这种资金交易系统。
商品“秒杀”系统中,乐观锁的具体应用方法,是在DB的“库存”记录中维护一个版本号。在更新“库存”的操作进行前,先去DB获取当前版本号。在更新库存的事务提交时,检查该版本号是否已被其他事务修改。如果版本没被修改,则提交事务,且版本号加1;如果版本号已经被其他事务修改,则回滚事务,并给上层报错。
这个方案解决了“并发请求抢锁”的问题,可以提高DB的并发处理能力。
应用于微信红包系统,则会存在下面三个问题 :
1.在并发抢到相同版本号的拆红包请求中, 只有一个能拆红包成功 , 其他的请求 将事务回滚并返回失败,给用户 报错 ,用户体验完全不可接受。
2.将会导致 ***时间 同时拆红包的用户有一部分直接 返回失败 ,反而那些“ 手慢 ”的用户,有可能因为 并发减小 后拆红包 成功 ,这会带来用户体验上的负面影响。
3.会带来 大数量 的 无效 更新 请求 、事务 回滚 ,给 DB 造成不必要的额外 压力 。
微信红包用户发一个红包时,微信红包系统生成一个ID作为这个红包的唯一标识。接下来这个红包的所有发红包、抢红包、拆红包、查询红包详情等操作,都根据这个ID关联。
红包系统根据这个红包ID,按一定的规则(如按ID尾号取模等),垂直上下切分。切分后,一个垂直链条上的逻辑Server服务器、DB统称为一个SET。
各个SET之间相互独立,互相解耦。并且同一个红包ID的所有请求,包括发红包、抢红包、拆红包、查详情详情等,垂直stick到同一个SET内处理,高度内聚。通过这样的方式,系统将所有红包请求这个巨大的洪流分散为多股小流,互不影响,分而治之,如下图所示。
这个方案解决了同时存在海量事务级操作的问题,将海量化为小量。
红包系统是资金交易系统,DB操作的事务性无法避免,所以会存在“并发抢锁”问题。但是如果到达DB的事务操作(也即拆红包行为)不是并发的,而是串行的,就不会存在“并发抢锁”的问题了。
按这个思路,为了使拆红包的事务操作串行地进入DB,只需要将请求在 Server层以FIFO ( 先进先出 )的方式排队,就可以达到这个效果。从而问题就集中到Server的FIFO队列设计上。
微信红包系统设计了分布式的、轻巧的、灵活的FIFO队列方案。其具体实现如下:
首先,将同一个红包ID的所有请求stick到同一台Server。
上面SET化方案已经介绍,同个红包ID的所有请求,按红包ID stick到同个SET中。不过在同个SET中,会存在多台Server服务器同时连接同一台DB(基于容灾、性能考虑,需要多台Server互备、均衡压力)。
为了使同一个红包ID的所有请求,stick到同一台Server服务器上,在SET化的设计之外,微信红包系统添加了一层基于红包ID hash值的分流,如下图所示。
其次,设计单机请求排队方案。
将stick到同一台Server上的所有请求在被接收进程接收后,按红包ID进行排队。然后 串行地进入worker进程 (执行业务逻辑)进行处理,从而达到 排队 的效果,如下图所示。
最后,增加memcached控制并发。
为了 防止 Server中的请求队列过载导致队列被降级,从而所有请求 拥进DB ,系统增加了与Server服务器同机部署的 memcached ,用于控制拆同一个红包的 请求并发数 。
具体来说,利用memcached的 CAS原子累增操作 ,控制同时进入 DB执行拆红包事务的请求数 ,超过预先设定数值则 直接拒绝服务 。用于 DB负载升高时的降级 体验。
通过以上三个措施,系统有效地 控制了DB的“并发抢锁” 情况。
红包系统的分库表规则,初期是根据 红包ID的hash值 分为多库多表。随着红包数据量逐渐增大,单表数据量也逐渐增加。而DB的性能与单表数据量有一定相关性。当单表数据量达到一定程度时,DB性能会有大幅度下降,影响系统性能稳定性。采用 冷热分离 ,将历史冷数据与当前热数据分开存储,可以解决这个问题。
系统在以 红包ID维度 分库表的基础上,增加了以 循环天分表的维度 ,形成了 双维度分库表 的特色。
具体来说,就是分库表规则像db_xx.t_y_dd设计,其中,xx/y是红包ID的 hash值后三位 ,dd的取值范围在01~31,代表一个月天数最多 31 天。
通过这种双维度分库表方式,解决了DB单表数据量膨胀导致性能下降的问题,保障了系统性能的稳定性。同时,在热冷分离的问题上,又使得数据搬迁变得简单而优雅。
综上所述,微信红包系统在解决高并发问题上的设计,主要采用了SET化分治、请求排队、双维度分库表等方案,使得单组DB的并发性能 提升了8倍 左右,取得了很好的效果。
分布式应用框架Akka详解
秒杀系统的架构设计
秒杀系统,是典型的短时大量突发访问类问题。对这类问题,有三种优化性能的思路:
写入内存而不是写入硬盘
异步处理而不是同步处理
分布式处理
用上这三招,不论秒杀时负载多大,都能轻松应对。更好的是,Redis能够满足上述三点。因此,用Redis就能轻松实现秒杀系统。
用我这个方案,无论是电商平台特价秒杀,12306火车票秒杀,都不是事:)
下面介绍一下为什么上述三种性能优化思路能够解决秒杀系统的性能问题:
1. 写入内存而不是写入硬盘
传统硬盘的读写性能是相当差的。SSD硬盘比传统硬盘快100倍。而内存又比SSD硬盘快10倍以上。因此,写入内存而不是写入硬盘,就能使系统的能力提升上千倍。也就是说,原来你的秒杀系统可能需要1000台服务器支撑,现在1台服务器就可以扛住了。
你可能会有这样的疑问:写入内存而不是持久化,那么如果此时计算机宕机了,那么写入的数据不就全部丢失了吗?如果你就这么倒霉碰到服务器宕机,那你就没秒到了,有什么大不了?
最后,后面真正处理秒杀订单时,我们会把信息持久化到硬盘中。因此不会丢失关键数据。
Redis是一个缓存系统,数据写入内存后就返回给客户端了,能够支持这个特性。
2. 异步处理而不是同步处理
像秒杀这样短时大并发的系统,在性能负载上有一个明显的波峰和长期的波谷。为了应对相当短时间的大并发而准备大量服务器来应对,在经济上是相当不合算的。
因此,对付秒杀类需求,就应该化同步为异步。用户请求写入内存后立刻返回。后台启动多个线程从内存池中异步读取数据,进行处理。如用户请求可能是1秒钟内进入的,系统实际处理完成可能花30分钟。那么一台服务器在异步情况下其处理能力大于同步情况下1800多倍!
异步处理,通常用MQ(消息队列)来实现。Redis可以看作是一个高性能的MQ。因为它的数据读写都发生在内存中。
3. 分布式处理
好吧。也许你的客户很多,秒杀系统即使用了上面两招,还是捉襟见肘。没关系,我们还有大招:分布式处理。如果一台服务器撑不住秒杀系统,那么就多用几台服务器。10台不行,就上100台。分布式处理,就是把海量用户的请求分散到多个服务器上。一般使用hash实现均匀分布。
这类系统在大数据云计算时代的今天已经有很多了。无非是用Paxos算法和Hash Ring实现的。
Redis Cluster正是这样一个分布式的产品。
使用Redis实现描述系统
Redis和Redis Cluster(分布式版本),是一个分布式缓存系统。其支持多种数据结构,也支持MQ。Redis在性能上做了大量优化。因此使用Redis或者Redis Cluster就可以轻松实现一个强大的秒杀系统。
基本上,你用Redis的这些命令就可以了。
RPUSH key value
插入秒杀请求
当插入的秒杀请求数达到上限时,停止所有后续插入。
后台启动多个工作线程,使用
LPOP key
读取秒杀成功者的用户id,进行后续处理。
或者使用LRANGE key start end命令读取秒杀成功者的用户id,进行后续处理。
每完成一条秒杀记录的处理,就执行INCR key_num。一旦所有库存处理完毕,就结束该商品的本次秒杀,关闭工作线程,也不再接收秒杀请求。
要是还撑不住,该怎么办
也许你会说,我们的客户很多。即使部署了Redis Cluster,仍然撑不住。那该怎么办呢?
记得某个伟人曾经说过:办法总比困难多!
下面,我们具体分析下,还有哪些情况会压垮我们架构在Redis(Cluster)上的秒杀系统。
脚本攻击
如现在有很多抢火车票的软件。它们会自动发起http请求。一个客户端一秒会发起很多次请求。如果有很多用户使用了这样的软件,就可能会直接把我们的交换机给压垮了。
这个问题其实属于网络问题的范畴,和我们的秒杀系统不在一个层面上。因此不应该由我们来解决。很多交换机都有防止一个源IP发起过多请求的功能。开源软件也有不少能实现这点。如linux上的TC可以控制。流行的Web服务器Nginx(它也可以看做是一个七层软交换机)也可以通过配置做到这一点。一个IP,一秒钟我就允许你访问我2次,其他软件包直接给你丢了,你还能压垮我吗?
交换机撑不住了
可能你们的客户并发访问量实在太大了,交换机都撑不住了。
这也有办法。我们可以用多个交换机为我们的秒杀系统服务。
原理就是DNS可以对一个域名返回多个IP,并且对不同的源IP,同一个域名返回不同的IP。如网通用户访问,就返回一个网通机房的IP;电信用户访问,就返回一个电信机房的IP。也就是用CDN了!
我们可以部署多台交换机为不同的用户服务。 用户通过这些交换机访问后面数据中心的Redis Cluster进行秒杀作业。
总结
有了Redis Cluster的帮助,做个支持海量用户的秒杀系统其实So Easy!
这里介绍的方案虽然是针对秒杀系统的,但其背后的原理对其他高并发系统一样有效。
最后,我们再重温一下高性能系统的优化原则:
写入内存而不是写入硬盘
异步处理而不是同步处理
分布式处理
原文:
Redis 秒杀系统的设计与实现
还记得刚工作那会,每每听到大牛们聊技术,各种专业术语,巴拉巴拉的,简直像是在听天书,比如什么中间件、分布式、SOA、无状态、热更新、懒加载、ACID、LVS、LDAP、VIP、CDN、负载均衡、鲁棒性、POJO、DSL、DI、IOC,太多太多了。一转眼快 10 年过去了,当很多新人再问到我这些名词的时候,我就在想,能不能用通俗易懂的大白话,就能聊明白这些专业的技术知识呢?
最近,给几个公司做技术咨询,经常会聊到秒杀系统。所以,借这次机会,尝试用大白话和大家聊聊 Redis 秒杀系统的设计与实现,。
说起 “秒杀”,我相信大家肯定都耳熟能详了,双十一零点抢购、手机整点抢购、抢火车票、1 元秒杀、抢红包等等,都可以说是秒杀的各种应用场景了。
秒杀系统的设计 ,难就难在,在极短的时间内,应对瞬时涌入平时成百上千倍的巨大流量,还包括各种攻击刷量作弊等未知流量,最终我们要保证在用户体验顺畅良好的情况下,不能多卖或者少卖。
而当我们公司决定要做秒杀系统的时候,我就去找业务,到时大概会有多少 UV,不知道 10 倍或者 100 倍?然后去找老板,给技术多少预算,最多平时的 10 倍不能再多了,当然越少越好,呵呵,也就是说让我们用平时最多 10 倍的预算去解决不可预估的用户流量,怎么做?要是有钱直接扔 1 万台服务器跑去吧,钱能解决的事就不是事,但问题是现在还没那么多钱,还要把事情搞定。
在聊秒杀系统设计之前,让我们先回到现实生活中,聊聊常见的“秒杀”场景和秒杀场景的独有特点,以及它们都是怎么应对的,在应对过程中都需要注意什么。
日常生活中,其实也有很多秒杀场景,比如,早上 9 点超市开门,老大爷老大妈抢购蔬菜水果,是不是? 还有,新楼盘开盘抢购,是不是? 股市开盘、交易所现场,是不是?
对的,生活中其实有太多类似场景了, 你有没有发现“秒杀”的独有特点呢?
记住了上面三个特点,我们就可以区分和确定秒杀的业务场景了。 这里我举一个特别的例子, 你说挤公交车,算不算秒杀场景呢?
下面,我再和大家聊一个关于抢猪肉的故事。
在保安部门充分讨论之后,保安大队长决定通过以下安排,在保证人员安全的前提下,还要做到相对公平。
后来,活动井然有序的开始了,但是由于猪肉销售场地太远,销售窗口又少,老大爷和老大妈们买肉又精挑细选,导致整个过程很漫长,而且外面等候的人们都开始骚动起来,这个时候保安大队长赶紧找到经理:
故事讲完了,如果我们把上面的故事,理解为秒杀业务场景,我们就可以总结出一个 秒杀系统的设计原则 了:
如何设计一个秒杀系统
1) 对现有网站业务的冲击
因为秒杀活动只是网站营销的一个附加活动,这个活动具有时间短,并发访问量大的特点,如果和网站原有应用部署在一起,必然会对现有业务造成冲击,稍有不慎可能导致整个网站瘫痪。
2) 高并***况以及数据库的负载
用户在秒杀开始前,通过不停的刷新浏览器页面以保证不会错过秒杀,这些请求如果按照一般的网站应用架构,访问应用服务器、连接数据库,会对应用服务器、数据库服务器造成极大的负载压力。
3) 突然增加的网络和服务器带宽
假设商品页面大小200K(主要是商品图片大小),那么需要的网络和服务器带宽是2G(200K×10,000),这些网络带宽是因为秒杀活动新增的,超过网站平时使用的带宽。
4) 直接下单
秒杀的游戏规则是到了秒杀时间才能开始对商品下单购买,在此时间点之前,只能浏览商品信息,不能下单。而下单页面也是一个普通的URL,如果得到这个URL,不用等到秒杀开始就可以下单了。
5) 防止机器秒杀
防止网上的一些“秒杀器”
针对上面的5个问题,对应的策略如下:
1) 秒杀系统独立部署
为了避免因为秒杀活动的高并发访问而拖垮整个网站,使整个网站不必面对蜂拥而来的用户访问,将秒杀系统独立部署,如果需要,还可以使用独立的域名,以和网站完全隔离,即使秒杀系统崩溃了,也不会对网站造成任何影响。
关于秒杀系统设计和秒杀系统设计体会的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。