Reactor线程Reactor线程10-11 09:05

PHP秒杀系统 高并发高性能的极致挑战☆

---恢复内容开始---

秒杀系统特点
人多商品少
时间短流量高
外挂机器[黄牛和非黄牛]

技术分析
瞬间高并发的处理能力
多层次的分布式处理能力
人机交互与对抗[12306验证码图片]

技术选型分析
Linux+Nginx+PHP+Mysql+Redis
CDN,智能DNS,分布式缓存,全国多节点,多线路接入
LVS负载均衡

基本功能和流程
后台:活动管理/商品管理/订单管理/日志管理,数据列表和内容的编辑增删(逻辑删除)改查
前台:商品展示/抢购/我的订单/购物车/登录等功能
安全:验证码/回答/分析日志,防攻击、防作弊、防机器人

用户大概访问交互流程
用户进来的时候先看到秒杀商品的展示页面,然后从页面选择商品参与秒杀,参与秒杀时需要提交验证码,验证用户登录状态等之类的验证,把问答或者一些验证信息填完之后就可以提交订单完成秒杀功能,然后等待结果,有可能成功或者失败,提示一些信息,用户能够感知到的秒杀流程。

用户选择想要秒杀的商品,输入了要购买的商品数量,点击提交,这时候我们的秒杀程序就要开始响应了,于是秒杀开始。
先验证用户提交信息,比如还有用户登录状态,验证问答信息或者更多的信息。先验证信息是否对,如果有错误,那么提示错误信息,如果对,那么进入库存验证,如果库存不足,或者活动结束了,提示库存不足,那么秒杀结束。如果订单提交成功,那么生成订单,生成订单时会有订单相关的数据处理,比如库存的更新等。毕竟是并发提交,有可能生成订单也会出现问题,如果生成订单环节出现了问题,即使前面的环节通过,在此环节也会出现问题,比如订单生成的时候,前面的一个人先生成了订单,库存不足了,还会出错,所以在这个环节一定还会有其他的异常信息出现,那么还需要给用户提交错误信息,如果没出错,那么秒杀成功。

以上大概流程是不可或缺的,也是有点粗略。如果我们流程仅仅这几个点的话,那么我们的流程中其实还差的很多。我们在设计过程中,先列出来,还需要根据这几个流程进行补充。
程序运行起来会有几个输入的验证:如问答的验证,用户登录状态验证,用户是否进入黑名单,以及参数的验证,商品信息的参数,活动信息的参数,其他校验信息的参数验证等。
还有输出的验证:异常情况的输出和成功正常情况的输出。

其他情况:比如购买的库存是一种商品的话,在处理的时候要容易一点,会做一下比较。如果是多件商品的话,每个商品就需要每个进行验证。如果商品还有类型的区分,比如手机有好多种型号,那么还需要根据型号处理。有的时候还会涉及到优惠券等等。。

 

商品页面开发
静态化展示页面[效率要比动态PHP高很多,PHP程序需要解析等步骤,本身就需要很多流程,整个下来PHP的处理花的时间和资源要多]
商品状态的控制
开始前、进行中、库存不足、结束
数据逻辑处理
大致流程:验证用户是否登录、验证参数是否合法、验证活动信息状态、验证商品信息状态是否正常、验证问题回答是否正确、验证用户是否已经购买、验证用户购买的商品数量是否在限制的范围内、验证商品库存是否充足、扣除商品购买数量、创建订单、返回提示信息
保证数据一致性、高效处理

秒杀商品类型

单商品秒杀特点
简单,没有选择、独立
没有关联关系、容易
验证逻辑更少

组合商品秒杀特点
支持多商品选择
多商品库存、限购数量
验证和处理的逻辑多

秒杀级别

万次秒杀
就是几万次请求,活动就结束,请求比较少,并发低,实现基本功能就可以满足
不太需要考虑性能优化方面,这样服务器资源时间等成本节省
单机,比如PHP+Mysql数据库放在一起,并且动态访问,单台就可以支持

百万次秒杀
请求量和并发量都开始有明显提升,需要做部分优化,系统架构、代码逻辑、服务器等
WEB服务器集群,多台服务器
引入Redis缓存,不能仅仅Mysql来应对更多的查询
不能有严重的接口漏洞问题等低级错误
压力测试,把所有接口高并发测试

过亿次秒杀
所有问题都要极端化考虑,不管是在逻辑运算,及运算次数,多一次内存的分配,等等,哪些能优化,哪些能避免
没遇到过的问题很可能发生,比如想象中极不可能发生的问题,服务器宕机,内存不够等,我们也需要思考避免
需要能临时调配大量的服务器资源,平时准备大量的不现实也不合理,因为并发可能很短就结束。比如购买云主机,在时间阶段内利用,快速克隆部署运行,不需要的时候再释放掉,在成本方面可以减少服务器和资源型的依赖
引入中控服务器(控制服务器的服务器)
多机房多数据中心 

 大概秒杀流程

在用户在点击购买的时候,需要做一些安全性的参数验证,大致包括活动标识、商品标识、要购买的商品数量、安全加密串、再比如问答验证。在完成这些信息的提交后,程序流程的大概验证顺序为:
验证用户是否登录->验证加密签名是否合理->验证规定时间段内IP和用户保持不变(如五分钟,加密签名里包含用户登录的时间还有IP,在用户购买的时候提交和当前时间以及IP对比)->验证问题回答是否正确->统一格式化一下购买的商品信息(比如转化为容易处理的数组格式)->配合redis缓存,验证一下订单是否已经购买过,防止重复购买->验证活动、商品的状态是否正常(比如是否卖完,是否还在卖等)->先更新缓存中剩余的商品数量,再更新数据库扣除商品的数量->扣除成功后创建订单信息->创建完成订单后设置缓存用于已经购买

优化单机性能

 提高页面访问速度

减少页面大小,启用gzip压缩
减少资源请求数量,合并和压缩css、js
设置浏览器缓存,利用CDN加速

 提高秒杀接口速度

因为性能方面几个瓶颈,无非就是通过网络请求数据,网络速度是比较大的问题;另外通过磁盘读写文件性能也比较低,那么我们程序的时候,我们尽可能少用这种性能比较低的存储设备,比如内存,内存的速度要比磁盘快很多。当然还有更多方法....
接口静态化
如果接口是PHP编写的动态程序,效率肯定比静态化的低很多,比如我们的首页、商品展示页面 ,是动态页面的话,动态的效率再高,里面包括很多效率高的处理方法,比如缓存,就算再快,也会有开销,也不如直接静态化效率高。如果我们把页面静态化,效率会提高很多,Nginx对这种静态文件的访问极其高的,一秒钟一两万次不会存在很大的瓶颈,对于服务器压力来说,一秒钟一两万次服务器负载也是没多大问题;但是我们用PHP写的动态结果,一秒钟一两万次肯定是不可以的,性能再好一秒钟五六百次一千次效率也是很高的了。所以可以把接口做静态化处理很快。比如,把首页静态化,生成静态的index.html文件就可以了,剩下只需要处理的一个地方就是登陆,基于cookie的方式保存登陆状态,cookie的解析是通过服务端来解析,而我们在这个环节需要改一下,页面展示的登录状态判断不能通过服务器端了,因为已经是一个静态页面了,这里我们需要通过js来读取cookie信息,把这个状态信息通过js输出出来就可以了。当然还有其他的接口,比如最重要的秒杀接口,我们肯定会想,这个接口肯定是一个动态的,如果是一个静态的大家看到的结果是一样的,那么还怎么进行秒杀?没错,秒杀接口肯定是一个动态的,按功能来讲,如果是静态的就是假的了,欺骗用户。比如发布了一款产品,整体下来大约是1000000次并发请求访问,我们的实际要卖出的商品数量为1000个,那么我们有必要全部的访问都是动态的么,有没有想过前面1000个是动态访问,后面的多余访问次数是静态的1000000-1000次,那么这样多放几台服务器,或者部署CDN,带宽充足就好了。 
快速终止的逻辑放在前面,先判断条件,再执行后面的程序(比如面对一千万个访问,商品库存有5000件,那么商品卖完[10000000-5000个访问不走程序逻辑]或者活动没有进行等状态直接访问静态文件,和上一个差不多)
增加冗余的定制化数据,保证程序更快捷。以空间换时间,比如增加新的数据结构,增加数组,在存储空间啊,内存啊,程序编写的复杂度要求高一点,让用户更快速的访问我们的接口。

提高数据处理速度

数据量大的时候,数据库索引不能少,更不能乱。(数据量大的时候,效率差别会很明显。索引尽量的少,更不能乱,需要的时候就创建它,如果需要的时候没有索引,那会非常不好。如在订单表里,用户在秒杀的时候,需要判断用户之前有没有参加过活动,那么我们在表里会查询响应用户和活动id两个字段进行创建符合索引,效率会更高,同时还可以用前面字段索引查询,利用率高了;还比如商品相关的订单,给商品创建订单;还比如时间范围内的,给时间加索引;创建索引后,数据每次的更改和插入也会对索引操作,这样对数据写操作增加开销。要遵循索引的创建原则。再比如我们对订单状态做了索引,因为字段节点上有几个状态节点,节点特别少,如果表里了有1000万的数据量,发现每个节点里有100万个数据,虽然对符合状态的查询很快,但是每个节点下符合条件的都有100万的数据量,但是后续的处理很困难。这也是不符合的,还不如多几次查找。我们尽量把索引建到比较分散的列上,比如uid,活动id虽然现在不多,可能慢慢的规模会增多)
减少数据规模(如果要检索的符合条件的数据很多,那么效率也不会体现出来,比如查询出符合条件的数据太多,而节点类型并不多的字段。比如一个数据表有1000万条数据,另一个表里有10万,那很明显我们在这1000万条数据里做操作及时有索引,但是他的更新查找等写操作比只有10万条数据的要差,这就是规模的影响。如何减少呢?比如预计订单表会很大,订单表相应的字段有活动字段,那么我们可以通过不同的活动创建不同的表来存储响应活动的订单信息。这样规模就减少了)
将数据放到redis缓存中(存内存、结构简单、性能好,可以每秒做到一万次甚至几万次的操作,减少Mysql数据库压力)
代码逻辑方面的优化
比如在参数校验方面,可以把较为简单的校验放在前面;再比如数据结构的合理利用;合理地简化程序流程

 分布式方案

多个接入层服务器
如果效率要说高的话,其实单个服务器最高的,所有的请求传输都在一台服务器完成,如果都在同一个服务器,整个业务的流程会更短。单机自己接收自己处理,数据库、缓存,都在同一台服务器可以避免网络传输这种问题。但是要考虑更大的访问量,随着访问量越大,单机的服务是没办法满足需求的,当达到更高的访问级别,那么不得不考虑分布式方案。如果规模比较小的话,像是面向企业内部的这种产品,用户量不大,所以单机就可以完成,最多的是就是数据库独立,保证安全稳定,处理服务器就那么一台,这样规模比较小。关于分布式的方案无非就是不是一个服务器,多个服务器就可以理解为分布式。有的产品很难做到分布式,原因呢就是有些业务逻辑很难分开,比如session分布式存储和文件,分流访问导致session丢失,当然也可以配置到缓存里解决这个问题。所以我们在开发的时候一定要注意减少服务器的差异,每一台服务器最好是一样的,就算随时去掉一台服务器或者加入一批服务器不会有太大影响,这样就可以很容易实现分布式。比如一个集群,就一个接入的服务器,一般来说访问量在百万级或者千万级左右,我构建一个集群规模可大可小,也就可以提供这种服务。但是秒杀系统可能规模更大,过亿的话,集群小的话,可能就无法正常提供服务了。因为我们就一个集群,网络的话可能访问就比较慢,还有距离问题,比如跨南北城市,因为就一个接入的终端。另外一个接入的话,自身处理的能力也是有瓶颈的。所以我们在做秒杀的时候,尽可能考虑更大的规模,更多的接入层服务器。
多个机房,每个机房部署一个集群,每个集群一个LVS作为接入层服务器做请求转发,不做业务处理,性能稳定性会更好,也就是部署多个集群。一般一个LVS服务器每天提供上亿个访问转发是有压力的,如果我们的访问量是十亿,做十个集群,LVS是可以应付的。我们可以做多个机房,分为华北、华南等区域划分,这么做的目的还是希望离用户更近一点,当然还是希望能同城访问,这样访问更好。
智能DNS为不同网络不同地域的用户解析到不同的LVS,就好比用户来自北京,当然就希望用户访问北京的接入层服务;比如我用的电信宽带,当然还是希望访问的是电信。整体而言智能DNS就可以保障效率。
部分静态文件、接口引入CDN,因为CND是两三百个机房,CDN本身也有缓存,这样处理的速度会提高,减轻我们自己服务器的压力,硬件就不要考虑了,只需要考虑带宽等因素

多WEB服务器单数据中心
LVS后端挂载多个WEB服务器,规模在2~10不等,不建议规模做特别大,不如多做几个集群,因为管理不太方便。
单数据中心,开发更简单,数据一致性有保证。如果就一个数据库,那么开发在请求配置的时候,就当做本地请求就可以了,不用考虑一次操作写多少次,需不需要同步。部署多个服务的话,代价就大,维护起来就有成本,单个的话还是比较方便的,百万级的话还是可以的。不过如果太大,还是要花费大量人力物力。
跨机房时网络问题比较突出,要有光线专线宽带保障。比如同机房的网络延迟是几毫米,跨机房的话可能几十毫秒,就跟跨城市差不多。一定要保障跨机房的时候,保障速度。当然数据调用也要考虑网络时间开销。

 多WEB服务器多数据中心

在多数据中心就可以避免单数据中心的一些问题,一个是单数据中心的瓶颈,另一个是网络问题。我们在每一组服务器都有自己独立的数据中心,那么所有的数据处理在本地局域网就完成这些问题,就不会出现这种跨机房延迟问题。因为我们有多个数据中心,它的处理能力也会提升,所以在处理瓶颈这块就不是问题了。
每个数据中心都是一个独立的服务器集群,因为每个数据中心都是和一个集群在一起的,这一组服务器在处理数据的读写等操作,都在局域网完成,所以效率高了。不足的地方是,数据一致性的难度就会有所提高,那么我们可以引入中控服务器来调配多数据中心的数据,保证用户与库存的均衡(管理数据中心,调配数据中心的数据,他不会处理业务,只处理多个数据中心的数据变化),那么数据同步吗、数据一致性也就不会有问题出现了。
服务器规模预估
数据量:订单量、用户量分析
访问量:全程的请求数量,每次请求的速度
资源量:CDN带宽(请求数量和文件资源大小来算),服务器带宽,WEB服务器,Mysql,redis数量

防止黄牛

问答、交互式验证码等机制、用户访问页面路径校验、页面停留时间和点击位置和时间、用户基本信息和IP还有浏览信息等

---恢复内容结束---

程序之家二维码

小额赞赏

000
评论