HTML5技术

性能优化知多少 - 『圣杰』

字号+ 作者:H5之家 来源:H5之家 2017-07-03 11:02 我要评论( )

性能优化知多少 1. 引言 最近一段时间,系统新版本要发布,在beta客户测试期间,暴露了很多问题,除了一些业务和异常问题外,其他都集中在性能上。有幸接触到这些性能调优的机会,当然要学习总结了。 性能优化是一个老生常谈的问题了,典型的性能问题如页面

性能优化知多少

1. 引言

最近一段时间,系统新版本要发布,在beta客户测试期间,暴露了很多问题,除了一些业务和异常问题外,其他都集中在性能上。有幸接触到这些性能调优的机会,当然要学习总结了。

性能优化是一个老生常谈的问题了,典型的性能问题如页面响应慢、接口超时,服务器负载高、并发数低,数据库频繁死锁等。而造成性能问题又有很多种,比如磁盘I/O、内存、网络、算法、大数据量等等。我们可以大致把性能问题分为四个层次:代码层次、数据库层次、算法层次、架构层次。
所以下面我会结合实际性能优化案例,和大家分享下性能调优的工具、方法和技巧。

2. 先说心态

说到性能问题,你可能首先就想到的是麻烦或者头大,因为一般性能问题都比较紧急,轻则影响客户体验,重则宕机导致财务损失,而且性能问题比较隐蔽,不易发现。因此一时间无从下手,而这时我们就很容易从心底开始去排斥它,不愿接这烫手的山芋。

而恰巧,性能调优是体现程序员水平的一个重要指标。

因为处理bug、崩溃、调优、入侵等突发事件比编程本身更能体现平庸程序员与理想程序员的差距。当面对一个未知的问题时,如何定位复杂条件下的核心问题、如何抽丝剥茧地分析问题的潜在原因、如何排除干扰还原一个最小的可验证场景、如何抓住关键数据验证自己的猜测与实验,都是体现程序员思考力的最好场景。是的,在衡量理想程序员的标准上,思考力比经验更加重要。

所以,若你不甘平庸,请拥抱性能调优的每一个机会。当你拥有一个正确的心态,你所面对的性能问题就已经解决了一半。

3. 再说技巧

拿到一个性能问题,不要忙着先上工具,先了解问题出现的背景,问题的严重程度。然后大致根据自己的经验积累作出预估。比如客户来了个性能问题说系统宕机了,已经造成资金损失了。这种涉及到钱的问题,大家都比较敏感,根据自己的level,决定是否要接这个锅。这不是逃避,而是自知之明。

了解问题背景之后,下一步就来尝试问题重现。如果在测试环境能够重现,那这种问题就很好跟踪分析。如果问题不能稳定重现或仅能在生产环境重现,那问题就相对比较棘手,这时要立刻收集现场证据,包括但不限于抓dump、收集应用程序以及系统日志、关注CPU内存情况、数据库备份等等,之后不妨再尝试重现,比如恢复客户数据库到测试环境重现。

不管问题能否重现,下一步,我们就要大致对问题进行分类,是代码层次的业务逻辑问题还是数据库层次的操作耗时问题,又或是系统架构的吞吐量问题。那如何确定呢?而我倾向于先从数据库动手。我的习惯做法是,使用数据库监控工具,先跟踪下Sql耗时情况。如果监控到耗时较长的SQL语句,那基本上就是数据库层次的问题,否则就是代码层次。若为代码层次,再研究完代码后,再细化为算法或架构层次问题。

确定问题种类后,是时候上工具来精准定位问题点了:

  • Sql耗时问题,推荐使用免费的Plan Explorer分析执行计划。
  • 代码问题定位,优先推荐使用VS自带的Performance Analysis,其次是RedGate的性能分析套件.NET Developer Bundle;然后还有Jet Brains的dotTrace -- .NET performance profiler,dotMemory-- .NET memory profiler;再然后就是反人类的Windbg;等等。
  • 精准定位问题点后,就是着手优化了。相信到这一步,就是优化策略的选择了,这里就不展开了。

    优化后,最后当然要进行测试了,毕竟优化了多少,我们也要做到心里有谱才行。

    以上啰啰嗦嗦有点多,下面我们直接上案例。

    4. 案例分享

    下面就分享下我针对代码层面、数据库层面和算法层面的优化案例。

    4.1. SQL优化案例

    案例1:客户反馈某结算报表统计十天内的数据耗时10mins左右。

    由于前几天刚学会用RedGate的分析工具,拿到这个问题,本地尝试重现后,就直接想使用工具分析。然而,这工具在使用webdev模式起站点时,总是报错,而当时时一根筋,老是想解决这个工具的报错问题。结果,白白搞了半天也没搞定。最后不得已放弃工具,转而选择使用sql server profiler去监控sql语句耗时。一跟踪不要紧,问题就直接暴露了,整个全屏的重复sql语句,如下图。

    Sql Profiler监控结果

    这下问题就很明显了,八成是代码在循环拼接sql执行语句。根据抓取到sql关键字往代码中去搜索,果然如此。

    #region更新三张表数据结合的中间临时表数据,有上游单据的直接调拨单分多次下推时,只计算一次的调拨数量和价税合计 string sSql = string.Format(@ "SELECT FENTRYID FROM {0} GROUP BY FENTRYID HAVING COUNT(FENTRYID) > 1", sJoinDataTempTable); using(IDataReader reader = DBUtils.ExecuteReader(this.Context, sSql)) { while (reader.Read()) { sbSql.AppendFormat(@" UPDATE {0} SET FDIRECTQTY = 0,FALLAMOUNT = 0 WHERE FSEQ NOT IN ( SELECT TOP 1 FSEQ FROM {0} WHERE FENTRYID = {1}) AND FENTRYID = ({1});" , sJoinDataTempTable, Convert.ToInt32(reader["FENTRYID"])); listSqlObj.Add(new SqlObject(sbSql.ToString(), new List < SqlParam > ())); sbSql.Clear(); } } #endregion

    看到这段代码,咱先不评判这段代码的优劣,因为毕竟代码注释清晰,省了我们理清业务的功夫。这段sql主要是想做去重处理,很显然选用了错误的方案。改后代码如下:

    string sqlMerge = string.Format(@" merge into {0} t1 using( select min(Fseq) fseq,Fentryid from {0} t2 group by fentryid ) t3 on (t1.fentryid = t3.fentryid and t1.fseq <> t3.fseq) when matched then update set t1.FDIRECTQTY = 0, t1.FALLAMOUNT = 0 ", sJoinDataTempTable); listSqlObj.Add(new SqlObject(sqlMerge, new List < SqlParam > ())); sbSql.Clear();

    改后测试相同数据量,耗时由10mins降到10s左右。

    4.2. 代码优化案例

    案例2:客户反馈销售订单100条分录行,保存进行可发量校验时,耗时7mins左右。

     

    1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

    相关文章
    • 轻量级高性能ORM框架:Dapper高级玩法 - 云中客

      轻量级高性能ORM框架:Dapper高级玩法 - 云中客

      2017-07-02 13:02

    • 前端性能优化 - Hero^

      前端性能优化 - Hero^

      2017-06-26 11:01

    • 高性能迷你React框架anu在低版本IE的实践 - 司徒正美

      高性能迷你React框架anu在低版本IE的实践 - 司徒正美

      2017-06-14 09:01

    • 事件总线(Event Bus)知多少 - 『圣杰』

      事件总线(Event Bus)知多少 - 『圣杰』

      2017-06-09 14:01

    网友点评
    r