標籤:
經過幾個月的忙碌,我廠最近的電商平台項目終於上線,期間遇到的問題以及解決方案,也可以拿來和大家多做交流了。
我廠的項目大多採用C#.net,使用逐漸發展併流行起來的EF(Entity Framework)架構,並搭配使用丹麥的一款主打CMS, DMS的.net web應用程式sitecore。
本篇為基礎篇,側重於闡述編碼規範和一些編碼技巧對系統效能的影響。不規範的編碼方式,可能對單個方法或模組產生的效能影響是微不足道的,但在大型電商項目中,高並發的情境隨處可見,欠妥的編碼方式,可能會對整個系統的效能及使用者體驗,造成很大的影響。
作為電商項目,對效能影響最明顯的模組,莫過於下訂單及修改庫存。在高並發情境下,這些模組的資料庫存取的效能要求是非常高的,少許的效能浪費,都可能使系統在使用中的表現差強人意。
首先,我們來闡述一下EF架構下得編碼規範。這裡我們可以參考msdn關於IQueryable<T>及IEnumerable<T>的介紹:
對於在記憶體中集合上啟動並執行方法(即擴充 IEnumerable< T> 的那些方法),返回的可枚舉對象將捕獲傳遞到方法的參數。在枚舉該對象時,將使用查詢運算子的邏輯,並返回查詢結果。
與之相反,擴充 IQueryable <T> 的方法不會實現任何查詢行為,但會產生一個表示要執行的查詢的運算式樹狀架構。查詢處理由源 IQueryable<T> 對象處理。
IQueryable<T>會產生一個查詢運算式樹,並不會將資料直接取出來,但該對象的擴充方法為我們提供了大量的高效協助工具功能,例如判斷資料是否存在的Any(),查詢資料數量的Count(),統計計數屬性的Sum()等,EF會協助我們產生最優的sql,以減少資料庫存取時的效能損耗。這些方法,我們都可以用比較笨拙的編碼方式進行實現,但其效率會低很多。
另外,編碼過程中應儘可能避免使用ToList()方法及GetById()方法,這裡依舊可以參考msdn,但筆者可以直白地去描述:採用這兩種方法後,程式會直接將資料從資料庫中讀取出來,並載入到記憶體,接下來我們可以直接操作這些實實在在的資料,並使用List<T>所擴充的方法。然而這種方案會造成以下效能問題:
1.將未經業務處理的運算式樹狀架構直接用來查詢資料,其資料量太大,資料庫的存取,磁碟的IO,都需要消耗大量的時間和空間。
2.EF支援的導覽屬性,會將相關聯的對象都查出來,屆時龐大的資料又擁有更龐大的分支,讓記憶體和CPU飆升。
為了避免上述問題,EF為我們提供了行之有效方法:
1.查詢初期少用ToList()及GetDtoById()方法。
2.查詢過程中,使用Select()和SelectMany()方法,只選取自己所需要的屬性或對象。
為了看出不規範的編碼方式帶來的效能損耗,我們來看一段例子比較:
1.以下是只選取自己所需的屬性的代碼:
var productSKU = unitOfWork.ProductSku.Get(p => p.Id == item.SKUId)
.Select(p => new { p.Id, p.ProductId, p.Product })
.FirstOrDefault();
提交訂單耗時的:
2.採用GetById()方法:
var productSKU = unitOfWork.ProductSku.GetByID(item.SKUId);
提交訂單耗時:
筆者電腦老舊,該資料在i5 8G ram的電腦中僅需要200ms的時間長度,在效能更強的伺服器上耗時更短。
從對比中我們可以看到:僅僅只修改了一個方法,程式請求的耗時竟有將近一倍的誤差!如果我們的代碼中充斥著這種懶惰的不規範寫法,在高並發情境下,系統會被拖得很慢,甚至會出現程式報錯。
接下來,我們說下拋開EF架構的做法,擅長sql編程的園友,可能會不屑EF的提供的各種方案,直接寫sql,採用ADO不是更快嗎?的確,直接運行sql會讓程式更快,ADO的速度是大家所認可的。EF也支援大家使用直接編寫sql的方式:
var productSKU = dbContext.Database.SqlQuery<ProductSku>(sqlStr);
如果sql編程功力深厚,筆者是非常支援這種編程方案的。但EF架構也有其天生的優勢:
1.EF架構讓更多的初級軟體從業者更快地學習和編寫程式
2.EF提供的完善的擴充方法,協助軟體從業人員實現各種功能
3.規範編寫的EF C#代碼,並不會比原生的sql慢太多
這讓筆者想起C#.net與Java程式員之間的矛盾^_^。筆者因為機緣巧合,也寫過一段時間的Java。
二者的設計理念確實有所不同:
1.C#.net讓初學者更快地入門,提供了更多的類庫和方法,其出色的IDE讓編程人員省心省力。且C#.net已經開源。
2.Java則需要編程人員做更多的思考,自己配置環境變數,自己敲命令列,讓編程人員在思考的過程中加深對電腦原理、作業系統和軟體工程的認識。
毫無疑問:兩者都是當代最出色的進階程式語言(PHP,Python,JavaScript等的同行勿噴^_^)與其花時間爭論誰才是最好的語言,不如兼而學之。
以上是本次效能最佳化介紹的基礎篇,後續會為大家帶來資料庫層面的最佳化經驗及體會。
C# 大型電商項目效能最佳化(一)