抢购活动性能瓶颈调优

Hi,我是阿昌,今天学习记录的是关于抢购活动性能瓶颈调优

每年的双十一都是很多研发部门最头痛的节日,由于这个节日比较特殊,公司一般都会准备大量的抢购活动,相应的瞬时高并发请求对系统来说是个不小的考验。

一次做双十一抢购活动,优惠力度特别大,购买量也很大,提交订单的接口 TPS 一度达到了 10W。

在首波抢购时,后台服务监控就已经显示服务器的各项指标都超过了 70%,CPU 更是一直处于 400%(4 核 CPU),数据库磁盘 I/O 一直处于 100% 状态。由于瞬时写入日志量非常大,导致后台服务监控在短时间内,无法实时获取到最新的请求监控数据,此时后台开始出现一系列的异常报警。

更严重的系统问题是出现在第二波的抢购活动中,由于第一波抢购时发现后台服务的压力比较大,于是就横向扩容了服务,但却没能缓解服务的压力,反而在第二波抢购中,系统很快就出现了宕机。这次活动暴露出来的问题很多。

  • 首先,由于没有限流,超过预期的请求量导致了系统卡顿;
  • 其次,我们是基于 Redis 实现了一个分布式锁分发抢购名额的功能,但这个功能抛出了大量异常;
  • 再次,就是误判了横向扩容服务可以起到的作用,其实第一波抢购的性能瓶颈是在数据库,横向扩容服务反而又增加了数据库的压力,起到了反作用;
  • 最后,就是在服务挂掉的情况下,丢失了异步处理的业务请求

以上面的这个案例为背景,讲下抢购业务中的性能瓶颈该如何调优。


一、抢购业务流程

在进行具体的性能问题讨论之前,不妨先来了解下一个常规的抢购业务流程,这样方便更好地理解一个抢购系统的性能瓶颈以及调优过程

  • 用户登录后会进入到商品详情页面,此时商品购买处于倒计时状态,购买按钮处于置灰状态。
  • 当购买倒计时间结束后,用户点击购买商品,此时用户需要排队等待获取购买资格,如果没有获取到购买资格,抢购活动结束,反之,则进入提交页面。
  • 用户完善订单信息,点击提交订单,此时校验库存,并创建订单,进入锁定库存状态,之后,用户支付订单款。
  • 当用户支付成功后,第三方支付平台将产生支付回调,系统通过回调更新订单状态,并扣除数据库的实际库存,通知用户购买成功。


二、抢购系统中的性能瓶颈

熟悉了一个常规的抢购业务流程之后,再来看看抢购中都有哪些业务会出现性能瓶颈。

1、商品详情页面

如果你有过抢购商品的经验,相信你遇到过这样一种情况,在抢购马上到来的时候,商品详情页面几乎是无法打开的。

这是因为大部分用户在抢购开始之前,会一直疯狂刷新抢购商品页面,尤其是倒计时一分钟内,查看商品详情页面的请求量会猛增。此时如果商品详情页面没有做好,就很容易成为整个抢购系统中的第一个性能瓶颈。

类似这种问题,通常的做法是提前将整个抢购商品页面生成为一个静态页面,并 push 到 CDN 节点,并且在浏览器端缓存该页面的静态资源文件,通过 CDN 和浏览器本地缓存这两种缓存静态页面的方式来实现商品详情页面的优化。

2、抢购倒计时

在商品详情页面中,存在一个抢购倒计时,这个倒计时是服务端时间的,初始化时间需要从服务端获取,并且在用户点击购买时,还需要服务端判断抢购时间是否已经到了。如果商品详情每次刷新都去后端请求最新的时间,这无疑将会把整个后端服务拖垮。

可以改成初始化时间从客户端获取,每隔一段时间主动去服务端刷新同步一次倒计时,这个时间段是随机时间,避免集中请求服务端。

这种方式可以避免用户主动刷新服务端的同步时间接口。

3、获取购买资格

在抢购中已经通过库存数量限制用户了,那为什么会出现一个获取购买资格的环节呢?

进入订单详情页面后,需要填写相关的订单信息,例如收货地址、联系方式等,在这样一个过程中,很多用户可能还会犹豫,甚至放弃购买。

如果把这个环节设定为一定能购买成功,那我们就只能让同等库存的用户进来,一旦用户放弃购买,这些商品可能无法再次被其他用户抢购,会大大降低商品的抢购销量。

增加购买资格的环节,选择让超过库存的用户量进来提交订单页面,这样就可以保证有足够提交订单的用户量,确保抢购活动中商品的销量最大化。

获取购买资格这步的并发量会非常大,还是基于分布式的,通常可以通过 Redis 分布式锁来控制购买资格的发放

4、提交订单

由于抢购入口的请求量会非常大,可能会占用大量带宽,为了不影响提交订单的请求,建议将提交订单的子域名与抢购子域名区分开,分别绑定不同网络的服务器

用户点击提交订单,需要先校验库存,库存足够时,用户先扣除缓存中的库存,再生成订单。如果校验库存和扣除库存都是基于数据库实现的,那么每次都去操作数据库,瞬时的并发量就会非常大,对数据库来说会存在一定的压力,从而会产生性能瓶颈。与获取购买资格一样,同样可以通过分布式锁来优化扣除消耗库存的设计。

由于已经缓存了库存,所以在提交订单时,库存的查询和冻结并不会给数据库带来性能瓶颈。但在这之后,还有一个订单的幂等校验,为了提高系统性能,同样可以使用分布式锁来优化。而保存订单信息一般都是基于数据库表来实现的,在单表单库的情况下,碰到大量请求,特别是在瞬时高并发的情况下,磁盘 I/O、数据库请求连接数以及带宽等资源都可能会出现性能瓶颈。

此时可以考虑对订单表进行分库分表,通常我们可以基于 userid 字段来进行 hash 取模,实现分库分表,从而提高系统的并发能力。

5、支付回调业务操作

在用户支付订单完成之后,一般会有第三方支付平台回调的接口,更新订单状态。

除此之外,还可能存在扣减数据库库存的需求。如果库存是基于缓存来实现查询和扣减,那提交订单时的扣除库存就只是扣除缓存中的库存,为了减少数据库的并发量,会在用户付款之后,在支付回调的时候去选择扣除数据库中的库存。此外,还有订单购买成功的短信通知服务,一些商城还提供了累计积分的服务。

在支付回调之后,可以通过异步提交的方式,实现订单更新之外的其它业务处理,例如库存扣减、积分累计以及短信通知等。

通常可以基于 MQ 实现业务的异步提交。


三、性能瓶颈调优

了解了各个业务流程中可能存在的性能瓶颈,再来讨论下,完成了常规的优化设计之后,商城还可能出现的一些性能问题,又该如何做进一步调优。

1、限流实现优化

限流 是常用的兜底策略,无论是倒计时请求接口,还是抢购入口,系统都应该对它们设置最大并发访问数量,防止超出预期的请求集中进入系统,导致系统异常。

通常我是在网关层实现高并发请求接口的限流,如果使用了 Nginx 做反向代理的话,就可以在 Nginx 配置限流算法

Nginx 是基于漏桶算法实现的限流,这样做的好处是能够保证请求的实时处理速度。

Nginx 中包含了两个限流模块:ngx_http_limit_conn_module 和 ngx_http_limit_req_module

  • 前者是用于限制单个 IP 单位时间内的请求数量
  • 后者是用来限制单位时间内所有 IP 的请求数量。

以下分别是两个限流的配置:

limit_conn_zone $binary_remote_addr zone=addr:10m;server {location / {limit_conn addr 1;}
http {limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;server {location / {limit_req zone=one burst=5 nodelay;}
}

在网关层,还可以通过 Lua 编写 OpenResty 来实现一套限流功能,也可以通过现成的 Kong 安装插件来实现。

除了网关层的限流之外,还可以基于服务层实现接口的限流,通过 Zuul RateLimitGuava RateLimiter 实现。

2、流量削峰

瞬间有大量请求进入到系统后台服务之后,首先是要通过 Redis 分布式锁获取购买资格,这个时候我们看到了大量的“JedisConnectionException Could not get connection from pool”异常。

这个异常是一个 Redis 连接异常,由于当时的 Redis 集群是基于哨兵模式部署的,哨兵模式部署的 Redis 也是一种主从模式,在写 Redis 的时候都是基于主库来实现的,在高并发操作一个 Redis 实例就很容易出现性能瓶颈。

可能会想到使用集群分片的方式来实现,但对于分布式锁来说,集群分片的实现只会增加性能消耗,这是因为需要基于 Redission 的红锁算法实现,需要对集群的每个实例进行加锁。

后来使用 Redission 插件替换 Jedis 插件,由于 Jedis 的读写 I/O 操作还是阻塞式的,方法调用都是基于同步实现,而 Redission 底层是基于 Netty 框架实现的,读写 I/O 是非阻塞 I/O 操作,且方法调用是基于异步实现。

但在瞬时并发非常大的情况下,依然会出现类似问题,此时,可以考虑在分布式锁前面新增一个等待队列,减缓抢购出现的集中式请求,相当于一个流量削峰

当请求的 key 值放入到队列中,请求线程进入阻塞状态,当线程从队列中获取到请求线程的 key 值时,就会唤醒请求线程获取购买资格。

3、数据丢失问题

无论是服务宕机,还是异步发送给 MQ,都存在请求数据丢失的可能。

例如,当第三方支付回调系统时,写入订单成功了,此时通过异步来扣减库存和累计积分,如果应用服务刚好挂掉了,MQ 还没有存储到该消息,那即使重启服务,这条请求数据也将无法还原。重试机制是还原丢失消息的一种解决方案。

在以上的回调案例中,可以在写入订单时,同时在数据库写入一条异步消息状态,之后再返回第三方支付操作成功结果。

在异步业务处理请求成功之后,更新该数据库表中的异步消息状态。

假设重启服务,那么系统就会在重启时去数据库中查询是否有未更新的异步消息,如果有,则重新生成 MQ 业务处理消息,供各个业务方消费处理丢失的请求数据。


四、总结

减少抢购中操作数据库的次数,缩短抢购流程,是抢购系统设计和优化的核心点。

抢购系统的性能瓶颈主要是在数据库,即使对服务进行了横向扩容,当流量瞬间进来,数据库依然无法同时响应处理这么多的请求操作。

可以对抢购业务表进行分库分表,通过提高数据库的处理能力,来提升系统的并发处理能力。

除此之外,还可以分散瞬时的高并发请求,流量削峰是最常用的方式,用一个队列,让请求排队等待,然后有序且有限地进入到后端服务,最终进行数据库操作。

当队列满了之后,可以将溢出的请求放弃,这就是限流了。

通过限流和削峰,可以有效地保证系统不宕机,确保系统的稳定性。


在提交了订单之后会进入到支付阶段,此时系统是冻结了库存的,一般会给用户一定的等待时间,这样就很容易出现一些用户恶意锁库存,导致抢到商品的用户没办法去支付购买该商品。
那该怎么优化设计这个业务操作呢?

发现确定的用户,可以针对他实行黑名单机制


Day820.抢购活动性能瓶颈调优 -Java 性能调优实战相关推荐

  1. 深扒,用 6 部分讲完 Java 性能调优:多线程 + 设计模式 + 数据库

    Java 性能调优 Java 性能调优,是一个老生常谈的话题.可能有些人觉得没用,一些细小的地方没有好修改的,改与不改对于代码的运行效率有什么影响呢? Java 性能调优不单单是学一门编程语言那么简单 ...

  2. 拿着阿里这份Java性能调优手册,我把公司项目性能提升了300%

    程序的性能受代码质量的直接影响.那么该如何让代码在级别上提升系统性能呢? 其实性能提升永远没有捷径,需要 分析.优化.实验.监控 ,需要一点点积累和深入.随着你对项目和性能优化理解不断深入,会发现提升 ...

  3. 调优为王!阿里巴巴彩版java性能调优实战,终于到手了!

    怎样才能做好性能调优? 开始之前,我先来讲一下我对性能调优的看法.在我看来Java的性能调优并不是像学习编程语言一样可以通过学习掌握,它是没有办法用直线的思维学会并掌握使用的,并且它对于程序员来说,对 ...

  4. GitHub疯狂转发!阿里巴巴彩版java性能调优实战,终于到手了!文末福利

    怎样才能做好性能调优? 开始之前,我先来讲一下我对性能调优的看法.在我看来Java的性能调优并不是像学习编程语言一样可以通过学习掌握,它是没有办法用直线的思维学会并掌握使用的,并且它对于程序员来说,对 ...

  5. 《Java性能调优实战》笔记(一)Java编程性能调优、多线程性能优化

    文章目录 一.Java性能调优概述 1.1 性能调优标准 1.2 制定性能调优策略 二.Java编程性能调优 2.1 字符串 2.2 正则表达式 2.3 ArrayList和LinkedList的选择 ...

  6. java性能保障技术_狙击P7!阿里大佬亲授“Java性能调优技术宝典”,太完整了!...

    一.前言 什么是性能调优? 性能调优其实很好理解,就是优化硬件.操作系统.应用之间的一个充分的协作,最大化的发挥出硬件的极致性能,来应对高负载的业务需求. 为什么需要性能优化? 其实说到底就是两个原因 ...

  7. 为什么对 Java 性能调优最后都像在调 you?

    不知道你有没有发现,优化Java,或者任何其他语言的代码性能经常被当做是一种暗黑艺术. 性能分析有种神秘感.画面类似是这样的:一个「黑客」经过多年练就的手艺,能够快速深入了解某个系统,并提出神奇的解决 ...

  8. java g1 收集调优_Java性能调优:充分利用垃圾收集器

    java g1 收集调优 JVM背后发生了什么,垃圾回收如何影响Java性能? 性能调优世界是一个危险的地方,一个JVM标志失衡,事情很快就会变得繁琐. 因此 ,我们决定求助于Java性能调优专家, ...

  9. java 性能调优_Java性能调优调查结果(第三部分)

    java 性能调优 这是本系列文章的第三篇,我们将分析2014年10月进行的调查的结果.如果您尚未这样做,我建议从本系列的前两篇文章开始: 问题严重性分析和监视域分析 . 这篇文章着重于故障排除/根本 ...

最新文章

  1. 深度数据包检测DPI开发解析
  2. 不同Activity之间传递数据--Bundle对象和startActivityForResult方法的实现
  3. [导入]XML for Analysis(XMLA)开发详解-(3)各版本的SQL Server及Windows下配置XMLA over HTTP访问SASS(OLAP)的文档合集...
  4. jvm性能调优实战 - 36XX:SoftRefLRUPolicyMSPerMB配置引起的Metaspace频繁FullGC
  5. git push出错 remote: Hook Error: request authorize server: curl error: Couldn't resolve host name
  6. epoll监听文件_介绍一下 Android Handler 中的 epoll 机制?
  7. LeetCode-1052:爱生气的书店老板
  8. 用SDWebImage加载FLAnimatedImage
  9. Python界面程序实例:按钮漂移,用Python小套路来撩女神
  10. ibatis.net 学习笔记
  11. 拿什么拯救Web时代的安全危机
  12. 卡巴斯基提供升级包 解决病毒库升级
  13. excel 度分秒转换经纬度
  14. TP-LINK设备如何配置无线上网短信Wifi认证?
  15. 解决 微信小程序 滚动 事件穿透 问题
  16. 华为服务器插键盘的位置,服务器如何调出软键盘
  17. maya群体拼图阵列动画插件 color dancerv1.1下载及教程
  18. 五种常见的嵌入式操作系统
  19. Cocos精品《热血传奇》|五千个日夜 同一个传奇
  20. 解决myeclipse中 保存代码时resetting selection耗时操作

热门文章

  1. es安全认证-search-guard
  2. Web 端口敲门的奇思妙想
  3. 腾讯为何做不了电商?
  4. #2020寒假集训#树形基础入门(Tree)代码笔记
  5. 什么是CNAS?你了解CNAS认证吗?
  6. ps一键滤镜网红文艺小清新VSCO全滤镜插件安装教程
  7. 若想进阶为软件架构师,这10本书必须读!
  8. 论面向服务架构设计及其应用
  9. OpenGL相机学习
  10. 计算机软件技术学什么专科,专科有哪些专业 专科有什么专业