
性能和效率132、提升Java性能的基本方法133、若非必要不要克隆对象134、推荐使用“望闻问切”的方式诊断性能135、必须定义性能衡量标准136、枪打出头鸟—解决首要系统性能问题137、调整JVM参数以提升性能138、性能是个大“咕咚”132、提升Java性能的基本方法Java从诞生之日起就被质疑字节码在JVM中运行是否会比机器码直接运行的效率会低很多很多技术高手、权威网站都有类似的测试和争论从而来表明Java比C或C更快或效率相同。此类话题我们暂且不表这类问题的争论没完没了也许等到我们退休的时候还想找个活动脑筋的方式此类问题就会是最好的选择我们先从如何提高Java的性能方面入手看看怎么做才能让Java程序跑得更快效率更高吞吐量更大。1不要在循环条件中计算如果在循环如for循环、while循环条件中计算则每循环一遍就要计算一次这会降低系统效率就比如这样的代码//每次循环都要计算count2while(icount2){//Do Something}应该替换为//只计算一遍inttotalcount 2;while(itotal){//Do Something}2尽可能把变量、方法声明为final static类型假设要将阿拉伯数字转换为中文数字其定义如下publicStringtoChineseNum(intnum){//中文数字String[]cns{零,壹,贰,叁,肆,伍,陆,柒,捌,玖};returncns[num];}每次调用该方法时都会重新生成一个cns数组注意该数组不会改变属于不变数组在这种情况下把它声明为类变量并且加上final static修饰会更合适在类加载后就生成了该数组每次方法调用则不再重新生成数组对象了这有助于提高系统性能代码如下。//声明为类变量finalstaticString[]cns{零,壹,贰,叁,肆,伍,陆,柒,捌,玖};publicStringtoChineseNum(intnum){returncns[num];}3缩小变量的作用范围关于变量能定义在方法内的就定义在方法内能定义在一个循环体内的就定义在循环体内能放置在一个try……catch块内的就放置在该块内其目的是加快GC的回收。4频繁字符串操作使用StringBuilder或StringBuffer虽然String的联接操作“”号已经做了很多优化但在大量的追加操作上StringBuilder或StringBuffer还是比“”号的性能好很多例如这样的代码StringstrLog file is ready......;for(inti0;imax;i){//此处生成三个对象strlog i;}应该修改为StringBuildersbnewStringBuilder(20000);sb.append(Log file is ready......);for(inti0;imax;i){sb.append(log i);}Stringlogsb.toString();5使用非线性检索如果在ArrayList中存储了大量的数据使用indexOf查找元素会比java.utils. Collections. binarySearch的效率低很多原因是binarySearch是二分搜索法而indexOf使用的是逐个元素比对的方法。这里要注意使用binarySearch搜索时元素必须进行排序否则准确性就不可靠了。6覆写Exception的fillInStackTrace方法我们在前面提到fillInStackTrace方法是用来记录异常时的栈信息的这是非常耗时的动作如果我们在开发时不需要关注栈信息则可以覆盖之如下覆盖fillInStackTrace的自定义异常会使性能提升10倍以上classMyExceptionextendsException{publicThrowablefillInStackTrace(){returnthis;}}7不建立冗余对象不需要建立的对象就不能建立说起来很容易要完全遵循此规则难度就很大了我们经常就会无意地创建冗余对象例如这样一段代码publicvoiddoSomething(){//异常信息StringexceptionMsg我出现异常了快来就救我;try{Thread.sleep(10);}catch(Exceptione){//转换为自定义运行期异常thrownewMyException(e,exceptionMsg);}}注意看变量exceptionMsg这个字符串变量在什么时候会被用到只有在抛出异常时它才有用武之地那它是什么时候创建的呢只要该方法被调用就创建不管会不会抛出异常。我们知道异常不是我们的主逻辑不是我们代码必须或经常要到达的区域那为了这个不经常出现的场景就每次都多定义一个字符串变量合适吗而且还要占用更多的内存所以在catch块中定义exceptionMsg方法才是正道需要的时候才创建对象。我们知道运行一段程序需要三种资源CPU、内存、I/O提升CPU的处理速度可以加快代码的执行速度直接表现就是返回时间缩短了效率提高了内存是Java程序必须考虑的问题在32位的机器上一个JVM最多只能使用2GB的内存而且程序占用的内存越大寻址效率也就越低这也是影响效率的一个因素。I/O是程序展示和存储数据的主要通道如果它很缓慢就会影响正常的显示效果。所以我们在编码时需要从这三个方面入手接口当然了任何程序优化都是从这三方面入手的。Java的基本优化方法非常多这里不再罗列相信读者也有自己的小本本上面所罗列的性能优化方法可能远比这里多但是随着Java的不断升级很多看似很正确的优化策略就逐渐过时了或者说已经失效了这一点还需要读者注意。最基本的优化方法就是自我验证找出最佳的优化途径提高系统性能不可盲目信任。133、若非必要不要克隆对象通过clone方法生成一个对象时就会不再执行构造函数了只是在内存中进行数据块的拷贝此方法看上去似乎应该比new方法的性能好很多但是Java的缔造者们也认识到“二八原则”80%甚至更多的对象是通过new关键字创建出来的所以对new在生成对象分配内存、初始化时做了充分的性能优化事实上一般情况下new生成的对象比clone生成的性能方面要好很多例如这样的代码。privatestaticclassAppleimplementsCloneable{publicObjectclone(){try{returnsuper.clone();}catch(CloneNotSupportedExceptione){thrownewError();}}}publicstaticvoidmain(String[]args){// 循环10万次finalintmaxLoops1010000;intloops0;// 开始时间longstartSystem.nanoTime();// 母对象AppleapplenewApple();while(loopsmaxLoops){apple.clone();}longmidSystem.nanoTime();System.out.println(clone方法生成对象耗时(mid-start) ns);// new生成对象while(--loops0){newApple();}longendSystem.nanoTime();System.out.println(new生成对象耗时(end-mid) ns);}在上面的代码中Apple是一个简单的可拷贝类用两种方式生成了10万个苹果一种是通过克隆技术一种是通过直接种植也就是new关键字按照我们的常识想当然地会认为克隆肯定比new要快但是结果却是这样的clone方法生成对象耗时18731431nsnew生成对象耗时2391924ns不用看具体的数字数数位数就可以了clone方法花费的时间是8位数而new方法是7位数用new生成对象比clone方法快很多原因是Apple的构造函数非常简单而且JVM对new做了大量的性能优化而clone方式只是一个冷僻的生成对象方式并不是主流它主要用于构造函数比较复杂对象属性比较多通过new关键字创建一个对象比较耗时间的时候。注意克隆对象并不比直接生成对象效率高。134、推荐使用“望闻问切”的方式诊断性能“望闻问切”是中医诊断疾病的必经步骤“望”是指观气色“闻”是指听声息“问”是指询问症状“切”是指摸脉象合称“四诊”经过这四个步骤大夫基本上就能确认病症所在然后加以药物调理或能还以病人健康身躯。一个应用系统如果出现性能问题不管是偶发性问题还是持久性问题都是系统“生病”的表现需要工程师去诊断然后对症下药。我们可以把Java的性能诊断也分为此四个过程把我们自己想象成医生吧只是我们的英文名字不叫Doctor而是叫做Trouble Shooter望观察性能问题的症状。有人投诉我们开发出的系统性能慢如蜗牛爬行执行一个操作在等待它返回的过程中用户已经完成了倒水、喝茶、抽烟等一系列消遣活动但系统还是没返回结果其实这是个好现象至少我们能看到症状从而可以对症下药。性能问题从表象上来看可以分为两类不可或很难重现的偶发性问题比如线程阻塞在某种特殊条件下多个线程访问共享资源时会被阻塞但不会形成死锁这种情况很难去重现当用户打电话投诉时我们自己赶到现场症状已经消失了然后1个月内再也没有出现过当我们都认为“磨合”期已过系统已经正常运行的时候又接到了类似的投诉崩溃呀对于这种情况“望”已经不起作用了不要为了看到症状而花费大量的时间和精力可以采用后续提到的“闻问切”方式。可重现的性能问题客户打电话给我们反映系统性能缓慢不需要我们赶到现场自己观察一下生产机就可以发现部分交易缓慢CPU过高可用内存较低等问题在这种情况下我们至少要测试三个有性能问题的交易或者三个与业务相关而技术无关的功能或者与技术有关而业务无关的功能为什么是三个呢因为“永远不要带两块手表”这会致使无法验证和校对。比如三个不同的输入功能都是用户输入信息然后保存到数据库中但是三个交易的性能都非常缓慢通过初步的“望”我们就可以基本确认是与数据库或数据驱动相关的问题若是只有一个交易缓慢其他两个正常那就可以大致定位到一个面该交易的逻辑层出现问题。闻中医上的“闻”是大夫听或嗅患者不自觉发出的声音和气味在性能优化上的“闻”则是关注项目被动产生的信息其中包括项目组的技术能力主要取决于技术经理的技术能力、文化氛围、群体的习惯和习性以及他们专注和擅长的领域等各位读者可能要疑惑了中医上“闻”的对象是病人而为什么这里“闻”的对象却是开发团队呢我们这样来思考该问题如果是一个人个体生病了找大夫如此处理是没有任何问题的但是如果是人类群体生病了那如何追寻这个根源呢假设人是上帝创造的如果有一群外星生物说“人类都有自私的缺陷”那是不是应该去观察一下上帝了解这个缺陷是源于他的习惯性动作还是技能缺乏或者是“文化传承”。对于一个Java应用来说我们就是“上帝”我们创造了他给了他生命能够运行给了他尊严用户需要它给了他灵魂解决了业务问题那一旦他生病是不是应该审视一下我们这些“上帝”呢或者我们得自我反省一下呢如果项目组的技术能力很强有资深的数据库专家有顶尖的架构师也有首席程序员那性能问题产生的根源就应该定位在无意识的代码缺陷上。如果项目组的文化氛围很糟糕组员不交流没有固定的代码规范缺乏整体的架构等那性能问题的根源就可能存在于某个配置上或者相互的接口调用上。如果项目组已经习惯了某一个框架而且也习惯了框架的种种约束那性能的根源就可能是有人越过了框架的协约。需要注意的是“闻”并不是主动地去了解而是由技术人或应用自行挥发出的“味道”需要我们要敏锐地抓住这可能会对性能分析有非常大的帮助。问“问”就是与技术人员缔造者和业务人员使用者一起探讨该问题了解性能问题的历史状况了解“慢”产生的前因后果比如对于业务人员我们可以咨询性能是不是一直这样慢从何时起慢到不能忍受哪一个操作或哪一类操作最慢大概的等待时间是多长用户的操作习惯是什么是喜欢快捷键还是喜欢用鼠标点击在什么时间段最慢业务高峰期是否有滞顿现象业务低谷是否也缓慢其他访问渠道如移动设备是否也有效率问题业务品种和数量有没有激增操作人员是否大规模增加是否在业务上发生过重大事项或重要变更当时的性能如何用户的操作习惯有没有改变或者用户是否自定义了某些功能而对于技术人员我们就要从技术角度来询问性能问题了而且由于技术人员对系统了如指掌可能会“无意识”地回避问题我们应该有技巧地处理这类问题例如可以这样来询问技术人员系统日志是否记录了缓慢信息是否可以回放缓慢交易缓慢时系统的CPU、内存、I/O如何高峰期和低谷时业务并发数量、并发交易种类、连接池的数量、数据的连接数量如何最早接到用户投诉是什么时候是如何处理的优化后如何数据量的增长幅度如何是否有历史数据处理策略系统是否有不稳定的情况是否出现过宕机是否产生过javacore文件最后一次变更是何时变更的内容是哪些变更后是否出现过性能问题操作系统、网络、存储、应用软件等环境是否发生过改变通过与技术人员和业务人员交流我们可以对性能问题有一个整体认识避免“管中窥豹只见一斑”的偏见更加有助于我们分析和定位问题。切“切”是“四诊”的最后一个环节也是最重要的环节这个环节结束我们就要给出定论问题出在什么地方该如何处理等。Java的性能诊断也是类似的“切”就要我们接触真实的系统数据需要去看设计看代码看日志看系统环境然后是思考分析最后给出结论。在这一环节中需要注意两点一是所有的非一手资料如报告、非系统信息都不是100%可信的二是测试环境毕竟是测试环境它只是证明假设的辅助工具并不能证明方法或策略的正确性。曾经遇到过这样一个案例有一个24小时运行的高并发系统从获得的资料上看在出现偶发性的性能故障前系统没有做过任何变更网络也没变更过业务也没有过大的变动业务人员的形容是“一夜之间系统就变慢了”而且该问题在测试机上不能模拟重现。接到任务后马上进行“望闻问”都没有太大的收获。进入到“切”环节时对大量的日志进行跟踪分析调试最终锁定到了加密机上加密机属于多个系统的共享资源当排队加密数据时就有可能出现性能问题最终的解决方案是增加一台加密机于是系统性能恢复正常。性能优化是一个漫长的工作特别是对于偶发性的性能问题不要期望找到“名医”立刻就能见效这是不现实的深入思考寻根探源最终必然能找到根源所在。中医上有一句话“病来如山倒病去如抽丝”系统诊断也应该这样一个过程切忌急躁。注意性能诊断遵循“望闻问切”不可过度急躁。135、必须定义性能衡量标准出现性能问题不可怕可怕的是没有目标用户只是说“我希望它非常快”或者说“和以前一样快”在这种情况下我们就需要把制定性能衡量标准放在首位了原因有两个1性能衡量标准是技术与业务之间的契约“非常快”是一个直观性的描述它不具有衡量的可能性对技术人员来说一个请求在2秒钟之内响应就可以认为是“非常快”了但对业务人员来说“非常快”指的是在0.5秒内看到结果—看出现偏差了。如果我们不解决这种偏差就有可能出现当技术人员认为优化结束的时候而业务人员还认为系统很慢仍然需要提高继续性能于是拒不签收验收文档这就产生商务麻烦了。2性能衡量标志是技术优化的目标性能优化是无底线的性能优化得越厉害带来的副作用也就明显例如代码的可读性差可扩展性降低等比如一个乘法计算我们一般是这样写代码的inti10016;如果我们为了提升系统性能使用左移的方式来计算代码如下inti1004;性能确实提高了但是也带来了副作用比如代码的可读性降低了很多要想让其他人员看明白这个左移是何意就需要加上注释说“把100扩大16倍”这在项目开发中是非常不合适的。因此为了让我们的代码保持优雅减少“坏味道”的产生就需要定义一个优化目标优化到什么地步才算结束。明白了性能标准的重要性就需要在优化前就制定好它一个好的性能衡量标准应该包括以下KPIKeyPerformance Indicators核心业务的响应时间。一个新闻网站的核心业务就是新闻浏览它的衡量标准就是打开一个新闻的时间一个邮件系统的核心业务就是邮件发送和接收速度一个管理型系统的核心就是流程提交这也就是它的衡量标准。重要业务的响应时间。重要业务是指在系统中占据前沿地位的业务但是不会涉及业务数据的功能 例如一个业务系统需要登录后才能操作核心业务这个登录交易就是它的重要交易比如邮件系统的登录。当然性能衡量标准必须在一定的环境下比如网络、操作系统、硬件设备等确定的情况下才会有意义并且还需要限定并发数、资源数如10万数据和1000万的数据响应时间肯定不同等当然很多时候我们并没有必要白纸黑字地签署一份协约我们编写性能衡量标准更多地是为了确定一个目标并尽快达到业务要求而已。136、枪打出头鸟—解决首要系统性能问题在一个系统出现性能问题的时候很少会出现只有一个功能有性能问题一个功能出现性能问题的情况非常容易解决基本上不会花费什么时间系统一旦出现性能问题也就意味着一批的功能都出现了问题在这种情况下我们要做的就是统计出业务人员认为重要而且缓慢的所有功能然后按照重要优先级和响应时间进行排序并找出前三名而这就是我们要找的“准出头鸟”。“准出头鸟”找到了然后再对这三个功能进行综合分析运行“望闻问切”策略找到问题的可能根源然后只修正第一个功能的性能缺陷再来测试检查是否解决了这个问题紧接着是第二个、第三个循环之。可能读者会产生疑问为什么这里只修正第一个缺陷而不是三个一起全部修正这是因为第一个性能缺陷才是我们真正的出头鸟在我做过的性能优化项目中超过80%的只要修正了第一个缺陷其他的性能问题就会自行解决或非常容易解决已经不成为问题了。比如BBS系统从用户登录到用户浏览、发帖都非常缓慢经过逐步筛选确定登录就是“出头鸟”需要着重解决代码如下classLoginextendsHttpServlet{publicvoiddoGet(HttpServletRequestreq,HttpServletResponseresp){//从req中获得用户名和密码StringuserName....;Stringpasswd...;//由登录逻辑处理登录booleanloginSuccessloginBiz.login(user,passwd);if(loginSuccess){//登录成功签到Checker.sign(userName);}else{//登录不成功记录日志log.warn(userName,登录失败!)}}}这是一个早期Web应用的典型验证代码先验证用户是否登录成功然后决定是否向Session中写入信息。在该BBS系统中分析发现login和sign操作都非常耗时那就首先跟踪login代码如下classLoginBiz{publicvoidlogin(String_user,String_password){//根据用户名获得用户对象UseruuserDao.getUserByUser(_user);returnu!nullu.getPasswd().equals(_password);}}追踪到这里发现从数据中取出用户对象的效率很低但是数据库的CPU、内存、I/O都没有问题而且没有到达最大连接数。继续追踪下去终于发现问题了数据库的版本和JDBC的版本不一致虽然在进行所有的连接、执行SQL、断开等操作时都没有出现任何问题但在多表的联合查询中速度非常慢。问题定位了将其替换成数据库匹配的驱动程序登录问题马上得到解决并且其他所有性能慢的问题都解决了归根结底其实都是数据库驱动问题引起的。解决性能问题时不要把所有的问题都摆在眼前这只会“扰乱”你的思维集中精力找到那个“出头鸟”解决它在大部分情况下一批性能问题都会迎刃而解而且我们的用户关注最多的可能就是系统20%的功能可能我们解决了这一部分已经达到了用户的预期目标也就标志着我们的优化工作可以结束了。注意解决性能优化要“单线程”小步前进避免关注点过多而导致精力分散。137、调整JVM参数以提升性能我们写的每一段Java程序都要在JVM中运行如果程序已经优化到了极致但还是觉得性能比较低那JVM的优化就要提到日程上来了。不过由于JVM又是系统运行的容器所以稳定性也是必须考虑的过度的优化可能就会导致系统故障频繁发生致使系统质量大幅下降。下面提供了四个常用的JVM优化手段供你在需要时参考。1调整堆内存大小我们知道在JVM中有两种内存栈内存Stack和堆内存Heap栈内存的特点是空间比较小速度快用来存放对象的引用及程序中的基本类型而堆内存的特点是空间比较大速度慢一般对象都会在这里生成、使用和消亡。栈空间是由线程开辟线程结束栈空间由JVM回收因此它的大小一般不会对性能有太大的影响但是它会影响系统的稳定性在超过栈内存的容量时系统会报StackOverflowError错误。可以通过“java-Xss size”设置栈内存大小来解决此类问题。堆内存的调整不能太随意调整得太小会导致Full GC频繁执行轻则导致系统性能急速下降重则导致系统根本无法使用调整得太大一则是浪费资源当然若设置了最小堆内存则可以避免此问题二则是产生系统不稳定的情况例如在32位的机器上设置超过1.8GB的内存就有可能产生莫名其妙的错误。设置初始化堆内存为1GB也就是最小堆内存最大堆内存为1.5GB可以用如下的参数java-Xmx1536m-Xms1024m2调整堆内存中各分区的比例JVM的堆内存包括三部分新生区Young GenerationSpace、养老区Tenure generation space、永久存储区Permanent Space其中新生成的对象都在新生区它又分为伊甸区Eden Space、幸存0区Survivor 0 Space和幸存1区Survivor 1 Space当在程序中使用了new关键字时首先在伊甸区生成该对象如果伊甸区满了则用垃圾回收器先进行回收然后把剩余的对象移动到幸存区0区或1区可如果幸存区也满了呢垃圾回收器会再回收一次然后再把剩余的对象移动到养老区那要是养老区也满了呢此时就会触发 Full GC这是一个非常危险的动作JVM会停止所有的执行所有系统资源都会让位给垃圾回收器会对所有的对象过滤一遍检查是否有可以回收的对象如果还是没有的话就抛出OutOfMemoryError错误系统不干了清楚了这个原理若还是不清楚请看看《JVMSpecification》那我们就可以思考一下如何提升性能了若扩大新生区势必会减少养老区这就可能产生不稳定的情况一般情况下新生区和养老区的比例为1:3左右设置命令如下java-XX:NewSize32m-XX:MaxNewSize640m-XX:MaxPermSize1280m-XX:NewRatio5该配置指定新生代初始化为32MB也就是新生区最小内存为32M最大不超过640MB养老区最大不超过 1280MB新生区和养老区的比例为1:5。3变更GC的垃圾回收策略Java程序性能的最大障碍就是垃圾回收我们不知道它何时会发生也不知道它会执行多长时间但是我们可以想办法改变它对系统的影响比如启用并行垃圾回收、规定并行回收的线程数量等命令格式如下java-XX:UseParallelGC-XX:ParallelGCThreads20这里启用了并行垃圾收集机制并且定义了20个收集线程默认的收集线程等于CPU的数量这对多CPU的系统是非常有帮助的可以大大减少垃圾回收对系统的影响提高系统性能。当然垃圾回收的策略还有很多属性可以修改比如UseSerialGC启用串行GC默认值、ScavengeBeforeFullGC新生代GC优先于Full GC执行、UseConcMarkSweepGC对老生代采用并发标记交换算法进行GC等这些参数需要在系统中逐步调试。4更换JVM如果所有的JVM优化都不见效那只有使用最后一招了更换JVM目前市面上比较流行的JVM有三个产品JavaHotSpot VM、Oracle JRockit JVM、IBM JVM其中HotSpot是我们经常使用的稳定性、可靠性都不错JRockit则以效率著称性能是它的优势但在决定使用该JVM之前一定要做好全面的系统测试它的某些行为可能会在JRockit上产生BugIBM JVM也比较稳定而且它在AIX系统上的表现要远远好于其他操作系统。JVM的优化不能像程序优化一样找到Bug就可以立刻解决JVM的优化一定是要循序渐进的参数设置不可激进特别是需要优化多个参数时一定要逐步实施确保每个优化步骤都达到了预期目标否则会对整个系统的稳定性产生较大的风险。需要提醒的是以上带有“-XX”的JVM参数可能是不健壮的SUN也不推荐使用可能后续会在没有通知的情况下就不再支持它了但是它又非常好用这需要在系统升级、迁移时慎重考虑。138、性能是个大“咕咚”有一部动画片叫《咕咚来了》其大致剧情是三只小兔在湖边玩耍忽然湖中传来“咕咚”一声这奇怪的声音把小兔们吓了一大跳。小兔们刚想去看个究竟又听到“咕咚”一声这可把小兔们吓坏了“快跑咕咚来了快逃呀”小兔们转身就跑。狐狸正同小鸟跳舞与跑来的兔子碰了个满怀。狐狸一听“咕咚来了”也紧张起来跟着就跑。它们又惊醒了睡觉的小熊和树上的小猴小熊和小猴也不问青红皂白跟着它们跑起来于是一路上跟着跑的动物越来越多大象、河马、老虎、野猪……岸上这阵骚乱让湖中的青蛙感到十分惊讶它拦住了这群吓蒙了的伙伴们问出了什么事大家七嘴八舌地形容“咕咚”是个多么可怕的怪物。青蛙问“谁见到了”大家互相推诿谁也没有亲眼看见于是决定回去看看明白。回到湖边又听见“咕咚”一声仔细一看原来是木瓜掉进水里发出的声音众动物不禁大笑起来。这寓言故事好笑吗很好笑但是要是发生在我们自己身上就不那么好笑了比如说某些Javaer一直在质疑Java系统的性能于是我们自己也跟着怀疑Java的性能—这就是发生在我们身边的真实“咕咚”Java系统的性能问题本就是子虚乌有的事情是我们自己吓唬自己其实我们可以从四个方面分析该问题1没有慢的系统只有不满足业务的系统不管是使用C开发还是Java开发的项目最终都会有一个产品诞生或服务于大众如网站或服务于企业企业级应用谁来决定一个系统的快慢呢不是计算机它只会使用毫秒、纳秒去记录时间但不会做判断它可以计算出一个交易执行了多长时间但它不能决定这个时间是长还是短那谁去判断呢是人准确地说是使用者即使是开发人员自己根据日志记录的时间来判断系统是慢了还是快了那也还是以使用者的身份来判断的对一个系统毫无了解的人员是无法判断出一个系统的快慢的。例如一个做统计的业务人员去看计费系统即使响应需要N秒的时间统计人员也会觉得非常快了那是因为统计系统的结果经常是按照小时、天来计算的。再比如即时通信系统有1秒内的延迟是可以接受的发送者发出消息到接受者接收消息的时间间隔为1秒但是语音通信系统若有1秒的延迟就是不可接受的了发送邮件N分钟后才收到这是可以容忍的但是对于同城银行内转账来说这个时间就是不可容忍的必须在秒级完成。不同的系统所要求的性能不同因此只要一个系统达到业务要求就可以认为它足够快我们不要期望跨系统间的性能对比这是毫无意义的。如果有使用者告诉你“这个系统太慢了”也就是在间接地提醒您系统没有满足业务需求尚待继续努力。2没有慢的系统只有架构不良的系统在做系统架构设计时架构师有没有考虑并行计算有没有考虑云计算技术有没有负载均衡……这些都是解决我们性能问题的良方只要架构设计得当效率就不是问题。即使是架构初期没有考虑扩展性那我们也有一些手段可解决性能问题。比如有一个批处理系统系统建设时的目标是5小时内生成2000万条业务数据可到第3年的时候公司发生了大规模的变化整合了其他同类公司需要处理的数据更多了在5小时内需要生成8000万业务。于是就得考虑架构的扩展了。有一个很简单的处理方案即应用服务器水平扩展增加业务数据源的纵向切割能力均分数据压力这样就可以很轻松地实现大数量的生成。再比如一个新闻网站刚开始上线时访问的人员不多响应都是在毫秒级别的随着访问量的激增响应时间呈阶梯型增加资深会员流失率翻倍跳跃如何解决该问题呢解决方案有两个一是增加IP层的负载均衡或者硬件设备或者软件架构把访问者分配到多个不同的应用服务器上降低单台应用服务器的性能压力二是增强系统的处理能力增大吞吐量比如提升数据源的响应能力划分数据的热度如把数据划分为Hot、Warm、Cold等区域分配不同的硬件资源和服务等级很多时候这两个方案配合起来使用会很快解决性能问题。3没有慢的系统只有懒惰的技术人员这里的技术人员涉及面很大可以是开发人员也可以是维护人员甚至是应用软件的顾问人员如数据库顾问、App Server的顾问等。一个系统出现问题或者是投产前后立刻出现的性能问题或者是运行中突发的性能问题或者是逐渐增长的数据用户或业务数据导致的性能问题只要我们肯用心查找并且拥有适当的资源如源码和支持资源一般都是可以解决的。最可怕的是我们的技术人员对性能问题漠不关心对时间效率不够敏感导致使用者怨声载道三人成虎最终致使此系统成为一个“慢得无法使用的系统”。这也要求我们在开发初期就适当考虑一下性能问题但不要把性能排为头号任务它不是它只是我们的一个关注点而已。4没有慢的系统只有不愿意投入的系统这里的投入指的是资源包括软硬件资源、人员资源及资金资源等这不是项目组能够单独解决的问题但是它会严重影响系统的性能。曾经遇到一个运行超过8年的分析系统从1年前开始只要是高峰期它的速度就会慢下来分析下来发现是因为并发用户超过了许可的数量造成系统阻塞性能缓慢唯一解决的法就是购买更多的许可数量但是8年了一个系统的生命期还能有多少呢—所以最后采用了自由放任的办法让其自行走到寿命的终结点然后建立新的分析系统。当然我们也会碰到查不出原因的性能问题这不可否认毕竟现在的系统越做越大源代码动辄就十万、百万级别让一个人或一个小团队将其彻头彻尾地查清楚也不现实而且性能问题涉及面非常广如操作系统、数据库、网络、存储等要想对这些技术都非常熟悉也很困难但查不出问题并不代表我们解决不了是的这与治疗癌症相似我们现在的科学还不知道它的发病机理不知道为什么会产生癌细胞但我们知道割除病变部位能够避免癌细胞扩散性能问题也一样我们可能不知道问题产生的原因但我们可以有N种手段来解决它。能够解决的问题还算是问题吗而且性能只是衡量系统的一个辅助指标而不是主指标如果您与业务人员交流说“我们可以把系统的响应时间提升到0.001秒内但前提是不实现您提出的需求”您猜业务人员会同意吗—不把我们这些“火星人”撵出门外已经算是客气的了注意对现代化的系统建设来说性能就是一个大“咕咚”—看清它的本质吧。