Java性能调优实战
刘超
金山软件西山居技术经理
立即订阅
7535 人已学习
课程目录
已完结 48 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 怎样才能做好性能调优?
免费
模块一 · 概述 (2讲)
01 | 如何制定性能调优标准?
02 | 如何制定性能调优策略?
模块二 · Java编程性能调优 (10讲)
03 | 字符串性能优化不容小觑,百M内存轻松存储几十G数据
04 | 慎重使用正则表达式
05 | ArrayList还是LinkedList?使用不当性能差千倍
加餐 | 推荐几款常用的性能测试工具
06 | Stream如何提高遍历集合效率?
07 | 深入浅出HashMap的设计与优化
08 | 网络通信优化之I/O模型:如何解决高并发下I/O瓶颈?
09 | 网络通信优化之序列化:避免使用Java序列化
10 | 网络通信优化之通信协议:如何优化RPC网络通信?
11 | 答疑课堂:深入了解NIO的优化实现原理
模块三 · 多线程性能调优 (10讲)
12 | 多线程之锁优化(上):深入了解Synchronized同步锁的优化方法
13 | 多线程之锁优化(中):深入了解Lock同步锁的优化方法
14 | 多线程之锁优化(下):使用乐观锁优化并行操作
15 | 多线程调优(上):哪些操作导致了上下文切换?
16 | 多线程调优(下):如何优化多线程上下文切换?
17 | 并发容器的使用:识别不同场景下最优容器
18 | 如何设置线程池大小?
19 | 如何用协程来优化多线程业务?
20 | 答疑课堂:模块三热点问题解答
加餐 | 什么是数据的强、弱一致性?
模块四 · JVM性能监测及调优 (6讲)
21 | 磨刀不误砍柴工:欲知JVM调优先了解JVM内存模型
22 | 深入JVM即时编译器JIT,优化Java编译
23 | 如何优化垃圾回收机制?
24 | 如何优化JVM内存分配?
25 | 内存持续上升,我该如何排查问题?
26 | 答疑课堂:模块四热点问题解答
模块五 · 设计模式调优 (6讲)
27 | 单例模式:如何创建单一对象优化系统性能?
28 | 原型模式与享元模式:提升系统性能的利器
29 | 如何使用设计模式优化并发编程?
30 | 生产者消费者模式:电商库存设计优化
31 | 装饰器模式:如何优化电商系统中复杂的商品价格策略?
32 | 答疑课堂:模块五思考题集锦
模块六 · 数据库性能调优 (8讲)
33 | MySQL调优之SQL语句:如何写出高性能SQL语句?
34 | MySQL调优之事务:高并发场景下的数据库事务调优
35 | MySQL调优之索引:索引的失效与优化
36 | 记一次线上SQL死锁事故:如何避免死锁?
37 | 什么时候需要分表分库?
38 | 电商系统表设计优化案例分析
39 | 数据库参数设置优化,失之毫厘差之千里
40 | 答疑课堂:MySQL中InnoDB的知识点串讲
模块七 · 实战演练场 (4讲)
41 | 如何设计更优的分布式锁?
42 | 电商系统的分布式事务调优
43 | 如何使用缓存优化系统性能?
44 | 记一次双十一抢购性能瓶颈调优
结束语 (1讲)
结束语 | 栉风沐雨,砥砺前行!
Java性能调优实战
登录|注册

44 | 记一次双十一抢购性能瓶颈调优

刘超 2019-08-31
你好,我是刘超。今天我们来聊聊双十一的那些事儿,基于场景比较复杂,这一讲的出发点主要是盘点各个业务中高频出现的性能瓶颈,给出相应的优化方案,但优化方案并没有一一展开,深度讲解其具体实现。你可以结合自己在这个专栏的所学和日常积累,有针对性地在留言区提问,我会一一解答。下面切入正题。
每年的双十一都是很多研发部门最头痛的节日,由于这个节日比较特殊,公司一般都会准备大量的抢购活动,相应的瞬时高并发请求对系统来说是个不小的考验。
还记得我们公司商城第一次做双十一抢购活动,优惠力度特别大,购买量也很大,提交订单的接口 TPS 一度达到了 10W。在首波抢购时,后台服务监控就已经显示服务器的各项指标都超过了 70%,CPU 更是一直处于 400%(4 核 CPU),数据库磁盘 I/O 一直处于 100% 状态。由于瞬时写入日志量非常大,导致我们的后台服务监控在短时间内,无法实时获取到最新的请求监控数据,此时后台开始出现一系列的异常报警。
更严重的系统问题是出现在第二波的抢购活动中,由于第一波抢购时我们发现后台服务的压力比较大,于是就横向扩容了服务,但却没能缓解服务的压力,反而在第二波抢购中,我们的系统很快就出现了宕机。
这次活动暴露出来的问题很多。首先,由于没有限流,超过预期的请求量导致了系统卡顿;其次,基于 Redis 实现的分布式锁分发抢购名额的功能抛出了大量异常;再次,就是我们误判了横向扩容服务可以起到的作用,其实第一波抢购的性能瓶颈是在数据库,横向扩容服务反而又增加了数据库的压力,起到了反作用;最后,就是在服务挂掉的情况下,丢失了异步处理的业务请求。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《Java性能调优实战》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(22)

  • -W.LI-
    课后思考题:
    文中老师讲了预扣库存可以多放开一点。比如实际只有100件商品,允许预扣300。支付成功后扣去真实库存。之前某包买东西,就遇见过了几天客服联系说没货了退款这种。我之前做过一个是支付完真实库存扣件失败,直接退款回滚数据的。
    恶意用户刷单的话可以对用户进行封号处理,在redis中缓存用户待带支付的订单数,每次进入带支付前校验下待支付的集合里有多少(金额数目都可)。判定为恶意刷单的直接黑名单。某东用的好像是黑名单。

    作者回复: 不是预扣库存多放开,是在预扣库存前设置一个购买资格,购买资格是300,预扣库存还是100不变。不会出现商品超卖的问题。

    问答题回答思路很好。

    2019-08-31
    2
    2
  • -W.LI-
    当请求的 key 值放入到队列中,请求线程进入阻塞状态,当线程从队列中获取到请求线程的 key 值时,就会唤醒请求线程获取购买资格。
    老师好!能讲下写读请求使用队列缓存的原理么?
    之前有看过servlet3.0的和这个是不是有点像。客户端的链接是阻塞的,服务端通过队列缓存,处理完以后通过之前的链接把数据写回给客户端。
    servlet3.0是servlet规范,我现在基本用的都会spring自带的dispa***。如果要实现这总异步IO需要我们自己实现servlet是么?
    IO方面的知识很薄弱,netty好像很经典可是从来没看过,一方面觉得自己菜,领一方面就是工作中没用上,我从下手。希望老师给点学习指南谢谢。
    依依不舍(´..)❤
    2019-08-31
    2
  • 梁中华
    我们上次把redis客户端从jedis改成redission后,会有部分查询请求出现延迟几十毫秒的现象,换回jedis里面好了,不知道老师有没有遇到过这种情况,是不是netty的很么参数设置的不对?

    作者回复: 是的,可以参考下官方的使用文档,在单机且运用服务的CPU核数比较小的环境下,可能测试性能效果没有很大差别,如果想要效果更明显,可以在redis集群环境且应用服务的CPU核数在16以上的环境下进行性能压测,效果会更明显。

    https://github.com/redisson/redisson/wiki/2.-配置方法#21-程序化配置方法

    2019-09-03
    1
  • 许童童
    期待老师的思考题解答。

    作者回复: 嗯,黑名单机制是一个方向

    2019-08-31
    1
  • 超威丶
    没有比较好的办法,如果等到付款才扣减库存,可能会出现超卖!一般好的办法限制一个账户买同个商品的数量,减少损失

    作者回复: 没有直接的解决方案,但是我们可以通过间接的方案来减少这种恶意锁单的问题。建立信用以及黑名单机制,首先在获取购买资格时将黑名单用户过滤掉,其次在获取购买资格后,信用级别高的用户优先获取到库存。用户一旦恶意锁单就会被加入到黑名单。

    2019-08-31
    1
    1
  • 王三好
    队列使用什么实现的

    作者回复: 有界队列LinkedBlockingQueue或者Disruptor实现队列

    2019-11-18
  • zk_207
    超哥,你们订单超时是基于定时任务去做的吗?比如我订单是3min有效,怎么保证3min没支付就取消?

    作者回复: 我们是放在mq中去实现的,rabbitmq中有一个延时队列,当过期时间到了,就会被放到死信队列中,只要去死信队列中实时消费就好了。

    定时任务也是一种实现方式,在分布式部署定时任务时,要实现分布式定时任务。

    2019-10-27
  • zk_207
    超哥,请教个问题,就是秒杀的时候我们一般是下单预扣减库存,比如10分钟之后如果没有支付的话库存回流,这时候怎么保证库存准确性与系统性能呢?

    作者回复: 我们是通过一个生产者消费者的方式实现缓存库存的添加和删除,并且通过分布式锁来保证原子性

    2019-10-25
  • zk_207
    您好,请问本文中说的订单幂等性校验如何控制吗?还有就是库存放在缓存中,DB和缓存如何保证一致性?能说下解决方案吗?

    作者回复: 通过分布式锁来控制的,在下单时,以缓存中的库存为准,不会修改DB中的库存,只有在支付完成之后,回调时去数据库中扣除库存,严格上来说,只要业务操作没有bug,两者的库存就是一致的

    2019-10-24
  • Mr.Strive.Z.H.L
    老师你文中提到的 锁库存,我理解就是缓存中扣减库存,因为没有涉及到db,所以没有实际的上锁。是这样吧? 如果用户迟迟没付款,订单超时后会增加缓存的库存吗?

    作者回复: 扣除缓存中的库存也需要分布式锁,订单超时被取消会增加库存。

    2019-10-09
  • 张德
    可以减少支付的预留时间 比如说就五分钟 剩下的告诉他还有机会 可以等等看
    2019-09-14
  • godtrue
    课后思考及问题

    1:在提交了订单之后会进入到支付阶段,此时系统是冻结了库存的,一般我们会给用户一定的等待时间,这样就很容易出现一些用户恶意锁库存,导致抢到商品的用户没办法去支付购买该商品。

    首先,感觉老师的问题有点奇怪,没明白“某些用户恶意锁库存,导致抢到商品的用户没办法去支付购买该商品的”——我的理解,300个人抢到了抢购的商品,实际只有100个,如果是先款订单,谁先付款谁就先实际抢购到对应的商品呗!如果担心付款后,不要了要求退货,这就是另外的事情了,一般而言待抢购的商品都是物超所值的,需要担心的应该是多抢。

    如果是要控制有购买资格的人数,可以利用大数据用户画像的方式,将级别高信用好的用户优先放过去,当然,黑名单也用起来过滤掉恶意用户,再者就是限制用户购买的商品数量。

    作者回复: 是的

    2019-09-12
  • JackJin
    我们可以考虑在分布式锁前面新增一个等待队列,减缓抢购出现的集中式请求,相当于一个流量削峰。当请求的 key 值放入到队列中,请求线程进入阻塞状态,当线程从队列中获取到请求线程的 key 值时,就会唤醒请求线程获取购买资格。
    老师这里不太理解!

    作者回复: 相当于线程池中的阻塞队列

    2019-09-04
  • 钱彬彬
    老师,库存使用redis进行预热缓存,如何保证不超卖呢?老师说用分布式锁,这里可以说的详细点么?

    作者回复: 通过分布式锁可以保证不超卖,具体回顾之前41讲的分布式锁设计。

    2019-09-03
  • 晓杰
    请问老师,流量削峰使用等待队列的话,是使用jdk自带的队列吗

    作者回复: 由于请求量比较大,我们可以使用其他脚本语言+redis实现一个中间代理来做排队等待,减少Java应用的内存压力。

    2019-09-01
    1
  • 晓杰
    问答题:在获取购买资格这一步,可以适当加大购买资格的数量

    作者回复: 到了支付界面,我们已经锁定库存了,所以即使增大购买资格,也没法解决这个问题。

    2019-09-01
  • 晓杰
    请问老师,在提交订单的时候加上订单的幂等校验是为了防止同一个用户重复提交订单吗

    作者回复: 对的

    2019-09-01
  • 晓杰
    请问老师,在提交订单时,缓存中库存的查询和扣减是不是应该做成一步操作

    作者回复: 是的

    2019-09-01
    1
  • 陈华应
    1,IP限流
    2,缩短锁库存时间(抢购场景就是抢,应该不会太担心用户只是为了下订单而不付款的情况)
    3,相同用户限制最大购买数量(数据放到缓存中用于抢购期间校验就行,不需要落DB)
    4,期待老师的解决方法

    作者回复: 设置黑名单是一种常用的解决方案

    2019-09-01
    1
  • QQ怪
    1.容忍超卖现象,实际售卖的库存得比网页库存大些,支付成功之后扣去真实库存,就算超卖严重,也可以后期补货或者选择退款结束订单;
    2.尽量缩短用户支付等待时间,加快被锁库存的释放;
    3.抢购活动支持同一用户限购次数。
    2019-08-31
收起评论
22
返回
顶部