02 | 如何制定性能调优策略?
02 | 如何制定性能调优策略?
讲述:李良
时长14:56大小13.67M
性能测试攻略
1. 微基准性能测试
2. 宏基准性能测试
1. 热身问题
2. 性能测试结果不稳定
3. 多 JVM 情况下的影响
合理分析结果,制定调优策略
1. 优化代码
2. 优化设计
3. 优化算法
4. 时间换空间
5. 空间换时间
6. 参数调优
兜底策略,确保系统稳定性
什么是兜底策略?
总结
思考题
赞 24
提建议
精选留言(50)
- 何何何何何少侠2019-05-241. 新品上线需要对系统基础功能、尤其是上线涉及改动、有耦合的业务做宏基准测试,如:用户服务、商品服务、订单服务、支付服务、优惠券服务等。从而保证支撑抢购活动的服务正常运行 2. 针对抢购活动,如:秒杀 团购等促销。需要做微基准测试以验证服务是否达到预期。测试过程中需要留意诸如 qps、内存、cpu、网络带宽、线程堆栈等指标是否达标。不仅考虑单机性能,更要拓展到集群时性能的阈值能达到多少从而给出更加准确的性能测试评估报告 3. 多说一句:此外还要考虑服务的质量,要测试出抢购活动的瓶颈在哪儿从而应对即将到来的大促活动,以方便开发、运维团队制定更好的如服务限流、降级、动态伸缩等方案。展开
作者回复: 回答的很全面,赞一个
110 - 木偶笨笨2019-05-23感觉论题有一点过于发散,讲到限流熔断这些内容了,我理解限流熔断实际是架构师的事情,是不是另开一课再讲。这门课focus在调优方法、工具、技巧,以及相关理论比如jvm、多线程原理是不是会更合适。
作者回复: 感谢你的建议。我相信很多同学跟你有一样的想法,那就是赶紧学会使用性能排查工具,性能如何监测分析,如何解决性能问题。 由于不同的性能问题,性能排查以及调优都是不固定的,所以在后面的一些章节中,会有一些结合实际场景来进行性能排查的实战。 在大家了解一些理论性的知识点以及基础之后,也有专门一讲来讲述性能监测工具、调优工具的使用,所以大家保持耐心,切记心急吃不了热豆腐。 在这里我们强调了即使我们性能测试做的再好,兜底策略是一定要做的,兜底也是性能调优的一部分。试想下,我们的性能调优做的再好,系统同样存在极限,当系统达到极限,系统肯定出现性能瓶颈。 在学习成长的过程中,我们切忌将知识点局限于某个层级,或者将自己局限于某一种语言。例如线程池的大小设置,其实也是一种限流的方式,所以限流熔断并不只是局限于架构这块的内容。 我们要做性能调优最重要的目的是什么?在我看来就是为了避免发生线上事故,如果发生线上事故,也是要避免线上大面积事故。所以性能调优做的再好,系统也是存在极限的,兜底策略是系统的保护伞,特别在高并发的系统中,降级/熔断/限流成为保证系统性能稳定性的重要环节。
共 4 条评论48 - 陆离2019-05-24老师你好,最近我司app也是上线运行一段时间之后就一直504了,需要重启一下才能恢复正常。线上的情况比较特殊,获取不到dump信息,local端测试没有发现问题。老师有什么排查方向可以指点一下吗?
作者回复: 你好 陆离,你有没有通过命令提前打开JVM内存异常日志呢,可以在启动tomcat时,配置参数-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/heap/dump,如果遇到内存异常,则会生成dump文件。 504是响应超时,有很多种可能。我建议你先排查应用服务内存是否存在异常,在504时,登录服务器使用top命令查看内存、cpu使用情况,同时查看应用日志是否存在异常日志,排除应用服务的问题; 也有可能是nginx的问题,我们可以查看nginx的日志是否存在异常,如果存在异常,应该调优nginx; 我们可以通过netstat命令查看linux服务器的连接状态,是否存在大量time wait状态的连接。 如果有,需要排除linux服务器的socket最大连接数是否设置合适,太小也容易造成504。
共 2 条评论28 - Hammy2019-06-21老师你好,听了你的课受益匪浅。但是我有一个问题,您在将空间换时间的举例中使用了数据库分表这种当做案例,我个人觉得数据库分表本质上不属于空间换时间的样例。因为单表和多表存储数据的总量本质上是恒定的,之所以能提高性能是因为分表以后,b+tree索引维护的数据量会降低,从而可以减少查询数据的总量以及索引的维护成本。我个人觉得分表这种样例是属于将数据结构进行拆分,减少单个数据结构存储的数据总量从而提升性能,但本质上并没有增加额外的空间。如果自己的理解有问题,希望可以指出。展开
作者回复: 理解的没错,这里用数据冗余来做案例更恰当
共 3 条评论13 - Geek_ebda962019-06-02老师,这句话 这就是热身过程,如果在进行性能测试时,热身时间过长,就会导致第一次访问速度过慢,你就可以考虑先优化,再进行测试。 指的优化是优化JVM的一些参数,还是指优化代码呢?如果是优化代码,热身时间过长,有没有例子能够说明一些,第一次查询数据先放入缓存这个算吗?
作者回复: 可以通过设置CompileThreshold参数降低执行方法次数阈值来提前预热代码,也可以通过调用WarmUpContextListener.invoke方法指定需要预热的方法,当然也可以在启动时提前写个循环或多线程调用该方法。 我们还可以使用一些工具来预热,例如之前有同学提到的JMH。
12 - 业余草2019-05-23总结的很好,期待后面的实战内容!!!10
- etdick2019-05-23老师,现在的微服务架构,一台物理机部署了多个微服务。每个服务相当于一个JVM。如何调优?
作者回复: 你好 etdick。 首先,在做性能测试时,我们应该单独部署测试每个微服务的性能,尽量排除服务之间的干扰,先完成单个服务的性能调优; 其次,模拟线上环境下多个服务部署,根据实际业务来模拟多个服务的高峰值的性能测试,如果服务与服务之间存在性能上的互相干扰,且属于不同的业务,我们应该考虑实际生产环境中,两个业务场景是不是存在相同的峰值期,若是,则需考虑分开不同服务器部署或根据需求进行服务降级。 除此之外,我们还可以设置JVM参数来调优各个JVM的内存分配以及垃圾回收。我们知道两个JVM会互相产生影响的主要原因是对CPU的使用情况,而垃圾回收频率是抢占CPU的主要因素。我们可以调优内存分配降低垃圾回收频率,或设置合适的垃圾回收器。由于不同场景具体的分配调优方式不同,我们将会在之后的内容中讲解到。
9 - SlamDunk2019-06-07如果我们的服务器有多个 Java 应用服务,部署在不同的 Tomcat 下,这就意味着我们的服务器会有多个 JVM。 不同tomcat也可以使用同一个jrm下的同一个jvm呀,为什么这里要说会有多个jvm呢?
作者回复: 一个Tomcat进程代表一个JVM
共 3 条评论8 - 建国2019-05-24老师我又来了,两个问题,1.您在这节中介绍的那么多的知识点在后面的课程中都会逐个讲解到吧 2.有没有nginx调优呢,因为我们给客户部署时发现,用阿里云的SLB和自己搭建的nginx,某个接口响应时间差10+倍
作者回复: 你好 建国,欢迎多提问。我先回答你的第一个问题,前面两讲中,一方面,是让你对性能调优有一个全面的认识: 调优的目的是什么,有没有指标可衡量,如何发现性能问题,发现后,我们有什么策略可以调优; 另一方面,我多次强调了基础知识以及调优的思维方式的重要性。所以接下来我将从基础讲起,再到高级篇,学会高性能编程的同时,总结出一惯的调优思维方式。从中很多章节中会有结合实际场景使用到一些测试工具以及性能调优工具。除了这些,我还会在最后用实战的方式来为你讲解实际业务场景下的调优。 从这个专栏的目录来看,没有专题专门讲nginx的调优,nginx如果只是作为转发,由于nginx是基于事件驱动模型实现的web请求转发,使用异步处理方式来避免阻塞,对性能损耗应该不大。如果用lua脚本做了一些逻辑判断,或者限流等等,这个是有损的,会带来很大的损耗。
7 - 昨夜星辰2019-05-23新上线的系统作宏基础测试,抢购活动作微基本测试7
- kevin2019-05-26使用for遍历LinkedList并不会使性能降低,编译器会编译成使用Iterator; ###java代码### public void forLinkedList() { LinkedList<String> list = new LinkedList(); for (String item : list) { } Iterator<String> iterable = list.iterator(); while (iterable.hasNext()) { String item = iterable.next(); } } ###javap -c xx.class的结果### Code: 0: new #2 // class java/util/LinkedList 3: dup 4: invokespecial #3 // Method java/util/LinkedList."<init>":()V 7: astore_1 8: aload_1 9: invokevirtual #4 // Method java/util/LinkedList.iterator:()Ljava/util/Iterator; 12: astore_2 13: aload_2 14: invokeinterface #5, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z 19: ifeq 35 22: aload_2 23: invokeinterface #6, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object; 28: checkcast #7 // class java/lang/String 31: astore_3 32: goto 13 35: aload_1 36: invokevirtual #4 // Method java/util/LinkedList.iterator:()Ljava/util/Iterator; 39: astore_2 40: aload_2 41: invokeinterface #5, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z 46: ifeq 62 49: aload_2 50: invokeinterface #6, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object; 55: checkcast #7 // class java/lang/String 58: astore_3 59: goto 40 62: return展开
作者回复: 你好kevin,可以将for(:)换成for(;;)试试看效果是不是不一样
共 2 条评论7 - -W.LI-2019-05-24抢购秒杀,感觉架构层面的优化比较多吧,尽量缩短链路,缩短响应时间,没有依赖的服务串行优化为并行。或者本地持久化后保证最终一致性。查询商品详情,下单支付这些接口宏观测试,内部的比较占用系统资源的关键代码(占用IO资源,逻辑复杂消耗CPU资源等)做微测试。还有就是需要做限流兜底,读服务采用合理的缓存策略等。6
- 阿厚2019-06-04多少别人一天没有解决的问题,被我用一部分一部分注释代码,半小时解决了。
作者回复: 如果能用排除法去解决问题,是一个比较好的方式。不过很多线上事故,在线下是无法重现的,这个方式就比较难派上用场了。
共 3 条评论5 - Mr.J2019-05-23老师您好,思考题中,新产品中的抢购活动,针对抢购的商品数量、支付等内容进行微基性能测试,对于商品数量、支付这些比较关键的代码,多线程高并发下商品数量的读写,数据同步,支付的安全等需要精准的测试,而宏基准性能测试更是偏向于整体的业务逻辑,针对整个新产品的整体功能,例如秒杀活动的从开始抢购到成功支付,或者开始抢购到未抢购到商品等流程进行宏基准性能测试,我这样理解对嘛老师
作者回复: 这位同学,你理解的很好。微基准测试我在这里纠正一点,包括进入抢购页面、提交订单、支付调起,再细一些包括排队等待功能、库存扣减的分布式锁功能、幂等性校验等。
5 - ANYI2019-05-23hi,老师,入职新公司,直接派去客户现场调优,有一份压测报工,知道是哪些场景性能有问题,但对于业务不熟,只有一堆代码;该如何快速进入;
作者回复: 你好 ANYI,建议可以先对一个一个小模块进行性能测试和调优。先对一些代码性问题进行优化,例如之前有同学提到的,合并多次请求,减少多次数据库操作,优化sql(优化join以及索引),优化Java基础代码(集合的合理使用,序列化的优化)等等,先完成这些基础性优化。 在这基础之上,我们再去针对一些业务进行优化,例如业务存在高耦合,我们可以解耦业务,使用一些好的设计方法。通过这种方式逐步了解整个系统的业务以及架构。 代码层级优化之后,我们可以考虑调优JVM、容器以及操作系统,我相信代码层的优化可以满足大部分的性能优化需求,其他的性能调优则是满足一些特殊的场景下的高性能需求。
5 - zhangtnty2019-05-24老师好, 我理解文中题目中抢购的不同实现方式是微观调优,综合考虑上线后流量峰值等可为宏观调优。 老师在文中提到的降级和限流是日常关键的一环,老师把它说成兜底,我常理解为"保命" 。也希望老师对于降级和限流可以展开分享一篇。各种调优最终都会有极限。
作者回复: 同学你好,你理解的很到位,兜底就是保命,但高于保命,我们不仅仅需要保证系统不挂掉,还要保证流量范围内的请求是正常的。微基准性能测试可以理解为对某块代码进行测试,包括对不同实现方式的性能测试比较。 后面我会在实战中讲到限流、降级的实现和使用,由于这个属于优化的辅助功能,不做具体实现方式的讲解。如果对相关知识感兴趣,可以留言保持沟通。
4 - CharlesCai2019-05-23期待作者的新内容!朗读者的声音好听又专业!提一个小功能,网页版能不能实现一下标记或做笔记的功能。
编辑回复: 接收成功!谢谢你的建议。
4 - zengxiangcai2019-07-11老师,你好,关于测试我有几个问题 1、一般测试环境服务器个线上服务器配置等可能不大一样,想测试环境搭建和线上一个配置一样代价也有点高,这种情况一般该怎么做呢? 2、测试的数据测试环境个线上可能量级不大一样,这样也必然影响测试结果的吧? 3、像jmeter这种在一台机器模拟多线程去访问服务进行测试,会不会测试机无法模拟那么多线程影响测试效果展开共 1 条评论3
- Eddie2019-05-27老师您好,提两个小问题 1,在对象中,对象的属性在controller层接收,传入对象中,或构造方法传入,这时候字符串是在普通堆区还是在堆区字符串常量池 2,还有1.8之后常量池位置从方法区转移到堆String常量池了,这样做的好处是什么呢
作者回复: 你好 Eddie,我理解的是,在运行时创建的字符串对象都会在堆中创建,且不会在常量池中创建了,常量池中只是在编译加载类时构造,当然在调用intern方法时也会在常量池中创建对象。 如果传入的是int char等类型的,只会在虚拟机栈中存储,而对象类型的,需要在堆中创建,栈中存储的是引用。 第二个问题,常量池在1.6时很容易发生内存溢出异常,移到堆中之后,常量池大小增加了,也能更好的使用常量池来优化字符串对象。
2 - 进步慢是一种罪2019-05-23抢购活动(秒杀)作为微基准测试,商品详情页浏览,支付,支付后的通知等做宏基准测试。2