优化的点:秒杀系统单独独立出来;独立作一个机器集群;将热点数据(如库存数据)单独放到一个缓存系统中,以提升“读性能“ ;增长秒杀答题,防止有秒杀器抢单;页面进行完全的动静分离,使得用户秒杀时不须要刷新整个页面;在服务端对秒杀商品进行本地缓存,不须要再调用依赖系统的后台获取;增长系统限流保护,防止最坏状况发生。前端
1,把静态数据缓存到离用户最近的地方,缓存到用户浏览器里、CDN 上或者在服务端的 Cache 中java
2,静态化改造就是要直接缓存 HTTP 链接。mysql
3,让谁来缓存静态数据也很重要,缓存能够放在Web 服务器(如 Nginx、Apache、Varnish)更擅长处理大并发的静态文件请求。redis
4.4.1 发现静态热点数据:强制卖家报名,活动商品打标;大数据计算统计top N 商品算法
4.4.2 发现动态热点数据:1,构建一个异步的系统,它能够收集交易链路上各个环节中的中间件产品的热点 Key;2,创建一个热点上报和能够按照需求订阅的热点服务的下发规范;3,将上游系统收集的热点数据发送到热点服务台,而后下游系统(如交易系统)就会知道哪些商品会被频繁调用,而后作热点保护。sql
4.5.1 优化:缓存起来,而后能够采用 LRU 淘汰算法替换。数据库
4.5.2 限制:例如对被访问商品的 ID 作一致性 Hash,而后根据 Hash 作分桶,每一个分桶设置一个处理队列,这样能够把热点商品限制在一个请求队列里,防止因某些热点商品占用太多的服务器资源,而使其余请求始终得不到服务器的处理资源。浏览器
4.5.3 隔离:缓存
4.5.3.1 业务隔离:开卖前,卖家单独报名,对热点数据提早作预热安全
4.5.3.2 系统隔离:能够经过分组部署的方式和另外 99% 分开,杀能够申请单独的域名,目的也是让请求落到不一样的集群中。
4.5.3.3 数据隔离:秒杀所调用的数据大部分都是热点数据,好比会启用单独的 Cache 集群或者 MySQL 数据库来放热点数据,目的也是不想0.01% 的数据有机会影响 99.99% 数据。
服务器的处理资源是恒定的,为了用户体验,咱们必须按照用户请求的最大峰值进行预分配机器吗,但这样会大大增长服务器成本;因此削峰一方面是为了下降成本,一方面是为了系统的稳定性。
5.2.1 排队:经过使用消息中间件(做用:异步,解耦,削峰填谷)把请求平滑缓冲入队列中,能够承接瞬间过来的大流量,避免瞬时大流量请求致使系统崩溃。而后业务系统再去处理队列中的请求。若是瞬时流量过大,达到了消息队列处理的上限,这时候请求可能会被直接丢弃。咱们能够采用把请求序列化到文件中来进行处理(相似于mysql bin log的方式),后期再从文件中读取请求进行进一步的处理。
5.2.2 答题: 1,能够防止秒杀器 2,能够把请求基于时间分片,下降瞬时请求并发率。
5.2.3 分层过滤:在不一样的层次尽量地过滤掉无效请求,让“漏斗”最末端的才是有效的请求。总之就是读不作强一致性校验,写时须要作一致性检查。很是适合交易性的写请求,好比减库存或者拼车等,拼车时座位是一直在变化的,不必定要保证读到的必定是准确的,只须要在写数据时进行强一致性检验便可。
5.2.4 业务手段:除了采用技术手段解决,还能够采用业务手段。好比秒杀时,能够经过发放优惠券或者开启抽奖活动等吸引一部分买家到其余地方,也能够起到缓存流量的做用。
这里主要讨论服务端性能。主要因素:响应时间、线程数
qps:每秒处理的请求数。
6.1.1 响应时间对qps的影响
响应时间 = cpu执行时间+线程等待时间(好比 RPC、IO 等待、Sleep、Wait)
真正对cpu有影响的是cpu的执行时间,咱们应该致力于减小 CPU 的执行时间。
6.1.2 线程数对qps的影响
线程并不是越多越好,线程自己也消耗资源,也受到其余因素的制约,好比线程切换成本高,每一个线程也会耗费必定的内存。
最佳实践计算公式:线程数 = [(线程等待时间 + 线程 CPU 时间)/线程 CPU 时间] × CPU 数量,最好的办法是经过性能测试来发现最佳的线程数。
首先,秒杀场景的瓶颈更多的发生在cpu上。
6.2.1 经过cpu诊断工具:JProfiler、Yourkit
6.2.2 经过jstack定时打印调用栈,若是某些函数调用频繁或者耗时较多,这些函数就会出如今系统调用栈里,经过采样的方式定时发现耗时较多的函数
6.2.3 怎么判断cpu有没有达到瓶颈:当QPS达到极限时,判断cpu使用率有没有超过95%
6.3.1 减小编码:编码查表很是耗费资源
6.3.2 减小序列化:序列化大部分发生在RPC调用当中,应该避免或减小RPC调用,能够将两个关联性比较强的应用合并部署到同一台机器,使用同一个TOMCAT,且不能走本机的SOCKET。
6.3.3 java极致优化 1,首先作静态化改造,让大部分的请求在NIGNX服务器或WEB服务器上直接返回;2,使用Servlet处理请求,避免使用mvc框架,能够绕过一些无用的处理逻辑(取决于项目对框架的依赖);3,直接输出流数据。响应时推荐使用JSON,而不是模板引擎(通常都是解释执行)。
6.3.4 并发读优化:集中式缓存为了提升命中率通常会采用一致性hash的策略。但还不足以处理大秒,能够采用应用层的localcache,在秒杀系统的单机上缓存商品相关的数据,因此还须要动静数据的分离。静态数据全量推送上去,动态数据采用被动失效的方式缓存一段时间,失效后再去主动缓存拉取最新的数据。
咱们使用电商平台购物通常都会涉及两个核心流程:下订单、付款,那么咱们应该是在用户下订单的时候减库存仍是在实际付款后再进行减库存操做呢?接下去咱们分析一下各个方案的优缺点,以及存在的问题。
7.1.1 下单减库存:这种方式必定不会出现超卖,但可能出现客户下完单,不付款的状况。若是存在恶意用户大量下单,却不付款,这种方式很快就会把库存减为0,致使商品不能正常售卖。
7.1.2 付款减库存:会产生大量客户下完单,付款时却不成功的状况,库存超卖,致使客户体验较差。
7.1.3 预扣库存: 用户下单后,库存为其保存一段时间(如10分钟),超时未付款后,库存自动释放,释放后其余买家能够继续购买。在买家付款前,系统会检验该订单的库存是否还有保留:1,若是保留成功,则完成付款则实际减去库存,2,若是没有保留,则再次尝试预扣;若是库存不足(就是预扣失败),则不容许付款。若是预扣成功,则完成付款并实际减去库存。
7.2.1 问题: 预扣库存的方式,虽然能够在必定程度上解决上面的问题,但没法完全解决。针对恶意下单这种状况,虽然把有效的付款时间设置为 10 分钟,可是恶意买家彻底能够在 10 分钟后再次下单,或者采用一次下单不少件把库存减完。
7.2.2 解决方案:解决办法仍是要结合安全和反做弊的措施来制止。例如给常常下班不付款的用户打标(能够在被打标的用户下单时不减库存^-^),给某些类目设置最大购买件数(例如活动商品一次只能购买三件),以及对重复下单不付款的操做进行次数限制。
普通业务系统会采用预扣库存的方式,秒杀系统由于大部分人都是抱着“抢到就是赚到的心态”,不多会下单不付款。并且秒杀中商家通常不容许超卖,另外逻辑简单,在性能上也更有优点。下单减库存,在数据一致性上主要表现为保证大并发时库存不能为负数,通常采用事务控制,保证减后不为负数,其次能够采用库存字段设置为无符号整数,这样减为负数时会抛出sql错误;再有一种是使用case when语法,UPDATE item SET inventory = CASE WHEN inventory >= xxx THEN inventory-xxx ELSE inventory END
7.4.1 库存在交易系统中很明显是热点数据。大并发下单的读能够采用localcache(即在秒杀系统的单机上缓存商品相关的数据)和对数据进行分层过滤的方式,可是大并发写时不管如何都避免不了的。
7.4.2 若是业务逻辑没有复杂的SKU库存和总库存这种联动关系的话,彻底能够直接放在带有持久化功能的缓存系统(如redis)中,若是有比较复杂的逻辑或者须要使用事务,仍是放在数据库中操做比较好。
7.4.2 另外就是单个热点商品会影响整个数据库的性能,致使0.01%影响99.99%的商品的售卖,能够采用把热点商品单独放在热点库中,这种须要作热点数据的动态迁移以及单独的数据库。
7.4.3 要解决并发锁的问题有两种方案:1,应用层作排队,2,数据库层作排队
8.1.1 架构阶段:异步容灾,异步化,分组隔离,避免单点
8.1.2 编码阶段:超时处理,错误捕获,限流保护,异步线程
8.1.3 测试阶段:beta测试,自动化对比测试 ,保证测试用例的覆盖度,最坏状况下也有相应的处理流程。
8.1.4 发布阶段:分批发布,多版本发布,有紧急的回滚机制
8.1.5 运行阶段:实时监控报警,过载保护,自动降级,数据对帐
8.1.6 故障发生:快速恢复,故障定位
8.2.1 降级:配置一套系统化预案与开关系统(如临时下架优惠券系统)
8.2.2 限流:限制一部分流量保护系统。分为:客户端限流与服务端限流。限流的实现方式既要支持URL以及方法级别的限流又要支持基于QPS与线程的限流(经过压测获取系统最大QPS,好比10000,咱们能够设置8000来进行限流保护)。限流时会致使用户请求失败必定要设置超时,防止因被限流的请求因不能fast fail快速失败而拖垮系统。
8.2.3 拒绝服务:当系统负载达到必定的阈值时,例如 CPU 使用率达到 90% 或者系统 load 值达到2*CPU 核数时,系统直接拒绝全部请求。在最前端的 Nginx 上设置过载保护,当机器负载达到某个值时直接拒绝 HTTP 请求并返回 503 错误码,在 Java 层一样也能够设计过载保护。这样设计在系统负载太高时不提供服务用来保护系统,当负载降低时又能够很容易恢复。
8.2.4 总结:网站的高可用建设是基础,能够说要深刻到各个环节,更要长期规划,并进行体系化建设,要在预防(创建常态的压力体系,例如上线前的单机压测到上线后的全链路压测)、管控(作好线上运行时的降级、限流和兜底保护)、监控(创建性能基线来记录性能的变化趋势以及线上机器的负载报警体系,发现问题及时预警)和恢复体系(遇到故障要及时止损,并提供快速的数据订正工具等)等这些地方增强建设,每个环节可能都有不少事情要作。