我都今天没答题为啥显示我抢单数量上限了?

“收银行卡四件套,有需要微我”“全新卡资料齐全”……在网络平台活跃着一些中间商,他们常用暗语交流,只因收购和销售的产品属于监管明令禁止倒卖的“违禁品”——银行卡“四件套”。继《拆解银行卡“四件套”贩卖套路》后,北京商报记者注意到,在市场上还存在一种出借银行卡赚取佣金这种看似诱人的“跑分”兼职项目。殊不知这种“躺平”赚快钱的方法看似简单,一旦银行卡、收款码落入中间商手中,用户很可能成为博彩、境外洗钱的工具。

“有个赚钱的路子要不要试试?”“你有几张卡?给你返佣金,用你的卡刷刷流水就可以”……虽说动手出借、提供个人支付结算账号密码太简单,但殊不知你已经落入“黑产”陷阱。北京商报记者注意到,目前在市场上存在一种地下交易,中间商们流转于各大社交平台寻找“鱼饵”,而他们做的就是银行卡抢单“跑分”的生意,“跑分”就是持卡人需要交出自己的银行卡、支付宝收款码替别人代为收款来赚取佣金,佣金的比例在1%-5%。

“跑分”的形式分为线上、线下,线上需要提前缴纳押金、保证金,还需要提供银行卡账号、支付宝账号、身份证、手机号码等个人信息。

“佣金9500元”“这是最后一单了”……卡贩子中间商王磊宁(化名)每天都在社交群寻找可以提供银行卡抢单“跑分”的用户,他手里有一笔9万元的“物料”需要尽快洗白。王磊宁给持卡人开出的佣金也非常丰厚,他向北京商报记者介绍称,“线上押金收800元,先给你卡里转账9万元,你转到另一个银行卡,交易完成后返还押金和9500元佣金”。

“分批转,不能一次性转,一次性风险太大了,或者是几个小时转一次,这样风险最低。” 王磊宁保证,“我保你5单之内一点事都没有,就这个单子利润大,博彩的风险是最低的。”

和王磊宁一样,还有多位中间商也做着抢单“跑分”的生意,且开出的佣金都价格不菲。“你要做就把银行卡账号和支付宝收款码发过来,我们这边是团队不是个人,有多个档位选择,比如押金350元,可以‘跑分’1.18万元,你能挣1800元佣金;押金500元,可以‘跑分’2.3万元,你可以挣3000元佣金。”另一位中间商向北京商报记者介绍称,“每天可以做2单,只要额度不超过5万元一般都没风险”。

和此前银行卡贩卖“四件套”相同的是,“跑分”也有线下模式,持卡人、中间商、洗钱交易对手在同一个地方聚集,共同会面。“线下不要押金,你最好办一张大额转账的银行卡过来,包吃包住包车费,卡用完你带走,线下佣金最高能拿到8%。”一位中间商说道。

出借银行卡、支付宝收款码这一行为看似简单,但殊不知这种“躺平”赚快钱的方法,也会让用户以身试法沦为“阶下囚”。北京寻真律师事务所律师王德怡在接受北京商报记者采访时分析称,根据刑法规定,自然人或者单位明知他人利用信息网络实施犯罪,为其犯罪提供互联网接入、服务器托管、网络存储、通讯传输等技术支持,或者提供广告推广、支付结算等帮助,情节严重的,构成帮助信息网络犯罪活动罪。出租出售银行卡只是帮信罪的一种行为方式。提供支付宝收款码的行为,本质上也是一种支付结算帮助行为,行为人首先可能涉嫌的是帮信罪。由于相应的银行卡并不在本人手中,所以上述银行卡可能被用于不法用途;而根据相关司法解释,相关银行卡内的资金流水额将是量刑的依据和参考。因此,出借银行卡或支付码的行为存在较大的刑事风险。

和银行卡贩卖“黑产”一致,中间商收到赃款时为了确保安全,需要先“跑分”洗钱,把钱洗白后再转入自己账户。为了拓展业务,还有部分中间商开发了“跑分”平台,不断发展线下代理、利用代收款等幌子诓骗用户。

那么此类“跑分”平台操作究竟是如何运作的呢?北京商报记者联系到一位“跑分”平台中间商,在她的指引下,记者下载了一款应用程序,该应用程序便是他们用来发布订单、接收订单、交易的“跑分”平台。

北京商报记者进一步注册后发现,用户只要成功注册并进一步完善信息后,就可以抢单。用户需要在卡里预留元本金,还需要开通手机银行和短信通知,1张卡每天的转账金额不超过5万元。

当中间商需要转移赃款时,会将转账需求发布在平台上供“跑分客”抢单,“跑分客”抢单成功后,再经多个不同账户层层转账,从而达到洗钱目的。例如,抢单成功后,用户向A账户充值5000元,此时A的余额会显示为5600元。这时“跑分”平台的玩家就会将累计的资金转移至用户收款码中,600元佣金到手。

在北京商报记者加入的“跑分”群中,已有不少用户被诓骗进群,这些用户大部分对“跑分”这一运作模式并不知情,也有用户在群中提问:“是否为诈骗平台?”不过只要有用户发出质疑,就会被管理员踢出群聊。

而对那些已经从事“跑分”的用户来说,可以继续发展下线扩大团队。一位“跑分”平台管理员介绍称,“拉下线的方式很简单,就是发布兼职内容,找到用户,然后把用户拉入群里就可以,只要用户抢单就可以赚0.5%的佣金”。这样,一个庞大的“跑分”团队被建立,黑产的钱被彻底洗白,用户大多是不知情的“小白”,溯源较难。

“个人账户实际上是用户最敏感的个人信息之一,出借首先会让自己的个人隐私暴露在网络之中,其次可能会间接地成为洗钱等违法行为的帮凶。”博通分析金融行业资深分析师王蓬博直言,“跑分”平台本身就是违规行为,很可能被用于洗钱或者给博彩等非法交易使用,属于重点打击的对象。用户不但可能要承受个人资产的损失和信息的泄露,还有可能需要承担法律责任,或者面临账号被封的情况,不能因小失大。对于此类平台,发现就应该及时跟相应机关同步,配合警方及时介入。

抢单跑分涉嫌“帮信罪”

近年来,银行卡已成为诈骗、洗钱案件高发的根源,依托银行卡犯罪的“跑分”模式更是风险难测,不仅在操作中需要实名、绑卡、上传收款码等,个人信息容易泄露,大部分用户还需要利用自有资金刷流水,也会存在资金损失风险。

一位银行业风控人士向北京商报记者介绍称,抢单“跑分”模式存在洗钱等法律风险、信息泄露风险以及个人信用风险。当前银行也在不断加强对交易流水的监控,一旦出现大额频繁的资金交易,就需要通过提升费率、降低金额、暂停交易甚至销户等进行处罚,同时也会对用户做好反洗钱培训教育。

易观高级分析师苏筱芮表示,出借自己的银行账户,支付宝、微信支付等二维码风险巨大,个人隐私可能会泄露,继而被不法分子利用,存在财产损失风险。广大用户需提升风险防范意识,保护好个人信息安全,出租、出借、出售、购买电话卡、银行账户、营业执照等涉及违法犯罪,要从源头加强法律意识,做一个遵纪守法的好公民。

“对商业银行而言,建议从技术上建立风险识别和警示制度,对交易数量、地点、频率明显有违常理的,应当及时采取人工干预,及时关停相关交易。” 王德怡说道。

鉴于上述“跑分”模式存在多种风险,支付机构也已出手整治,支付宝安全专家在接受北京商报记者采访时表示,支付宝会根据账户注册、登录、实名认证等行为节点环境数据,建立实时策略和实时账户租界模型,针对出借支付宝账户行为进行风险检测;同时,会根据账户出借意图和风险程度进行梯度惩戒,如账户核身、教育警示、答题考核、限制收款、不予开户准入惩戒等。

“同时提醒大家,出售、出租、出借银行卡和收款码等参与‘跑分’的行为容易被犯罪分子利用,可能涉嫌“帮信罪”,切勿将自己账户交由他人使用,谨防诈骗。”上述支付宝安全专家提醒称。北京商报记者 宋亦桐

头两天,我的高中女神突然联系我,把我激动得都打嗝了。

她说结婚后就不上班了,老公总出差不在家,自己闲着实在无聊。

这一句话让我浮想联翩——这是要跟我搞事情?可是我没有宋吉吉的心理素质啊……

接着她问我, “ 有没有在家就能做的赚钱路子? ”

我以为她是找我叙旧的,没想到是来跟我取经的。不能辜负女神的信任,我花了一个多星期,搜集整理了各种“不用上班就能赚钱”的方法。

最后发现路子实在太多,来不及跟女神逐一讲解了,就写篇文章交差吧。

一、最简单的赚钱模式——卖东西

谁家都有闲置不用的东西,扔了不舍得,留着又占地方,挂在网上卖掉帮你 实现 “断舍离”! 不合身的衣服、淘汰的手机、用不上的家具电器、年会礼品、赠送门票 ……别不好意思卖, 你永远猜不到别人缺什么,说不定正好有人缺心眼呢!

有些人心灵手巧,喜欢 DIY一些小东西,如果做成成品出售,价格瞬间翻几番。比如十字绣、小首饰、小摆件,原材料只要几块钱,做好成品再卖就值几十块。还有这两年流行的手工香皂、手工皮包,网上的教程一大堆,做好了能赚成百上千块。

沈阳一位大哥放弃公司和应酬

做手工皮包20年,售价上千元

现在微商的口碑不太好,除非有好的货源,不然不仅赚不到钱,还把朋友都得罪了。我不提倡做微商代理,大部分微商代理都是变相的传销,坑了朋友也坑了自己。

下面给实在缺钱,又没技能和资源的人指条路子——出卖自己。

别误会,我这里没有黄赌毒!我是说,比如长发及腰的女士可以卖头发,价格几百元不等 (但是收头发的都是亲自下刀,可能会被贴着头皮剃光,你得做好心理准备) 。 再比如捐精、捐卵、卖血、试药 ……不过这些都是以牺牲健康为代价,如果不是走投无路,大家还是不要尝试了。

不知道去哪卖?在互联网时代,打开手机,全世界都是你的客户! 下面推荐几个比较正规的二手交易 APP:

阿里巴巴旗下二手转卖APP,直接关联淘宝账户,在淘宝上购买的订单,可以“一键转卖”,十分方便。“鱼塘”功能便于近距离交易,异地交易可以直接通过“菜鸟裹裹”下单邮寄。

58同城旗下,可以关联微信好友,你的微信好友可以看到你的转卖。平台提供二手手机验货估价,方便更快卖出。

只做品牌、正品商品二手买卖,买卖二手手机,平台可以免费验机。

市面上还有很多专做一个品类的二手交易平台,比如专门做女性奢侈品二手转卖的、做母婴用品租赁的、专做二手手机的等等。

淘宝网、赶集网、58同城网

这些神奇的网站,卖什么的都有,你可以把任何东西都挂上去。

朋友圈、微博等社交媒体

最后别忘了朋友也是潜在客户,你可以直接在朋友圈发布消息,也可以创建一个微店,在朋友圈卖些闲置。

(发朋友圈也是有技巧的,刷屏很容易被拉黑,要想赚人钱先要赚人心,如何更好地运营朋友圈,可以看看之前的文章《不刷屏,如何让朋友买你的东西》)

卖货时别忘了在各个平台都同步发布,多一个平台,多一次曝光。

二、较新兴的赚钱模式——卖时间

随着移动互联网兴起,前几年出了一种网赚新玩法 —— 卖时间 。 只要有一个手机,下载几个 APP就有钱赚 ,虽然赚不到大钱,但是赚点零花钱还是可以的。

这类手机APP有很多,大致可以分为以下几类:

手机试玩类 APP主要就是让你 试玩一些游戏或者应用,达到规定的要求后,给试玩者积分或者现金作为回报。按照 APP宣传的,这些钱都是做推广的公司出,属于一种广告宣传,从这点上来讲,确实具备赚钱的可能性。

任务没什么难度,无非就是看广告视频,下载应用或者游戏,注册账号或者 试玩达到规定时间之类的。 每条任务的奖励是 0.1-0.5元,苹果系统的奖励相对高一些。如果你能“收徒弟”邀请新人,还有额外分成。 投入时间去做,每个月也能赚到几百块的零花钱。

现在市面上体验试玩类的APP特别多, 在应用商店搜索关键词“赚钱”,就能看到一大堆。 不过这类APP可能存在违规风险,存活时间不一定,没准啥时候就被应用商店给下架或者倒闭了,所以要记得及时提现。

还有一类网赚方法是通过答题或填调查问卷来赚钱,比试玩更靠谱一些。手机上有不少这样的 APP,比如: 赚点、微调查、爱调研。

但是经过我的试用,以上几个APP的体验都不太好,不是页面太卡就是问卷太少。不过没关系,电脑上的调查平台更多,我们可以在电脑网页上操作。

我之前发过一篇文章,详细讲解了如何通过做调查问卷来赚钱,一个月轻松入账几百元,戳蓝字链接即可查看:

《只要你有时间,在家上网就能赚一千!》

还有一种网赚方法,只要转发文章或转发各类广告推广消息就能赚钱,就是这么容易。类似功能的手机APP有:

淘宝客大家都不陌生,手机下载一个淘宝联盟APP,把里面的商品推广信息转发到微信群或朋友圈中,只要有人通过你的推广链接购买了商品,你就可以获得相应的佣金。

关于这个我之前也写过文章,戳蓝色链接看详细赚钱流程:《零成本,有人日赚30万,这个项目你也能干!》

这是一个手机APP,转发、浏览、注册里面的各类推广信息,即可获得收益,就是钱有点少。

只要通过赚点点APP阅读转发文章,或者关注公众号,都能赚取相应的报酬。

众包指的是公司或机构把过去由员工执行的工作任务,外包给非特定的、大型的大众网络的做法。说白了,就是大公司的小打杂不够用 ,就通过网络把活派给普通人。现在做众包的平台有不少,下面这几个 APP是比较靠谱的。

这是目前用户最多的众包APP,通过去超市拍照收集商品信息、到小区电梯检查广告投放情况,参与用户调研、免费尝试新品并提交反馈等等小任务,就能获得相应回报,每个任务回报2.5-3元不等,可以提现至支付宝。

阿里巴巴旗下的众包平台,淘宝、天猫、支付宝、高德、菜鸟都是任务的提供方,任务一般包括照片采集、图片标注,还有去室外验收广告投放情况等,有的任务一次就能赚40元。另外还可以申请成为阿里云客服,平台背景强大,比较安全。

注册认证后,就可以做一个兼职的美团外卖小哥了,你可以选择自己熟悉的店家或小区进行配送,抢单后1小时内送达,每单可以挣3到10块钱,满100元就可以提现。

上面说的这些网赚路子, 有的直接付现金 ,达到一定金额后就可以提现,一般可 提现到支付宝或者微信。 有的奖励是以积分、 Q币等形式发放 ,也可以用来兑换礼品、话费等等。

但要注意的是,现在的 手机赚钱 APP鱼龙混杂,有些APP可能是骗局。 正规的手机赚钱APP无需任何成本,也没有所谓的押金之说,那些需要你缴纳押金、充值之类的都是骗局。 还有的体验类APP,让你下载试玩的可能包含吸费APP, 在下载前要注意甄别,要仔细查看软件要开通的权限。

三、最高级的赚钱模式——卖知识

上面说的那些都没啥技术门槛,谁都能做,不过也只能赚点零花钱,要想赚得更多,就不能光做体力活了,还要动点脑子。 最高级的赚钱模式就是靠卖知识和技能来赚钱。

下面讲几个利用知识和技能在家赚钱的例子,其实没你想得那么难。

威客是指那些通过互联网把自己的智慧、知识、能力、经验转换成实际收益的人。做威客没啥门槛,不看学历和出身,全凭能力吃饭。

什么人可以做威客呢?举几个例子:

你懂会计,可以做代账会计。

你懂法律,可以做法律咨询,起草合同。

你懂设计,可以帮人设计Logo,广告海报。

你会编程,可以帮人做网站开发,APP开发。

你文笔好,可以帮人代笔写软文,写稿子。

你会做PPT,可以帮人做PPT美化。

你外语好,可以去做翻译。

你懂新媒体运营,可以做微信公众号代运营。

你长得好看,可以去做网店模特。

即使你只会忽悠,也可以给人算命起名。

关键是去哪找到能雇佣你的人呢?有一类专门撮合威客交易的网站:

最大的威客网站,类型丰富,需求很多

原来叫威客中国,以设计类为主

主要做网络营销、微博营销、网店服务等,技术门槛不高

主要做IT技术开发外包

-不论是个人还是企业都可以注册,接单前要完善个人信息和实名认证;

-建立自己的店铺,完善的店铺信息更容易被雇主选中;

-在需求大厅里有各种类型的任务,也可以看到雇主的任务要求,找到适合自己的就可以报名参与;

-如果被选用,完成任务后即可收到报酬。

如果你欠缺专业技能也不要紧,你可以选择做 网络营销类 的任务 ,不用什么技术能力,如:做 发贴推广、网店搜索收藏、 QQ群推广 等等,只要肯做就能钱赚。

在上面的威客需求里你能看到刷百度知道问答都能赚钱,除此之外,现在通过百度经验也能赚钱。

-首先注册一个百度经验账号;

-发布一篇原创三星经验并通过审核,即可加入百度回享计划;

-每1000次浏览量有3元、4元、5元不等的收入,还有红包奖励0.2元—50元不等、优秀经验10元奖励、全勤奖励15元—100元不等;

-优质原创,且互动活跃度高的经验,回享度会提高,相应的收入也会增加。

利用今日头条等自媒体赚钱

内容创业这两年特别火爆,也成为大量宅家人士的赚钱渠道。我之前已经说过很多次了,也给大家开过课程讲解整个赚钱流程。

扫码学习如何通过“今日头条”赚钱

现在大部分手机APP都有短视频功能,其实在这些新媒体上发布的小视频,都可以借广告分成来赚钱。

短视频平台有很多:除了各大主流视频网站,如 优酷、爱奇艺、腾讯视频、搜狐视频 等,还有很多新兴自媒体平台,如 快手、今日头条、火山视频、秒拍、美拍、小咖秀 等等。

很多人觉得拍视频很麻烦,其实只要会一些简单的视频剪辑操作就可以。平时我们看视频应该也会发现,流传广的视频大部分都不是原创。

只要把热门视频进行截取就能获得不错的流量收入,比如综艺节目、电影电视剧、明星采访等等。如果还能做 一些拼接和编辑,那就更好了。要想实现更好的传播,还要注意两点: 一是视频不超过 3分钟,二是要起一个吸引眼球的标题。

当然,作为一个自媒体人,我是很鼓励原创的,如果你有能力长期出产优质的原创内容最好。不仅能赚到流量收入,还能打造个人品牌,如果把IP打造出来了,那就是另一个赚钱境界了。

关于在家赚钱,上面说的是一些模式化的方法。事实上,时代发展这么快,共享经济走入人们的视野,我们的身边陆续出现了很多曾经无法想象的赚钱路子,让我们刷新了对工作和赚钱的认知。

之前钱哥写过一篇文章,讲了身边的人做共享民宿、海外代购、共享私厨,丰富了业余生活又赚到了钱,这里面都是真实经历:《不上班,就不能赚钱吗?》

据领英统计,到2020年,将有43%的人成为自由职业者, 赚钱不再和努力工作挂钩,宅在家里也许赚得更多。

不论你是在家带娃的全职主妇,还是有工作但想赚外快的职场人士,不论你是在校学生,还是退休在家,上面的赚钱方法你都可以尝试。

当然了,赚多赚少还是看个人能力,同一条路上,有人能赚到财富自由,有人只能赚到一顿晚饭。

唯一可以肯定的是,如果你不去做,一分钱都赚不到。

你还有什么不错的赚钱路子,分享一下呗。

4万变10万,100%稳赚,关键要有方法!

生病倒赚30万,全靠这么干!

同事说了一句话,让我赚了2000块!

有哪些行业内幕,让你颠覆三观?

财运好的人,都有什么特点?

跟史上第一富豪学习如何“吹牛”致富

喜欢此文,欢迎打赏、点赞、留言!

如果问起秒杀系统如何设计,相信都能说出个123来,但是如果细究其中的细节点,很多人估计就无法很快的打上来了。本文从六个方面,来简要讲一下秒杀系统要如何设计,应该主要哪些事情。

01 | 设计秒杀系统时应该注意的5个架构原则

说起秒杀,我想你肯定不陌生,这两年,从双十一购物到春节抢红包,再到 12306 抢火车票,“秒杀”的场景处处可见。简单来说,秒杀就是在同一个时刻有大量的请求争抢购买同一个商品并完成交易的过程,用技术的行话来说就是大量的并发读和并发写。不管是哪一门语言,并发都是程序员们最为头疼的部分。

同样,对于一个软件而言也是这样,你可以很快增删改查做出一个秒杀系统,但是要让它支持高并发访问就没那么容易了。比如说,如何让系统面对百万级的请求流量不出故障?如何保证高并发情况下数据的一致性写?完全靠堆服务器来解决吗?这显然不是最好的解决方案。

在我看来,秒杀系统本质上就是一个满足大并发、高性能和高可用的分布式系统。今天,我们就来聊聊,如何在满足一个良好架构的分布式系统基础上,针对秒杀这种业务做到极致的性能改进。

架构原则:“4 要 1 不要

”如果你是一个架构师,你首先要勾勒出一个轮廓,想一想如何构建一个超大流量并发读写、高性能,以及高可用的系统,这其中有哪些要素需要考虑。我把这些要素总结为“4 要 1 不要”。
所谓“数据要尽量少”,首先是指用户请求的数据能少就少。请求的数据包括上传给系统的数据和系统返回给用户的数据(通常就是网页)。为啥“数据要尽量少”呢?因为首先这些数据在网络上传输需要时间,其次不管是请求数据还是返回数据都需要服务器做处理,而服务器在写网络时通常都要做压缩和字符编码,这些都非常消耗 CPU,所以减少传输的数据量可以显著减少 CPU 的使用。例如,我们可以简化秒杀页面的大小,去掉不必要的页面装修效果,等等。其次,“数据要尽量少”还要求系统依赖的数据能少就少,包括系统完成某些业务逻辑需要读取和保存的数据,这些数据一般是和后台服务以及数据库打交道的。调用其他服务会涉及数据的序列化和反序列化,而这也是 CPU 的一大杀手,同样也会增加延时。而且,数据库本身也容易成为一个瓶颈,所以和数据库打交道越少越好,数据越简单、越小则越好。

用户请求的页面返回后,浏览器渲染这个页面还要包含其他的额外请求,比如说,这个页面依赖的 CSS/JavaScript、图片,以及 Ajax 请求等等都定义为“额外请求”,这些额外请求应该尽量少。因为浏览器每发出一个请求都多少会有一些消耗,例如建立连接要做三次握手,有的时候有页面依赖或者连接数限制,一些请求(例如 JavaScript)还需要串行加载等。另外,如果不同请求的域名不一样的话,还涉及这些域名的 DNS 解析,可能会耗时更久。所以你要记住的是,减少请求数可以显著减少以上这些因素导致的资源消耗。例如,减少请求数最常用的一个实践就是合并 CSS 和 JavaScript 文件,把多个 JavaScript 文件合并成一个文件,在 URL 中用逗号隔开( URL,然后动态把这些文件合并起来一起返回。

所谓“路径”,就是用户发出请求到返回数据这个过程中,需求经过的中间的节点数。通常,这些节点可以表示为一个系统或者一个新的 Socket 连接(比如代理服务器只是创建一个新的 Socket 连接来转发请求)。每经过一个节点,一般都会产生一个新的 Socket 连接。然而,每增加一个连接都会增加新的不确定性。从概率统计上来说,假如一次请求经过 5 个节点,每个节点的可用性是 99.9% 的话,那么整个请求的可用性是:99.9% 的 5 次方,约等于 99.5%。所以缩短请求路径不仅可以增加可用性,同样可以有效提升性能(减少中间节点可以减少数据的序列化与反序列化),并减少延时(可以减少网络传输耗时)。要缩短访问路径有一种办法,就是多个相互强依赖的应用合并部署在一起,把远程过程调用(RPC)变成 JVM 内部之间的方法调用。在《大型网站技术架构演进与性能优化》一书中,我也有一章介绍了这种技术的详细实现。

所谓依赖,指的是要完成一次用户请求必须依赖的系统或者服务,这里的依赖指的是强依赖。举个例子,比如说你要展示秒杀页面,而这个页面必须强依赖商品信息、用户信息,还有其他如优惠券、成交列表等这些对秒杀不是非要不可的信息(弱依赖),这些弱依赖在紧急情况下就可以去掉。要减少依赖,我们可以给系统进行分级,比如 0 级系统、1 级系统、2 级系统、3 级系统,0 级系统如果是最重要的系统,那么 0 级系统强依赖的系统也同样是最重要的系统,以此类推。注意,0 级系统要尽量减少对 1 级系统的强依赖,防止重要的系统被不重要的系统拖垮。例如支付系统是 0 级系统,而优惠券是 1 级系统的话,在极端情况下可以把优惠券给降级,防止支付系统被优惠券这个 1 级系统给拖垮。

系统中的单点可以说是系统架构上的一个大忌,因为单点意味着没有备份,风险不可控,我们设计分布式系统最重要的原则就是“消除单点”。那如何避免单点呢?我认为关键点是避免将服务的状态和机器绑定,即把服务无状态化,这样服务就可以在机器中随意移动。如何那把服务的状态和机器解耦呢?这里也有很多实现方式。例如把和机器相关的配置动态化,这些参数可以通过配置中心来动态推送,在服务启动时动态拉取下来,我们在这些配置中心设置一些规则来方便地改变这些映射关系。应用无状态化是有效避免单点的一种方式,但是像存储服务本身很难无状态化,因为数据要存储在磁盘上,本身就要和机器绑定,那么这种场景一般要通过冗余多个备份的方式来解决单点问题。前面介绍了这些设计上的一些原则,但是你有没有发现,我一直说的是“尽量”而不是“绝对”?我想你肯定会问是不是请求最少就一定最好,我的答案是“不一定”。我们曾经把有些 CSS 内联进页面里,这样做可以减少依赖一个 CSS 的请求从而加快首页的渲染,但是同样也增大了页面的大小,又不符合“数据要尽量少”的原则,这种情况下我们为了提升首屏的渲染速度,只把首屏的 HTML 依赖的 CSS 内联进来,其他 CSS 仍然放到文件中作为依赖加载,尽量实现首屏的打开速度与整个页面加载性能的平衡。所以说,架构是一种平衡的艺术,而最好的架构一旦脱离了它所适应的场景,一切都将是空谈。我希望你记住的是,这里所说的几点都只是一个个方向,你应该尽量往这些方向上去努力,但也要考虑平衡其他因素。
不同场景下的不同架构案例
前面我说了一些架构上的原则,那么针对“秒杀”这个场景,怎样才是一个好的架构呢?下面我以淘宝早期秒杀系统架构的演进为主线,来帮你梳理不同的请求体量下,我认为的最佳秒杀系统架构。

前面我说了一些架构上的原则,那么针对“秒杀”这个场景,怎样才是一个好的架构呢?下面我以淘宝早期秒杀系统架构的演进为主线,来帮你梳理不同的请求体量下,我认为的最佳秒杀系统架构。如果你想快速搭建一个简单的秒杀系统,只需要把你的商品购买页面增加一个“定时上架”功能,仅在秒杀开始时才让用户看到购买按钮,当商品的库存卖完了也就结束了。这就是当时第一个版本的秒杀系统实现方式。但随着请求量的加大(比如从 1w/s 到了 10w/s 的量级),这个简单的架构很快就遇到了瓶颈,因此需要做架构改造来提升系统性能。

  • 把秒杀系统独立出来单独打造一个系统,这样可以有针对性地做优化,例如这个独立出来的系统就减少了店铺装修的功能,减少了页面的复杂度;
  • 在系统部署上也独立做一个机器集群,这样秒杀的大流量就不会影响到正常的商品购买集群的机器负载;
  • 将热点数据(如库存数据)单独放到一个缓存系统中,以提高“读性能”;
  • 增加秒杀答题,防止有秒杀器抢单。

此时的系统架构变成了下图这个样子。最重要的就是,秒杀详情成为了一个独立的新系统,另外核心的一些数据放到了缓存(Cache)中,其他的关联系统也都以独立集群的方式进行部署。

然而这个架构仍然支持不了超过 100w/s 的请求量,所以为了进一步提升秒杀系统的性能,我们又对架构做进一步升级,

  • 对页面进行彻底的动静分离,使得用户秒杀时不需要刷新整个页面,而只需要点击抢宝按钮,借此把页面刷新的数据降到最少;
  • 在服务端对秒杀商品进行本地缓存,不需要再调用依赖系统的后台服务获取数据,甚至不需要去公共的缓存集群中查询数据,这样不仅可以减少系统调用,而且能够避免压垮公共缓存集群。
  • 增加系统限流保护,防止最坏情况发生。

经过这些优化,系统架构变成了下图中的样子。在这里,我们对页面进行了进一步的静态化,秒杀过程中不需要刷新整个页面,而只需要向服务端请求很少的动态数据。而且,最关键的详情和交易系统都增加了本地缓存,来提前缓存秒杀商品的信息,热点数据库也做了独立部署,等等。

从前面的几次升级来看,其实越到后面需要定制的地方越多,也就是越“不通用”。例如,把秒杀商品缓存在每台机器的内存中,这种方式显然不适合太多的商品同时进行秒杀的情况,因为单机的内存始终有限。所以要取得极致的性能,就要在其他地方(比如,通用性、易用性、成本等方面)有所牺牲。

02 | 如何才能做好动静分离?有哪些方案可选?

数据的动静分离。不知道你之前听过这个解决方案吗?不管你有没有听过,我都建议你先停下来思考动静分离的价值。如果你的系统还没有开始应用动静分离的方案,那你也可以想想为什么没有,是之前没有想到,还是说业务体量根本用不着?不过我可以确信地说,如果你在一个业务飞速发展的公司里,并且你在深度参与公司内类秒杀类系统的架构或者开发工作,那么你迟早会想到动静分离的方案。为什么?很简单,秒杀的场景中,对于系统的要求其实就三个字:快、准、稳。

那怎么才能“快”起来呢?我觉得抽象起来讲,就只有两点,一点是提高单次请求的效率,一点是减少没必要的请求。今天我们聊到的“动静分离”其实就是瞄着这个大方向去的。
不知道你是否还记得,最早的秒杀系统其实是要刷新整体页面的,但后来秒杀的时候,你只要点击“刷新抢宝”按钮就够了,这种变化的本质就是动静分离,分离之后,客户端大幅度减少了请求的数据量。这不自然就“快”了吗?

那到底什么才是动静分离呢?所谓“动静分离”,其实就是把用户请求的数据(如 HTML 页面)划分为“动态数据”和“静态数据”。

简单来说,“动态数据”和“静态数据”的主要区别就是看页面中输出的数据是否和 URL、浏览者、时间、地域相关,以及是否含有 Cookie 等私密数据。比如说:

很多媒体类的网站,某一篇文章的内容不管是你访问还是我访问,它都是一样的。所以它就是一个典型的静态数据,但是它是个动态页面。

我们如果现在访问淘宝的首页,每个人看到的页面可能都是不一样的,淘宝首页中包含了很多根据访问者特征推荐的信息,而这些个性化的数据就可以理解为动态数据了。

这里再强调一下,我们所说的静态数据,不能仅仅理解为传统意义上完全存在磁盘上的 HTML 页面,它也可能是经过 Java 系统产生的页面,但是它输出的页面本身不包含上面所说的那些因素。也就是所谓“动态”还是“静态”,并不是说数据本身是否动静,而是数据中是否含有和访问者相关的个性化数据。

还有一点要注意,就是页面中“不包含”,指的是“页面的 HTML 源码中不含有”,这一点务必要清楚。理解了静态数据和动态数据,我估计你很容易就能想明白“动静分离”这个方案的来龙去脉了。分离了动静数据,我们就可以对分离出来的静态数据做缓存,有了缓存之后,静态数据的“访问效率”自然就提高了。
那么,怎样对静态数据做缓存呢?我在这里总结了几个重点。

第一,你应该把静态数据缓存到离用户最近的地方。静态数据就是那些相对不会变化的数据,因此我们可以把它们缓存起来。缓存到哪里呢?常见的就三种,用户浏览器里、CDN 上或者在服务端的 Cache 中。你应该根据情况,把它们尽量缓存到离用户最近的地方。

第二,静态化改造就是要直接缓存 HTTP 连接。相较于普通的数据缓存而言,你肯定还听过系统的静态化改造。静态化改造是直接缓存 HTTP 连接而不是仅仅缓存数据,如下图所示,Web 代理服务器根据请求 URL,直接取出对应的 HTTP 响应头和响应体然后直接返回,这个响应过程简单得连 HTTP 协议都不用重新组装,甚至连 HTTP 请求头也不需要解析。

第三,让谁来缓存静态数据也很重要。不同语言写的 Cache 软件处理缓存数据的效率也各不相同。以 Java 为例,因为 Java 系统本身也有其弱点(比如不擅长处理大量连接请求,每个连接消耗的内存较多,Servlet 容器解析 HTTP 协议较慢),所以你可以不在 Java 层做缓存,而是直接在 Web 服务器层上做,这样你就可以屏蔽 Java 语言层面的一些弱点;而相比起来,Web 服务器(如 Nginx、Apache、Varnish)也更擅长处理大并发的静态文件请求。

理解了动静态数据的“why”和“what”,接下来我们就要看“how”了。我们如何把动态页面改造成适合缓存的静态页面呢?其实也很简单,就是去除前面所说的那几个影响因素,把它们单独分离出来,做动静分离。

下面,我以典型的商品详情系统为例来详细介绍。这里,你可以先打开京东或者淘宝的商品详情页,看看这个页面里都有哪些动静数据。我们从以下 5 个方面来分离出动态内容。

- URL 唯一化。商品详情系统天然地就可以做到 URL 唯一化,比如每个商品都由 ID 来标识,那么 就可以作为唯一的 URL 标识。为啥要 URL 唯一呢?前面说了我们是要缓存整个 HTTP 连接,那么以什么作为 Key 呢?就以 URL 作为缓存的 Key,例如以 id=xxx 这个格式进行区分。
- 分离浏览者相关的因素。浏览者相关的因素包括是否已登录,以及登录身份等,这些相关因素我们可以单独拆分出来,通过动态请求来获取。
- 分离时间因素。服务端输出的时间也通过动态请求获取。
- 异步化地域因素。详情页面上与地域相关的因素做成异步方式获取,当然你也可以通过动态请求方式获取,只是这里通过异步获取更合适。

分离出动态内容之后,如何组织这些内容页就变得非常关键了。这里我要提醒你一点,因为这其中很多动态内容都会被页面中的其他模块用到,如判断该用户是否已登录、用户 ID 是否匹配等,所以这个时候我们应该将这些信息 JSON 化(用 JSON 格式组织这些数据),以方便前端获取。

前面我们介绍里用缓存的方式来处理静态数据。而动态内容的处理通常有两种方案:ESI(Edge Side Includes)方案和 CSI(Client Side Include)方案。

ESI 方案(或者 SSI):即在 Web 代理服务器上做动态内容请求,并将请求插入到静态页面中,当用户拿到页面时已经是一个完整的页面了。这种方式对服务端性能有些影响,但是用户体验较好。

CSI 方案。即单独发起一个异步 JavaScript 请求,以向服务端获取动态内容。这种方式服务端性能更佳,但是用户端页面可能会延时,体验稍差。

动静分离的几种架构方案

前面我们通过改造把静态数据和动态数据做了分离,那么如何在系统架构上进一步对这些动态和静态数据重新组合,再完整地输出给用户呢?这就涉及对用户请求路径进行合理的架构了。根据架构上的复杂度,有 3 种方案可选:实体机单机部署;统一 Cache 层;上 CDN。

方案 1:实体机单机部署

这种方案是将虚拟机改为实体机,以增大 Cache 的容量,并且采用了一致性 Hash 分组的方式来提升命中率。这里将 Cache 分成若干组,是希望能达到命中率和访问热点的平衡。Hash 分组越少,缓存的命中率肯定就会越高,但短板是也会使单个商品集中在一个分组中,容易导致 Cache 被击穿,所以我们应该适当增加多个相同的分组,来平衡访问热点和命中率的问题。这里我给出了实体机单机部署方案的结构图,如下:

实体机单机部署有以下几个优点:
没有网络瓶颈,而且能使用大内存;
既能提升命中率,又能减少 Gzip 压缩;
减少 Cache 失效压力,因为采用定时失效方式,例如只缓存 3 秒钟,过期即自动失效。
这个方案中,虽然把通常只需要虚拟机或者容器运行的 Java 应用换成实体机,优势很明显,它会增加单机的内存容量,但是一定程度上也造成了 CPU 的浪费,因为单个的 Java 进程很难用完整个实体机的 CPU。

另外就是,一个实体机上部署了 Java 应用又作为 Cache 来使用,这造成了运维上的高复杂度,所以这是一个折中的方案。如果你的公司里,没有更多的系统有类似需求,那么这样做也比较合适,如果你们有多个业务系统都有静态化改造的需求,那还是建议把 Cache 层单独抽出来公用比较合理,如下面的方案 2 所示。

所谓统一 Cache 层,就是将单机的 Cache 统一分离出来,形成一个单独的 Cache 集群。统一 Cache 层是个更理想的可推广方案,该方案的结构图如下:

*将 Cache 层单独拿出来统一管理可以减少运维成本,同时也方便接入其他静态化系统。此外,它还有一些优点。 *

单独一个 Cache 层,可以减少多个应用接入时使用 Cache 的成本。这样接入的应用只要维护自己的 Java 系统就好,不需要单独维护 Cache,而只关心如何使用即可
统一 Cache 的方案更易于维护,如后面加强监控、配置的自动化,只需要一套解决方案就行,统一起来维护升级也比较方便。
可以共享内存,最大化利用内存,不同系统之间的内存可以动态切换,从而能够有效应对各种攻击。

这种方案虽然维护上更方便了,但是也带来了其他一些问题,比如缓存更加集中,导致:

Cache 层内部交换网络成为瓶颈;
缓存服务器的网卡也会是瓶颈;
机器少风险较大,挂掉一台就会影响很大一部分缓存数据。

要解决上面这些问题,可以再对 Cache 做 Hash 分组,即一组 Cache 缓存的内容相同,这样能够避免热点数据过度集中导致新的瓶颈产生。

在将整个系统做动静分离后,我们自然会想到更进一步的方案,就是将 Cache 进一步前移到 CDN 上,因为 CDN 离用户最近,效果会更好。
但是要想这么做,有以下几个问题需要解决。

失效问题。前面我们也有提到过缓存时效的问题,不知道你有没有理解,我再来解释一下。谈到静态数据时,我说过一个关键词叫“相对不变”,它的言外之意是“可能会变化”。比如一篇文章,现在不变,但如果你发现个错别字,是不是就会变化了?如果你的缓存时效很长,那用户端在很长一段时间内看到的都是错的。所以,这个方案中也是,我们需要保证 CDN 可以在秒级时间内,让分布在全国各地的 Cache 同时失效,这对 CDN 的失效系统要求很高。

命中率问题。Cache 最重要的一个衡量指标就是“高命中率”,不然 Cache 的存在就失去了意义。同样,如果将数据全部放到全国的 CDN 上,必然导致 Cache 分散,而 Cache 分散又会导致访问请求命中同一个 Cache 的可能性降低,那么命中率就成为一个问题。

发布更新问题。如果一个业务系统每周都有日常业务需要发布,那么发布系统必须足够简洁高效,而且你还要考虑有问题时快速回滚和排查问题的简便性。

从前面的分析来看,将商品详情系统放到全国的所有 CDN 节点上是不太现实的,因为存在失效问题、命中率问题以及系统的发布更新问题。那么是否可以选择若干个节点来尝试实施呢?答案是“可以”,但是这样的节点*需要满足几个条件: *
靠近访问量比较集中的地区;
节点到主站间的网络比较好,而且稳定;
节点容量比较大,不会占用其他 CDN 太多的资源。

最后,还有一点也很重要,那就是:节点不要太多。基于上面几个因素,选择 CDN 的二级 Cache 比较合适,因为二级 Cache 数量偏少,容量也更大,让用户的请求先回源的 CDN 的二级 Cache 中,如果没命中再回源站获取数据

使用 CDN 的二级 Cache 作为缓存,可以达到和当前服务端静态化 Cache 类似的命中率,因为节点数不多,Cache 不是很分散,访问量也比较集中,这样也就解决了命中率问题,同时能够给用户最好的访问体验,是当前比较理想的一种 CDN 化方案。
除此之外,CDN 化部署方案还有以下几个特点:

  • 把整个页面缓存在用户浏览器中;
  • 如果强制刷新整个页面,也会请求 CDN;
  • 实际有效请求,只是用户对“刷新抢宝”按钮的点击。

这样就把 90% 的静态数据缓存在了用户端或者 CDN 上,当真正秒杀时,用户只需要点击特殊的“刷新抢宝”按钮,而不需要刷新整个页面。这样一来,系统只是向服务端请求很少的有效数据,而不需要重复请求大量的静态数据。秒杀的动态数据和普通详情页面的动态数据相比更少,性能也提升了 3 倍以上。

所以“抢宝”这种设计思路,让我们不用刷新页面就能够很好地请求到服务端最新的动态数据。

03 | 二八原则:有针对性地处理好系统的“热点数据”

而“热点数据”比较好理解,那就是用户的热点请求对应的数据。而热点数据又分为“静态热点数据”和“动态热点数据”

所谓“静态热点数据”,就是能够提前预测的热点数据。例如,我们可以通过卖家报名的方式提前筛选出来,通过报名系统对这些热点商品进行打标。另外,我们还可以通过大数据分析来提前发现热点商品,比如我们分析历史成交记录、用户的购物车记录,来发现哪些商品可能更热门、更好卖,这些都是可以提前分析出来的热点。

所谓“动态热点数据”,就是不能被提前预测到的,系统在运行过程中临时产生的热点。例如,卖家在抖音上做了广告,然后商品一下就火了,导致它在短时间内被大量购买。
由于热点操作是用户的行为,我们不好改变,但能做一些限制和保护,所以本文我主要针对热点数据来介绍如何进行优化。

前面,我介绍了如何对单个秒杀商品的页面数据进行动静分离,以便针对性地对静态数据做优化处理,那么另外一个关键的问题来了:如何发现这些秒杀商品,或者更准确地说,如何发现热点商品呢?

你可能会说“参加秒杀的商品就是秒杀商品啊”,没错,关键是系统怎么知道哪些商品参加了秒杀活动呢?所以,你要有一个机制提前来区分普通商品和秒杀商品。

我们从发现静态热点和发现动态热点两个方面来看一下。

如前面讲的,静态热点数据可以通过商业手段,例如强制让卖家通过报名参加的方式提前把热点商品筛选出来,实现方式是通过一个运营系统,把参加活动的商品数据进行打标,然后通过一个后台系统对这些热点商品进行预处理,如提前进行缓存。但是这种通过报名提前筛选的方式也会带来新的问题,即增加卖家的使用成本,而且实时性较差,也不太灵活。不过,除了提前报名筛选这种方式,你还可以通过技术手段提前预测,例如对买家每天访问的商品进行大数据计算,然后统计出 TOP N 的商品,我们可以认为这些 TOP N 的商品就是热点商品。

我们可以通过卖家报名或者大数据预测这些手段来提前预测静态热点数据,但这其中有一个痛点,就是实时性较差,如果我们的系统能在秒级内自动发现热点商品那就完美了。能够动态地实时发现热点不仅对秒杀商品,对其他热卖商品也同样有价值,所以我们需要想办法实现热点的动态发现功能。

这里我给出一个动态热点发现系统的具体实现。

1、构建一个异步的系统,它可以收集交易链路上各个环节中的中间件产品的热点 Key,如 Nginx、缓存、RPC 服务框架等这些中间件(一些中间件产品本身已经有热点统计模块)。
2、建立一个热点上报和可以按照需求订阅的热点服务的下发规范,主要目的是通过交易链路上各个系统(包括详情、购物车、交易、优惠、库存、物流等)访问的时间差,把上游已经发现的热点透传给下游系统,提前做好保护。比如,对于大促高峰期,详情系统是最早知道的,在统一接入层上 Nginx 模块统计的热点 URL。
3、将上游系统收集的热点数据发送到热点服务台,然后下游系统(如交易系统)就会知道哪些商品会被频繁调用,然后做热点保护。

这里我给出了一个图,其中用户访问商品时经过的路径有很多,我们主要是依赖前面的导购页面(包括首页、搜索页面、商品详情、购物车等)提前识别哪些商品的访问量高,通过这些系统中的中间件来收集热点数据,并记录到日志中。

我们通过部署在每台机器上的 Agent 把日志汇总到聚合和分析集群中,然后把符合一定规则的热点数据,通过订阅分发系统再推送到相应的系统中。你可以是把热点数据填充到 Cache 中,或者直接推送到应用服务器的内存中,还可以对这些数据进行拦截,总之下游系统可以订阅这些数据,然后根据自己的需求决定如何处理这些数据。

打造热点发现系统时,我根据以往经验总结了几点注意事项

1、这个热点服务后台抓取热点数据日志最好采用异步方式,因为“异步”一方面便于保证通用性,另一方面又不影响业务系统和中间件产品的主流程。
2、热点服务发现和中间件自身的热点保护模块并存,每个中间件和应用还需要保护自己。热点服务台提供热点数据的收集和订阅服务,便于把各个系统的热点数据透明出来。
3、热点发现要做到接近实时(3s 内完成热点数据的发现),因为只有做到接近实时,动态发现才有意义,才能实时地对下游系统提供保护。

处理热点数据通常有几种思路:一是优化,二是限制,三是隔离。
先来说说优化。优化热点数据最有效的办法就是缓存热点数据,如果热点数据做了动静分离,那么可以长期缓存静态数据。但是,缓存热点数据更多的是“临时”缓存,即不管是静态数据还是动态数据,都用一个队列短暂地缓存数秒钟,由于队列长度有限,可以采用 LRU 淘汰算法替换。

再来说说限制。限制更多的是一种保护机制,限制的办法也有很多,例如对被访问商品的 ID 做一致性 Hash,然后根据 Hash 做分桶,每个分桶设置一个处理队列,这样可以把热点商品限制在一个请求队列里,防止因某些热点商品占用太多的服务器资源,而使其他请求始终得不到服务器的处理资源。

最后介绍一下隔离。秒杀系统设计的第一个原则就是将这种热点数据隔离出来,不要让 1% 的请求影响到另外的 99%,隔离出来后也更方便对这 1% 的请求做针对性的优化。

具体到“秒杀”业务,我们可以在以下几个层次实现隔离。

业务隔离。把秒杀做成一种营销活动,卖家要参加秒杀这种营销活动需要单独报名,从技术上来说,卖家报名后对我们来说就有了已知热点,因此可以提前做好预热。
系统隔离。系统隔离更多的是运行时的隔离,可以通过分组部署的方式和另外 99% 分开。秒杀可以申请单独的域名,目的也是让请求落到不同的集群中。
数据隔离。秒杀所调用的数据大部分都是热点数据,比如会启用单独的 Cache 集群或者 MySQL 数据库来放热点数据,目的也是不想 0.01% 的数据有机会影响 99.99% 数据。

04 | 流量削峰这事应该怎么做?

我们知道服务器的处理资源是恒定的,你用或者不用它的处理能力都是一样的,所以出现峰值的话,很容易导致忙到处理不过来,闲的时候却又没有什么要处理。但是由于要保证服务质量,我们的很多处理资源只能按照忙的时候来预估,而这会导致资源的一个浪费。

这就好比因为存在早高峰和晚高峰的问题,所以有了错峰限行的解决方案。削峰的存在,一是可以让服务端处理变得更加平稳,二是可以节省服务器的资源成本。针对秒杀这一场景,削峰从本质上来说就是更多地延缓用户请求的发出,以便减少和过滤掉一些无效请求,它遵从“请求数要尽量少”的原则。

今天,我就来介绍一下流量削峰的一些操作思路:排队、答题、分层过滤。这几种方式都是无损(即不会损失用户的发出请求)的实现方案,当然还有些有损的实现方案,包括我们后面要介绍的关于稳定性的一些办法,比如限流和机器负载保护等一些强制措施也能达到削峰保护的目的,当然这都是不得已的一些措施,因此就不归类到这里了

要对流量进行削峰,最容易想到的解决方案就是用消息队列来缓冲瞬时流量,把同步的直接调用转换成异步的间接推送,中间通过一个队列在一端承接瞬时的流量洪峰,在另一端平滑地将消息推送出去。在这里,消息队列就像“水库”一样, 拦蓄上游的洪水,削减进入下游河道的洪峰流量,从而达到减免洪水灾害的目的。用消息队列来缓冲瞬时流量的方案,如下图所示:

但是,如果流量峰值持续一段时间达到了消息队列的处理上限,例如本机的消息积压达到了存储空间的上限,消息队列同样也会被压垮,这样虽然保护了下游的系统,但是和直接把请求丢弃也没多大的区别。就像遇到洪水爆发时,即使是有水库恐怕也无济于事

除了消息队列,类似的排队方式还有很多,例如:
1、利用线程池加锁等待也是一种常用的排队方式;
2、先进先出、先进后出等常用的内存排队算法的实现方式;
3、把请求序列化到文件中,然后再顺序地读文件(例如基于 MySQL binlog 的同步机制)来恢复请求等方式。

你是否还记得,最早期的秒杀只是纯粹地刷新页面和点击购买按钮,它是后来才增加了答题功能的。那么,为什么要增加答题功能呢?

这主要是为了增加购买的复杂度,从而达到两个目的。

第一个目的是防止部分买家使用秒杀器在参加秒杀时作弊。2011 年秒杀非常火的时候,秒杀器也比较猖獗,因而没有达到全民参与和营销的目的,所以系统增加了答题来限制秒杀器。增加答题后,下单的时间基本控制在 2s 后,秒杀器的下单比例也大大下降。答题页面如下图所示。

第二个目的其实就是延缓请求,起到对请求流量进行削峰的作用,从而让系统能够更好地支持瞬时的流量高峰。这个重要的功能就是把峰值的下单请求拉长,从以前的 1s 之内延长到 2s~10s。这样一来,请求峰值基于时间分片了。这个时间的分片对服务端处理并发非常重要,会大大减轻压力。而且,由于请求具有先后顺序,靠后的请求到来时自然也就没有库存了,因此根本到不了最后的下单步骤,所以真正的并发写就非常有限了。这种设计思路目前用得非常普遍,如当年支付宝的“咻一咻”、微信的“摇一摇”都是类似的方式。

这里,我重点说一下秒杀答题的设计思路。

如上图所示,整个秒杀答题的逻辑主要分为 3 部分。

题库生成模块,这个部分主要就是生成一个个问题和答案,其实题目和答案本身并不需要很复杂,重要的是能够防止由机器来算出结果,即防止秒杀器来答题。
题库的推送模块,用于在秒杀答题前,把题目提前推送给详情系统和交易系统。题库的推送主要是为了保证每次用户请求的题目是唯一的,目的也是防止答题作弊。
题目的图片生成模块,用于把题目生成为图片格式,并且在图片里增加一些干扰因素。这也同样是为防止机器直接来答题,它要求只有人才能理解题目本身的含义。这里还要注意一点,由于答题时网络比较拥挤,我们应该把题目的图片提前推送到 CDN 上并且要进行预热,不然的话当用户真正请求题目时,图片可能加载比较慢,从而影响答题的体验。

其实真正答题的逻辑比较简单,很好理解:当用户提交的答案和题目对应的答案做比较,如果通过了就继续进行下一步的下单逻辑,否则就失败。我们可以把问题和答案用下面这样的 key 来进行 MD5 加密:

验证的逻辑如下图所示:

注意,这里面的验证逻辑,除了验证问题的答案以外,还包括用户本身身份的验证,例如是否已经登录、用户的 Cookie 是否完整、用户是否重复频繁提交等。
除了做正确性验证,我们还可以对提交答案的时间做些限制,例如从开始答题到接受答案要超过 1s,因为小于 1s 是人为操作的可能性很小,这样也能防止机器答题的情况。

前面介绍的排队和答题要么是少发请求,要么对发出来的请求进行缓冲,而针对秒杀场景还有一种方法,就是对请求进行分层过滤,从而过滤掉一些无效的请求。分层过滤其实就是采用“漏斗”式设计来处理请求的,如下图所示。

假如请求分别经过 CDN、前台读系统(如商品详情系统)、后台系统(如交易系统)和数据库这几层,那么:

大部分数据和流量在用户浏览器或者 CDN 上获取,这一层可以拦截大部分数据的读取;
经过第二层(即前台系统)时数据(包括强一致性的数据)尽量得走 Cache,过滤一些无效的请求;
再到第三层后台系统,主要做数据的二次检验,对系统做好保护和限流,这样数据量和请求就进一步减少;
最后在数据层完成数据的强一致性校验。

这样就像漏斗一样,尽量把数据量和请求量一层一层地过滤和减少了。

分层过滤的核心思想是:在不同的层次尽可能地过滤掉无效请求,让“漏斗”最末端的才是有效请求。而要达到这种效果,我们就必须对数据做分层的校验。

*分层校验的基本原则是: *

将动态请求的读数据缓存(Cache)在 Web 端,过滤掉无效的数据读;
对读数据不做强一致性校验,减少因为一致性校验产生瓶颈的问题;
对写数据进行基于时间的合理分片,过滤掉过期的失效请求;
对写请求做限流保护,将超出系统承载能力的请求过滤掉;
对写数据进行强一致性校验,只保留最后有效的数据。

05 | 影响性能的因素有哪些?又该如何提高系统的性能?

那么,哪些因素对性能有影响呢?在回答这个问题之前,我们先定义一下“性能”,服务设备不同对性能的定义也是不一样的,例如 CPU 主要看主频、磁盘主要看 IOPS(Input/Output Operations Per Second,即每秒进行读写操作的次数)。

而今天我们讨论的主要是系统服务端性能,一般用 QPS(Query Per Second,每秒请求数)来衡量,还有一个影响和 QPS 也息息相关,那就是响应时间(Response Time,RT),它可以理解为服务器处理响应的耗时。

正常情况下响应时间(RT)越短,一秒钟处理的请求数(QPS)自然也就会越多,这在单线程处理的情况下看起来是线性的关系,即我们只要把每个请求的响应时间降到最低,那么性能就会最高。

但是你可能想到响应时间总有一个极限,不可能无限下降,所以又出现了另外一个维度,即通过多线程,来处理请求。这样理论上就变成了“总 QPS =(1000ms / 响应时间)× 线程数量”,这样性能就和两个因素相关了,一个是一次响应的服务端耗时,一个是处理请求的线程数。

首先,我们先来看看响应时间和 QPS 有啥关系。

对于大部分的 Web 系统而言,响应时间一般都是由 CPU 执行时间和线程等待时间(比如 RPC、IO 等待、Sleep、Wait 等)组成,即服务器在处理一个请求时,一部分是 CPU 本身在做运算,还有一部分是在各种等待。

如果代理服务器本身没有 CPU 消耗,我们在每次给代理服务器代理的请求加个延时,即增加响应时间,但是这对代理服务器本身的吞吐量并没有多大的影响,因为代理服务器本身的资源并没有被消耗,可以通过增加代理服务器的处理线程数,来弥补响应时间对代理服务器的 QPS 的影响。

其实,真正对性能有影响的是 CPU 的执行时间。这也很好理解,因为 CPU 的执行真正消耗了服务器的资源。经过实际的测试,如果减少 CPU 一半的执行时间,就可以增加一倍的 QPS。

也就是说,我们应该致力于减少 CPU 的执行时间。

其次,我们再来看看线程数对 QPS 的影响。

单看“总 QPS”的计算公式,你会觉得线程数越多 QPS 也就会越高,但这会一直正确吗?显然不是,线程数不是越多越好,因为线程本身也消耗资源,也受到其他因素的制约。例如,线程越多系统的线程切换成本就会越高,而且每个线程也都会耗费一定内存。

那么,设置什么样的线程数最合理呢?其实很多多线程的场景都有一个默认配置,即“线程数 = 2 * CPU 核数 + 1”。除去这个配置,还有一个根据最佳实践得出来的公式:

当然,最好的办法是通过性能测试来发现最佳的线程数。
换句话说,要提升性能我们就要减少 CPU 的执行时间,另外就是要设置一个合理的并发线程数,通过这两方面来显著提升服务器的性能。
现在,你知道了如何来快速提升性能,那接下来你估计会问,我应该怎么发现系统哪里最消耗 CPU 资源呢?

就服务器而言,会出现瓶颈的地方有很多,例如 CPU、内存、磁盘以及网络等都可能会导致瓶颈。此外,不同的系统对瓶颈的关注度也不一样,例如对缓存系统而言,制约它的是内存,而对存储型系统来说 I/O 更容易是瓶颈。

我们定位的场景是秒杀,它的瓶颈更多地发生在 CPU 上。

怎样简单地判断 CPU 是不是瓶颈呢?一个办法就是看当 QPS 达到极限时,你的服务器的 CPU 使用率是不是超过了 95%,如果没有超过,那么表示 CPU 还有提升的空间,要么是有锁限制,要么是有过多的本地 I/O 等待发生。

对 Java 系统来说,可以优化的地方很多,这里我重点说一下比较有效的几种手段,供你参考,它们是:减少编码、减少序列化、Java 极致优化、并发读优化。接下来,我们分别来看一下。

Java 的编码运行比较慢,这是 Java 的一大硬伤。在很多场景下,只要涉及字符串的操作(如输入输出操作、I/O 操作)都比较耗 CPU 资源,不管它是磁盘 I/O 还是网络 I/O,因为都需要将字符转换成字节,而这个转换必须编码。每个字符的编码都需要查表,而这种查表的操作非常耗资源,所以减少字符到字节或者相反的转换、减少字符编码会非常有成效。减少编码就可以大大提升性能

序列化也是 Java 性能的一大天敌,减少 Java 中的序列化操作也能大大提升性能。又因为序列化往往是和编码同时发生的,所以减少序列化也就减少了编码。

序列化大部分是在 RPC 中发生的,因此避免或者减少 RPC 就可以减少序列化,当然当前的序列化协议也已经做了很多优化来提升性能。有一种新的方案,就是可以将多个关联性比较强的应用进行“合并部署”,而减少不同应用之间的 RPC 也可以减少序列化的消耗。

所谓“合并部署”,就是把两个原本在不同机器上的不同应用合并部署到一台机器上,当然不仅仅是部署在一台机器上,还要在同一个 Tomcat 容器中,且不能走本机的 Socket,这样才能避免序列化的产生。

另外针对秒杀场景,我们还可以做得更极致一些,接下来我们来看第 3 点:Java 极致优化。

Java 和通用的 Web 服务器(如 Nginx 或 Apache 服务器)相比,在处理大并发的 HTTP 请求时要弱一点,所以一般我们都会对大流量的 Web 系统做静态化改造,让大部分请求和数据直接在 Nginx 服务器或者 Web 代理服务器(如 Varnish、Squid 等)上直接返回(这样可以减少数据的序列化与反序列化),而 Java 层只需处理少量数据的动态请求。针对这些请求,我们可以使用以下手段进行优化:

1、直接使用 Servlet 处理请求。避免使用传统的 MVC 框架,这样可以绕过一大堆复杂且用处不大的处理逻辑,节省 1ms 时间(具体取决于你对 MVC 框架的依赖程度)
2、直接输出流数据。使用 resp.getOutputStream() 而不是 resp.getWriter() 函数,可以省掉一些不变字符数据的编码,从而提升性能;数据输出时推荐使用 JSON 而不是模板引擎(一般都是解释执行)来输出页面。

也许有读者会觉得这个问题很容易解决,无非就是放到 Tair 缓存里面。集中式缓存为了保证命中率一般都会采用一致性 Hash,所以同一个 key 会落到同一台机器上。虽然单台缓存机器也能支撑 30w/s 的请求,但还是远不足以应对像“大秒”这种级别的热点商品。那么,该如何彻底解决单点的瓶颈呢?

答案是采用应用层的 LocalCache,即在秒杀系统的单机上缓存商品相关的数据。

那么,又如何缓存(Cache)数据呢?你需要划分成动态数据和静态数据分别进行处理:

1、像商品中的“标题”和“描述”这些本身不变的数据,会在秒杀开始之前全量推送到秒杀机器上,并一直缓存到秒杀结束;

2、像库存这类动态数据,会采用“被动失效”的方式缓存一定时间(一般是数秒),失效后再去缓存拉取最新的数据。

06 | 秒杀系统“减库存”设计的核心逻辑

千万不要超卖,这是大前提。

在正常的电商平台购物场景中,用户的实际购买过程一般分为两步:下单和付款。你想买一台 iPhone 手机,在商品页面点了“立即购买”按钮,核对信息之后点击“提交订单”,这一步称为下单操作。下单之后,你只有真正完成付款操作才能算真正购买,也就是俗话说的“落袋为安”。

那如果你是架构师,你会在哪个环节完成减库存的操作呢?总结来说,减库存操作一般有如下几个方式:

下单减库存,即当买家下单后,在商品的总库存中减去买家购买数量。下单减库存是最简单的减库存方式,也是控制最精确的一种,下单时直接通过数据库的事务机制控制商品库存,这样一定不会出现超卖的情况。但是你要知道,有些人下完单可能并不会付款。
付款减库存,即买家下单后,并不立即减库存,而是等到有用户付款后才真正减库存,否则库存一直保留给其他买家。但因为付款时才减库存,如果并发比较高,有可能出现买家下单后付不了款的情况,因为可能商品已经被其他人买走了
预扣库存,这种方式相对复杂一些,买家下单后,库存为其保留一定的时间(如 10 分钟),超过这个时间,库存将会自动释放,释放后其他买家就可以继续购买。在买家付款前,系统会校验该订单的库存是否还有保留:如果没有保留,则再次尝试预扣;如果库存不足(也就是预扣失败)则不允许继续付款;如果预扣成功,则完成付款并实际地减去库存。

以上这几种减库存的方式都会存在一些问题,下面我们一起来看下。

由于购物过程中存在两步或者多步的操作,因此在不同的操作步骤中减库存,就会存在一些可能被恶意买家利用的漏洞,例如发生恶意下单的情况。

假如我们采用“下单减库存”的方式,即用户下单后就减去库存,正常情况下,买家下单后付款的概率会很高,所以不会有太大问题。但是有一种场景例外,就是当卖家参加某个活动时,此时活动的有效时间是商品的黄金售卖时间,如果有竞争对手通过恶意下单的方式将该卖家的商品全部下单,让这款商品的库存减为零,那么这款商品就不能正常售卖了。要知道,这些恶意下单的人是不会真正付款的,这正是“下单减库存”方式的不足之处。

既然“下单减库存”可能导致恶意下单,从而影响卖家的商品销售,那么有没有办法解决呢?你可能会想,采用“付款减库存”的方式是不是就可以了?的确可以。但是,“付款减库存”又会导致另外一个问题:库存超卖。

假如有 100 件商品,就可能出现 300 人下单成功的情况,因为下单时不会减库存,所以也就可能出现下单成功数远远超过真正库存数的情况,这尤其会发生在做活动的热门商品上。这样一来,就会导致很多买家下单成功但是付不了款,买家的购物体验自然比较差。

可以看到,不管是“下单减库存”还是“付款减库存”,都会导致商品库存不能完全和实际售卖情况对应起来的情况,看来要把商品准确地卖出去还真是不容易啊!

那么,既然“下单减库存”和“付款减库存”都有缺点,我们能否把两者相结合,将两次操作进行前后关联起来,下单时先预扣,在规定时间内不付款再释放库存,即采用“预扣库存”这种方式呢?

这种方案确实可以在一定程度上缓解上面的问题。但是否就彻底解决了呢?其实没有!针对恶意下单这种情况,虽然把有效的付款时间设置为 10 分钟,但是恶意买家完全可以在 10 分钟后再次下单,或者采用一次下单很多件的方式把库存减完。针对这种情况,解决办法还是要结合安全和反作弊的措施来制止。

例如,给经常下单不付款的买家进行识别打标(可以在被打标的买家下单时不减库存)、给某些类目设置最大购买件数(例如,参加活动的商品一人最多只能买 3 件),以及对重复下单不付款的操作进行次数限制等。

针对“库存超卖”这种情况,在 10 分钟时间内下单的数量仍然有可能超过库存数量,遇到这种情况我们只能区别对待:对普通的商品下单数量超过库存数量的情况,可以通过补货来解决;但是有些卖家完全不允许库存为负数的情况,那只能在买家付款时提示库存不足。

大型秒杀中如何减库存?

目前来看,业务系统中最常见的就是预扣库存方案,像你在买机票、买电影票时,下单后一般都有个“有效付款时间”,超过这个时间订单自动释放,这都是典型的预扣库存方案。而具体到秒杀这个场景,应该采用哪种方案比较好呢?

由于参加秒杀的商品,一般都是“抢到就是赚到”,所以成功下单后却不付款的情况比较少,再加上卖家对秒杀商品的库存有严格限制,所以秒杀商品采用“下单减库存”更加合理。另外,理论上由于“下单减库存”比“预扣库存”以及涉及第三方支付的“付款减库存”在逻辑上更为简单,所以性能上更占优势。

“下单减库存”在数据一致性上,主要就是保证大并发请求时库存数据不能为负数,也就是要保证数据库中的库存字段值不能为负数,一般我们有多种解决方案:一种是在应用程序中通过事务来判断,即保证减后库存不能为负数,否则就回滚;另一种办法是直接设置数据库的字段数据为无符号整数,这样减后库存字段值小于零时会直接执行 SQL 语句来报错;再有一种就是使用 CASE WHEN 判断语句,例如这样的 SQL

在交易环节中,“库存”是个关键数据,也是个热点数据,因为交易的各个环节中都可能涉及对库存的查询。但是,我在前面介绍分层过滤时提到过,秒杀中并不需要对库存有精确的一致性读,把库存数据放到缓存(Cache)中,可以大大提升读性能。解决大并发读问题,可以采用 LocalCache(即在秒杀系统的单机上缓存商品相关的数据)和对数据进行分层过滤的方式,但是像减库存这种大并发写无论如何还是避免不了,这也是秒杀场景下最为核心的一个技术难题。

因此,这里我想专门来说一下秒杀场景下减库存的极致优化思路,包括如何在缓存中减库存以及如何在数据库中减库存。

秒杀商品和普通商品的减库存还是有些差异的,例如商品数量比较少,交易时间段也比较短,因此这里有一个大胆的假设,即能否把秒杀商品减库存直接放到缓存系统中实现,也就是直接在缓存中减库存或者在一个带有持久化功能的缓存系统(如 Redis)中完成呢?

如果你的秒杀商品的减库存逻辑非常单一,比如没有复杂的 SKU 库存和总库存这种联动关系的话,我觉得完全可以。但是如果有比较复杂的减库存逻辑,或者需要使用事务,你还是必须在数据库中完成减库存。

由于 MySQL 存储数据的特点,同一数据在数据库里肯定是一行存储(MySQL),因此会有大量线程来竞争 InnoDB 行锁,而并发度越高时等待线程会越多,TPS(Transaction Per Second,即每秒处理的消息数)会下降,响应时间(RT)会上升,数据库的吞吐量就会严重受影响。

这就可能引发一个问题,就是单个热点商品会影响整个数据库的性能, 导致 0.01% 的商品影响 99.99% 的商品的售卖,这是我们不愿意看到的情况。一个解决思路是遵循前面介绍的原则进行隔离,把热点商品放到单独的热点库中。但是这无疑会带来维护上的麻烦,比如要做热点数据的动态迁移以及单独的数据库等。

而分离热点商品到单独的数据库还是没有解决并发锁的问题,我们应该怎么办呢?要解决并发锁的问题,有两种办法:

应用层做排队。按照商品维度设置队列顺序执行,这样能减少同一台机器对数据库同一行记录进行操作的并发度,同时也能控制单个商品占用数据库连接的数量,防止热点商品占用太多的数据库连接。
数据库层做排队。应用层只能做到单机的排队,但是应用机器数本身很多,这种排队方式控制并发的能力仍然有限,所以如果能在数据库层做全局排队是最理想的。阿里的数据库团队开发了针对这种 MySQL 的 InnoDB 层上的补丁程序(patch),可以在数据库层上对单行记录做到并发排队。

你可能有疑问了,排队和锁竞争不都是要等待吗,有啥区别?

里面加提示(hint),在事务里不需要等待应用层提交(COMMIT),而在数据执行完最后一条 SQL 后,直接根据 TARGET_AFFECT_ROW 的结果进行提交或回滚,可以减少网络等待时间(平均约 0.7ms)。据我所知,目前阿里 MySQL 团队已经将包含这些补丁程序的 MySQL 开源。

另外,数据更新问题除了前面介绍的热点隔离和排队处理之外,还有些场景(如对商品的 lastmodifytime 字段的)更新会非常频繁,在某些场景下这些多条 SQL 是可以合并的,一定时间内只要执行最后一条 SQL 就行了,以便减少对数据库的更新操作。

本文内容取自许令波老师《如何设计一个秒杀系统》这一课程的部分内容。

我要回帖

更多关于 真快乐一直显示没抢到 的文章

 

随机推荐