作者|张金城性能优化是一个永恒的话题,它伴随着业务的迭代、产品的一步步进化,伴随着企业一步步成长又衰退,是我们面临的无法回避的问题。就像宇宙的增量法则一样,一切都在走向混乱和无序,随着企业的发展壮大、业务的扩大、人员的流动,业绩的恶化最终会导致不可控的一步。,以及复杂性的增加。我们无法消灭吸血鬼。性能优化没有灵丹妙药,但不代表性能优化没有共性。本文主要围绕作者做过的一些性能优化案例,试图总结解决性能问题的常用方法,以及如何继续有意识地避免增量过快。首先,我们把性能优化分为两种情况。一是企业发展阶段稳定期造成的性能瓶颈,二是企业发展临界点造成的性能瓶颈。知道第二条曲线原理的同学可以试着对应第二条曲线,一种是曲线内的性能优化,一种是跨曲线的性能优化。理论来自CharlesHandy《第二曲线:跨越“S型曲线”的二次增长》比如著名的C10K问题,淘宝架构近10年的演进基本属于第二种情况。在这种情况下,很难通过业务代码优化或简单的架构调整来解决性能问题。问题,这种情况下的性能优化一般是在算法&理论的突破或者架构哲学&语言的调整层面。我们无法在一条曲线上实现性能突破。我们可以看到曲线后期的收益越来越小。我们必须跳到一条新曲线。这也是很多大公司关注架构演进的原因。第一条曲线和第二条曲线重叠的部分是企业高层管理人员做出重要决策的机会。纵观淘宝的架构演进,显然符合第二曲线的原理。对于这种像换血一样的性能优化,在评估的时候,需要结合现有流量和指标的分析,给出一个强有力的、合理的数学模型,来验证当前的架构模型是否能够承受未来业务的快速发展。预判需要对市场有前瞻性和准确的估计。一旦发现数学模型证明架构模型无法承载更多业务增长,需要果断迁移到第二条曲线,企业的前瞻性和战略性在这个阶段得到充分体现。我们很多性能优化更多的是接触到第一种情况。我们需要在不破坏现有架构的情况下进行性能调优。我们在这个场景中继续总结:环境优化所谓环境优化,就是指对代码执行环境的优化。正如你的工作环境影响你的工作效率一样,程序的运行环境对性能也有很大的影响。比如网卡中断和CPU的亲缘关系。在Linux网络调优方面,如果发现网络流量上不去,那么有一个方面需要检查:网卡中断处理网络请求是否绑定到单个CPU(或者处理网络请求的同一个CPU)其他中断)。这是典型的运行环境对性能的影响。您的服务会因为网卡中断而导致性能急剧下降。当然,环境的优化需要经验。如果没有经验的话,定位问题会比较困难,但是一些基本的Linux优化知识还是很有必要的。你需要学会看各种指标,有足够的敏感度去发现异常指标,有足够的触发因素去凭经验识别异常指标。轮子的优化和选择很多库都提供了非常方便的功能,但是在某些情况下这些方便的功能对性能并不是很友好。准确的说,很多轮子是开发的黑匣子。就算有源码,也很少有人一行行去研究。很多性能问题往往是在一个简单的调用中暴露出来的。先说简单性吧。众所周知,HashTable和ConcurrentHashMap都是并发和安全的组件,只是在性能上有很大的区别。很明显,ConcurrentHashMap的性能会比HashTable更好。同理,ArrayBlockingQueue是JDK提供的同步阻塞队列。这个组件在很多场景下都会用到,但是在追求极致性能的时候,Disruptor是更好的选择。对于大量定时任务的调用,Netty的时间轮算法是更好的选择。轮子的性能选择可以遵循以下原则:无锁设计一般优于锁设计,细粒度锁优于粗粒度锁,环形队列设计一般优于无界队列设计。恶循环一般坏代码出现在循环中,比如几百个REST请求,几千个SQL请求,手动大海捞针,尤其是深调用栈很难找,这里就需要借助工具了,说说单独说一下,无论是基于语法树还是字节码静态检测还是持续性能集成都可以在一定程度上防止这种情况的发生。经验告诉我,这是优化结果的大头,项目越复杂,问题越严重,解决办法是做批处理,谨慎使用缓存。在这里,性能可视化和调用链分析可以帮助您快速定位问题。加锁可以说是性能优化的难点。一种锁会涉及业务。优化的重点是如何合理使用锁。行锁的使用有规范吗?锁的粒度够细吗?锁的集中管理是可能的。限制开发者直接使用锁。另一类锁一般在中间件中,属于通用组件,与业务关系不大。但如果瓶颈在中间件,就得着手优化了。最好的办法是实现无锁模型。缓存缓存可以解决一些性能问题。在某些情况下,它是杀手武器。但是缓存需要注意时效性和有效范围。控制好这两点一般会给缓存带来很大的好处。线程池线程池太大对性能也有一定的影响。毕竟JAVA线程是1:1的内核线程。解决方法是设置一个合适的线程池大小,不要太大。线程上下文切换的开销不小,还是干脆用阿里的JDK开启全局虚拟线程模式(黑科技)。同步同步一般会阻塞线程,需要大量的线程池。异步太难写了,协程JAVA不支持。可以的话用阿里的JDK。谨慎使用Hibernate。为什么单独说Hibernate呢?可能作者有条件反射吧。一般使用Hibernate的项目都或多或少对它的用法有误解,或者完全沉迷于它带来的便利而忽略了这些便利带来的好处。性能问题。项目中经常会遇到简单的N+1问题,复杂的级联更新问题导致的性能问题也很常见。总之,大家在使用Hibernate的时候要慎重。频繁的FullGC导致的GC性能问题也很常见。这个有点大,可以说好几天几夜,这里就不细说了。还有一些很奇妙的优化点,比如cacheline失效,一般业务不涉及,是中间件基础组件才会遇到的优化策略。业务优化,业务优化往往会取得非常喜人的结果,但这是一个权衡的问题,涉及到业务,所以要慎重。一般来说,性能优化专项工作中尽量不要修改业务。以上只是一些性能优化的经验,其中很多很难综合总结,但是有很多效果显着的优化项是可以通过模式来解决的,那么如何发现代码中的性能问题,并快速识别出那些不是对性能友好代码呢?请听下回分解。下一篇:《????性能优化那些事儿(二)》
