自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

性能優(yōu)化知多少

移動(dòng)開發(fā) 開發(fā)
性能優(yōu)化是一個(gè)老生常談的問題了,典型的性能問題如頁面響應(yīng)慢、接口超時(shí),服務(wù)器負(fù)載高、并發(fā)數(shù)低,數(shù)據(jù)庫頻繁死鎖等。而造成性能問題又有很多種,比如磁盤I/O、內(nèi)存、網(wǎng)絡(luò)、算法、大數(shù)據(jù)量等等。我們可以大致把性能問題分為四個(gè)層次:代碼層次、數(shù)據(jù)庫層次、算法層次、架構(gòu)層次。

 

[[196882]]
1. 引言

最近一段時(shí)間,系統(tǒng)新版本要發(fā)布,在beta客戶測(cè)試期間,暴露了很多問題,除了一些業(yè)務(wù)和異常問題外,其他都集中在性能上。有幸接觸到這些性能調(diào)優(yōu)的機(jī)會(huì),當(dāng)然要學(xué)習(xí)總結(jié)了。

性能優(yōu)化是一個(gè)老生常談的問題了,典型的性能問題如頁面響應(yīng)慢、接口超時(shí),服務(wù)器負(fù)載高、并發(fā)數(shù)低,數(shù)據(jù)庫頻繁死鎖等。而造成性能問題又有很多種,比如磁盤I/O、內(nèi)存、網(wǎng)絡(luò)、算法、大數(shù)據(jù)量等等。我們可以大致把性能問題分為四個(gè)層次:代碼層次、數(shù)據(jù)庫層次、算法層次、架構(gòu)層次。
所以下面我會(huì)結(jié)合實(shí)際性能優(yōu)化案例,和大家分享下性能調(diào)優(yōu)的工具、方法和技巧。

2. 先說心態(tài)

說到性能問題,你可能首先就想到的是麻煩或者頭大,因?yàn)橐话阈阅軉栴}都比較緊急,輕則影響客戶體驗(yàn),重則宕機(jī)導(dǎo)致財(cái)務(wù)損失,而且性能問題比較隱蔽,不易發(fā)現(xiàn)。因此一時(shí)間無從下手,而這時(shí)我們就很容易從心底開始去排斥它,不愿接這燙手的山芋。

而恰巧,性能調(diào)優(yōu)是體現(xiàn)程序員水平的一個(gè)重要指標(biāo)。

因?yàn)樘幚韇ug、崩潰、調(diào)優(yōu)、入侵等突發(fā)事件比編程本身更能體現(xiàn)平庸程序員與理想程序員的差距。當(dāng)面對(duì)一個(gè)未知的問題時(shí),如何定位復(fù)雜條件下的核心問題、如何抽絲剝繭地分析問題的潛在原因、如何排除干擾還原一個(gè)最小的可驗(yàn)證場(chǎng)景、如何抓住關(guān)鍵數(shù)據(jù)驗(yàn)證自己的猜測(cè)與實(shí)驗(yàn),都是體現(xiàn)程序員思考力的最好場(chǎng)景。是的,在衡量理想程序員的標(biāo)準(zhǔn)上,思考力比經(jīng)驗(yàn)更加重要。

所以,若你不甘平庸,請(qǐng)擁抱性能調(diào)優(yōu)的每一個(gè)機(jī)會(huì)。當(dāng)你擁有一個(gè)正確的心態(tài),你所面對(duì)的性能問題就已經(jīng)解決了一半。

3. 再說技巧

拿到一個(gè)性能問題,不要忙著先上工具,先了解問題出現(xiàn)的背景,問題的嚴(yán)重程度。然后大致根據(jù)自己的經(jīng)驗(yàn)積累作出預(yù)估。比如客戶來了個(gè)性能問題說系統(tǒng)宕機(jī)了,已經(jīng)造成資金損失了。這種涉及到錢的問題,大家都比較敏感,根據(jù)自己的level,決定是否要接這個(gè)鍋。這不是逃避,而是自知之明。

了解問題背景之后,下一步就來嘗試問題重現(xiàn)。如果在測(cè)試環(huán)境能夠重現(xiàn),那這種問題就很好跟蹤分析。如果問題不能穩(wěn)定重現(xiàn)或僅能在生產(chǎn)環(huán)境重現(xiàn),那問題就相對(duì)比較棘手,這時(shí)要立刻收集現(xiàn)場(chǎng)證據(jù),包括但不限于抓dump、收集應(yīng)用程序以及系統(tǒng)日志、關(guān)注CPU內(nèi)存情況、數(shù)據(jù)庫備份等等,之后不妨再嘗試重現(xiàn),比如恢復(fù)客戶數(shù)據(jù)庫到測(cè)試環(huán)境重現(xiàn)。

不管問題能否重現(xiàn),下一步,我們就要大致對(duì)問題進(jìn)行分類,是代碼層次的業(yè)務(wù)邏輯問題還是數(shù)據(jù)庫層次的操作耗時(shí)問題,又或是系統(tǒng)架構(gòu)的吞吐量問題。那如何確定呢?而我傾向于先從數(shù)據(jù)庫動(dòng)手。我的習(xí)慣做法是,使用數(shù)據(jù)庫監(jiān)控工具,先跟蹤下Sql耗時(shí)情況。如果監(jiān)控到耗時(shí)較長(zhǎng)的SQL語句,那基本上就是數(shù)據(jù)庫層次的問題,否則就是代碼層次。若為代碼層次,再研究完代碼后,再細(xì)化為算法或架構(gòu)層次問題。

確定問題種類后,是時(shí)候上工具來精準(zhǔn)定位問題點(diǎn)了:

  • Sql耗時(shí)問題,推薦使用免費(fèi)的Plan Explorer分析執(zhí)行計(jì)劃。
  • 代碼問題定位,優(yōu)先推薦使用VS自帶的Performance Analysis,其次是RedGate的性能分析套件.NET Developer Bundle;然后還有Jet Brains的dotTrace -- .NET performance profiler,dotMemory-- .NET memory profiler;再然后就是反人類的Windbg;等等。

精準(zhǔn)定位問題點(diǎn)后,就是著手優(yōu)化了。相信到這一步,就是優(yōu)化策略的選擇了,這里就不展開了。

優(yōu)化后,最后當(dāng)然要進(jìn)行測(cè)試了,畢竟優(yōu)化了多少,我們也要做到心里有譜才行。

以上啰啰嗦嗦有點(diǎn)多,下面我們直接上案例。

4. 案例分享

下面就分享下我針對(duì)代碼層面、數(shù)據(jù)庫層面和算法層面的優(yōu)化案例。

4.1. SQL優(yōu)化案例

案例1:客戶反饋某結(jié)算報(bào)表統(tǒng)計(jì)十天內(nèi)的數(shù)據(jù)耗時(shí)10mins左右。

由于前幾天剛學(xué)會(huì)用RedGate的分析工具,拿到這個(gè)問題,本地嘗試重現(xiàn)后,就直接想使用工具分析。然而,這工具在使用webdev模式起站點(diǎn)時(shí),總是報(bào)錯(cuò),而當(dāng)時(shí)時(shí)一根筋,老是想解決這個(gè)工具的報(bào)錯(cuò)問題。結(jié)果,白白搞了半天也沒搞定。最后不得已放棄工具,轉(zhuǎn)而選擇使用sql server profiler去監(jiān)控sql語句耗時(shí)。一跟蹤不要緊,問題就直接暴露了,整個(gè)全屏的重復(fù)sql語句,如下圖。

這下問題就很明顯了,八成是代碼在循環(huán)拼接sql執(zhí)行語句。根據(jù)抓取到sql關(guān)鍵字往代碼中去搜索,果然如此。

#region更新三張表數(shù)據(jù)結(jié)合的中間臨時(shí)表數(shù)據(jù),有上游單據(jù)的直接調(diào)撥單分多次下推時(shí),只計(jì)算一次的調(diào)撥數(shù)量和價(jià)稅合計(jì)
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

看到這段代碼,咱先不評(píng)判這段代碼的優(yōu)劣,因?yàn)楫吘勾a注釋清晰,省了我們理清業(yè)務(wù)的功夫。這段sql主要是想做去重處理,很顯然選用了錯(cuò)誤的方案。改后代碼如下:

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();

改后測(cè)試相同數(shù)據(jù)量,耗時(shí)由10mins降到10s左右。

4.2. 代碼優(yōu)化案例

案例2:客戶反饋銷售訂單100條分錄行,保存進(jìn)行可發(fā)量校驗(yàn)時(shí),耗時(shí)7mins左右。

拿到這個(gè)問題后,本地重現(xiàn)后,監(jiān)控sql耗時(shí)沒有異常,那就著重分析代碼了。因?yàn)榭砂l(fā)量校驗(yàn)的業(yè)務(wù)邏輯極其復(fù)雜,又加上又直接再一個(gè)類文件實(shí)現(xiàn)該功能,3500+行的代碼,加上零星注釋,真是讓人避之不及。逃避不是辦法,還是上工具分析一把。
這次我選用的時(shí)VS自帶的Performance Profiler,開發(fā)環(huán)境下極其強(qiáng)大的性能調(diào)優(yōu)工具。針對(duì)我們當(dāng)前案例,我們僅需要跟蹤指定服務(wù)對(duì)應(yīng)的dll即可,使用步驟如下:

  1. Analyze-->Profiler-->New Performance Session
  2. 打開Performance Explorer
  3. 找到新添加的Performance Session,右鍵Targets,然后選擇Add Target Binary,添加要跟蹤的dll文件即可
  4. 將應(yīng)用跑起來
  5. 選中Performance Session,右鍵Attach對(duì)應(yīng)進(jìn)程即可跟蹤分析性能了
  6. 在跟蹤過程中,可隨時(shí)暫停跟蹤和停止跟蹤

跟蹤結(jié)束后本案例跟蹤到的采樣結(jié)果如下圖:

同時(shí)Performance Profiler也給出了問題的建議,如下圖:

其中第1、4條大致說明程序I/O消耗大,第一代的GC上存在未及時(shí)釋放的垃圾占比過高。而根據(jù)上圖的采樣結(jié)果,我們可以直接看出是由于再代碼中頻繁操作DataTable引起的性能瓶頸。走讀代碼發(fā)現(xiàn)的確如此,所有的數(shù)量統(tǒng)計(jì)都是在代碼中循環(huán)遍歷DataTable進(jìn)行處理的。而最終的優(yōu)化策略,就相當(dāng)于一次大的重構(gòu),將所有代碼中通過遍歷DataTable的計(jì)算邏輯全部挪到SQL中去做。由于代碼過多,就不再放出。

案例3:客戶反饋批量引入1000張訂單,耗時(shí)40mins左右,且容易中斷。

同樣,我們還是先嘗試本地重寫。經(jīng)測(cè)試批量引入101張單據(jù),就耗時(shí)5mins左右。下一步打開Sql監(jiān)控工具也未發(fā)現(xiàn)耗時(shí)語句。但考慮到是批量導(dǎo)入操作,雖然單個(gè)耗時(shí)不多,但乘以100這個(gè)基數(shù),就明顯了。下面我們就使用RedGate的Ants Performance Profiler跟蹤一下。

該工具比較直觀,可以同時(shí)監(jiān)控代碼和SQL執(zhí)行情況。第一步,New Profiler Session,第二步進(jìn)行設(shè)置,如下圖。根據(jù)自己的應(yīng)用程序類別,選擇相應(yīng)的跟蹤方式。

針對(duì)這個(gè)問題,我們跟蹤到的調(diào)用堆棧和SQL耗時(shí)結(jié)果如下圖:

首先從調(diào)用堆棧中的Hit Count,我們可以首先看出它是一個(gè)批量過程,因?yàn)槿肟诤瘮?shù)僅調(diào)用一次;第二個(gè)我們可以代碼中是循環(huán)處理每一個(gè)單據(jù),因?yàn)镠it Count與我們批量引入的單據(jù)數(shù)量相符;第三個(gè),突然來了個(gè)10201,如果有一定的數(shù)字敏感性的話,這次性能問題的原因就被你找到了。這里就不賣關(guān)子了,101 x 101 = 10201。
是不是明白了什么,存在循環(huán)嵌套循環(huán)的情況。我們走讀代碼確定一下:

//Save.cs
public override void EndOperationTransaction(EndOperationTransactionArgs e) {
    //省略其他代碼
    foreach(DynamicObject dyItem in e.DataEntitys) {
        //反寫收款單
        WriteBackReceiveBill wb = new WriteBackReceiveBill();
        wb.WriteBackForSave(e, this.Context);
    }
}

//WriteBackReceiveBill .cs
public void WriteBackForSave(EndOperationTransactionArgs e, Context contx) {
    //省略其他代碼:
    foreach(DynamicObject item in e.DataEntitys) {
        //do something 
    }
}

好嘛,外層套了一個(gè)空循環(huán)卻什么也沒做。修改就很簡(jiǎn)單了,刪除無效外層循環(huán)即可。

4.3. 算法優(yōu)化案例

案例4:某全流程跟蹤報(bào)表超時(shí)。

這個(gè)報(bào)表是用來跟蹤所有單據(jù)從下單到出庫的業(yè)務(wù)流程數(shù)據(jù)流轉(zhuǎn)情況。而所有的流程數(shù)據(jù)都是按照樹形結(jié)果存儲(chǔ)在數(shù)據(jù)庫表中的,類似這樣:

圖中的流程為:
銷售合同-->銷售訂單-->發(fā)貨通知單-->銷售出庫單

為了構(gòu)造流程圖,之前的處理方法是把流程數(shù)據(jù)取回來,通過代碼構(gòu)造流程圖。這也就是性能差的原因。

而針對(duì)這種情況,就是考驗(yàn)我們平時(shí)經(jīng)驗(yàn)積累了。對(duì)于樹形結(jié)構(gòu)的表,我們也是可以通過SQL來進(jìn)行直接查詢的,這就要用到了SQL Server的CTE語法來進(jìn)行遞歸查詢。

5.總結(jié)

性能調(diào)優(yōu)是一個(gè)循序漸進(jìn)的過程,不可能一蹴而就,重在平時(shí)的點(diǎn)滴積累。關(guān)于工具的選擇和使用,本文并未展開,也希望讀者也不要糾結(jié)與此。當(dāng)你真正想解決一個(gè)問題的時(shí)候,相信工具的使用是難不住你的。

最后就大致總結(jié)下我的調(diào)優(yōu)思路:

  1. 調(diào)整心態(tài),積極應(yīng)對(duì)
  2. 了解性能背景, 收集證據(jù), 嘗試重現(xiàn)
  3. 問題分類,先監(jiān)控SQL耗時(shí),大致確定是SQL或是代碼層次原因
  4. 使用性能分析工具,確定問題點(diǎn)
  5. 調(diào)優(yōu)測(cè)試 
本文轉(zhuǎn)自博客園,版權(quán)歸作者和博客園共有。 作者:圣杰

 

責(zé)任編輯:張子龍 來源: 博客園
相關(guān)推薦

2012-06-28 13:44:55

測(cè)試性能測(cè)試并發(fā)

2012-06-26 09:20:49

性能測(cè)試

2024-08-06 10:07:15

2012-02-13 22:50:59

集群高可用

2013-03-21 11:20:00

性能測(cè)試性能調(diào)優(yōu)測(cè)試

2010-08-16 09:15:57

2013-12-23 14:00:31

Windows 8.2Windows 8.1

2021-12-04 11:17:32

Javascript繼承編程

2025-04-14 08:50:00

Google ADK人工智能AI

2010-09-29 09:28:04

DHCP工作原理

2022-01-06 16:20:04

Java排序算法排序

2018-12-12 15:01:22

開源存儲(chǔ) 軟件

2024-07-01 12:30:09

2013-08-02 09:42:37

BYODBYOC云存儲(chǔ)

2021-12-09 06:41:56

Python協(xié)程多并發(fā)

2022-05-08 18:02:11

tunnel隧道云原生

2009-03-06 19:19:55

2009-05-13 17:31:06

DBAOracleIT

2018-08-31 10:53:25

MySQL存儲(chǔ)引擎

2020-09-08 10:56:55

Java多線程存儲(chǔ)器
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)