新的一年,你的老闆或客戶,是否曾和你抱怨公司的網站效能愈來愈差?網站大家都會寫,自從有了 Visual Studio 之後,連你家樓下的正妹小喵和隔壁的王大嬸都會寫 ASP.NET。但同樣的一個畫面,背後的效能卻可能是天差地遠,更惶論多人同時上線的企業網站,而程式員的身價也因此有所差別。本帖提供一些改善網站效能的點子,從硬體、軟體、程式技巧的層面都有,也歡迎大家分享自己的經驗或秘技。
(1) 重新調整或重新設計 DB schema、索引 (index)
一個線上系統的效能不佳,主要原因都是來自於資料庫規劃及 SQL 陳述式層面,至於 .NET 程式撰寫不良都還在其次。
先將資料庫適度地做正規化,如:一個 Table 中,避免把常用的欄位、很少用的欄位,都塞在同一個表中,而影響資料掃描的速度。
應該將很少用的欄位,另切割出來成為另一個表。
----------------------------------------------
(2) 改寫 SQL 陳述式,注意 index 是否在查詢時有真的被用到
* 同樣的功能,一個不良的「關聯子查詢」和良好的「獨立子查詢」,之間的 SQL 效能差距,是不到一秒鐘和好幾分鐘以上的差距。
* 一些 SQL 關鍵詞,只要一出現在 SQL 陳述式中,就可能造成表的「索引 (index)」完全失效或部分失效,變成要整個表去逐行逐列地掃描,
例如: NOT、NOT IN、!=、<>、OR ...等關鍵詞,
還有「LIKE '%關鍵詞'」的模糊查詢,也會造成索引失效,但「LIKE '關鍵詞%'」就不會造成索引失效。
----------------------------------------------
(3) 使用 Native 的 DataProvider
放棄 OleDb,改用 ADO.NET Native 的 DataProvider,如: SqlClient、OracleClient。但若您公司堅持要用 Sybase 這種從 2003 年之後,就不曾更新 DB driver 的資料庫,就只好繼續用效能不佳的 OleDb 去聯機了。
據版工我用 Visual Studio 內建的 stress test 工具,測試 OleDb 和 SqlClient 的效能差距,類比 30 人同時上線,用瀏覽器擷取一萬筆資料,兩者的速度就差了一秒鐘;且當資料庫的資料越多,或越多人同時上線時,效能差距會更明顯。
----------------------------------------------
(4) 用程式或軟體做緩衝
用程式做緩衝,如 ASP.NET 從 1.x 時代,就已內建的 Cache (緩衝) 機制;或用一些第三方的輔助軟體、Framework,這方面若有其它網友知道好用的軟體,亦懇請留言告知。
----------------------------------------------
(5) 用硬體做快取或緩衝、砸錢加裝 AP Server
ITHome - 遊戲基地網頁效能提升的關鍵人物
以下引用自原文:
種種缺失使得網站的使用人數銳減。面對網站一堆問題,陳xx也決定要將網站再次大幅度調整,將之前的網頁程式,以及 SQL 查詢語句全部重寫,他們花了三個月的時間執行。
陳xx還在原本的網頁伺服器,與資料庫伺服器的架構中,加入一組應用程式伺服器,作為網頁伺服器 cache 資料的來源。
改版之後的新網站,搜尋速度提升許多,先前每日的統計資料中,處理速度超過 3 秒的資料超過 50 萬筆;而改版後,每星期超過 3 秒的查詢不到 10 筆,而這少數反應速度不夠快的查詢,也多是內部作業執行大量批處理導致的。
由於原本使用的 L4 Switch 較為老舊,負載量比較差,因此陳xx選擇將它汰換新的裝置,加強負載量;恰好那時正好準備將應用伺服器的架構上線,就藉此機會將網路架構更新。陳xx說,這樣的架構搭配負載較強的 L4 交換器,強化網站的處理效能,並憑藉此抵禦網路攻擊。在此之後,網站依然會受到零星攻擊,但都不會對造成太大影響。
----------------------------------------------
(6) 用硬體做快取或緩衝、砸錢加裝 AP Server
數字之牆 - 網站外銷的個人實踐(二)運營
以下引用自原文:
全盛時期,來自美國 blog 的流量每天達 80 萬次。這個數字其實不高,對程式高手來說是小菜一碟,但筆者是半吊子工程師,知識有限也因此可能程式寫得不好,頻頻被主機供貨商發信警告,要求改善網站系統效能。最後,我決定開發 cache system。
cache system 緩衝系統上線後,將資料庫讀寫,從每天 80 萬次降低到每天 16 萬次。這期間也請高手朋友幫忙進行資料庫結構最佳化,協助很大。筆者在過程中學習到,一個良好的「緩衝系統 (cache system)」對於提供 Widget 功能的網站來說非常重要。
…中間略…
能夠做到隨時搬遷整個網站到另一家主機供貨商,除了程式本身的調整外,還要歸功於網站管理軟體的盛行。在此要推薦的一套稱為 Plesk 的網站管理軟體。有的主機供貨商會直接幫你安裝 Plesk ,免費或另外付費。 Plesk 的所有管理功能都是透過 Web 介面,方便到無以複加,大大降低對技術能力的要求。
除了 Plesk 以外,網站管理軟體還有其它選擇。還有 WHM 加上 cPanel 的組合,也是常見的網站管理解決方案。不過筆者還是比較偏好使用 Plesk,畢竟使用起來容易,也難怪他們的市場佔有率一直是獨大。只是,功力高的工程師可能會喜歡 WHM + cPanel,因為彈性比較大。不論選擇哪一種,都可以協助你節省許多時間。
----------------------------------------------
(7) 加裝實體機器做 Loading Balance (Server Load Balancer)。一些 Server OS 亦內建此類設定功能。
----------------------------------------------
(8) 程式技巧 - ADO.NET
能用 DataReader 就不要用 DataSet / DataTable,前者讀取速度快又不耗記憶體;後者雖較有彈性,但速度較慢又會每個使用者消耗許多記憶體。若您連 DropDownList 控制項的資料來源,都用 SqlDataSource 控制項的預設值 - DataSet,則當頁面裡塞了一堆下拉選單時,效能當然會受影響。
但前提是程式員對 ADO.NET 要有一定程度的瞭解,若只會用 Visual Studio 透過圖形介面,拖拉 TableAdapter、DataTable、.xsd 就免談了。
若為 DataTable 建立 Primary Key,DataTable 會建立一個索引,追蹤新增到 DataTable 中的資料是否符合此條件約束 (constraint)。ADO.NET 2.0 會使用 algorithm 的「紅/黑樹狀結構演算法 (Red-Black Tree,是一種「平衡樹」演算法) 去處理索引,讓 DataTable 的資料量大時,較方便維護索引;但缺點是建立索引時會降低一些效能。
此外,資料庫的訪問和撈值,應該盡量在一次 DB connection 做完,一個 connection 可搭配多個 DbCommand 對象使用,不用每次都一個 DbConnection 配一個 DbCommand。
在此推薦一本不錯的 ADO.NET 原文書:
Programming Microsoft ADO.NET 2.0 Core Reference:
http://www.amazon.com/Programming-Microsoft%C2%AE-ADO-NET-Core-Reference/dp/073562206X/ref=sr_1_1?ie=UTF8&s=books&qid=1230971264&sr=1-1
有探討許多市面上書籍少見的深入內容,像 Oracle + ADO.NET 的各種應用、Connection Pooling 的特性、各種的資料庫 Balk (批次) 作業應用。
----------------------------------------------
(9) 程式技巧 - .NET 文法
* 避免一些書上教的,把 DataTable 或大量資料,直接塞進 Session 裡,此舉在真正要上線的系統必死無疑。Session 在多人同時上線時,記憶體的消耗是很可觀的,因為 Session 是每個使用者各存一份在伺服器的記憶體裡,而非像「緩衝 (cache)」是所有的使用者共用伺服器的一塊記憶體。在很多 ASP.NET 的需求中,可用 HiddenField 控制項或 ViewState 取代 Session。
* 能用「泛型 (Generics)」就不要用舊版的寫法,Generics 除了安全外,亦可避免 .NET 類型在 Boxing / Unboxing 轉型時影響效能,例如:
能用 List<string> 就不要用舊的 ArrayList,能用 Dictionary<TKey,TValue> 就不要用 Hashtable 或跑雙層的迴圈 (loop),因 ArrayList、Hashtable 的 element 屬於 object 類,在儲存或檢索如 int 等「實值型別 (Value Type)」時,會引發 Boxing / Unboxing。
在大多數的情況下,List、Dictionary 等泛型類,擁有較佳的效率,而且是型別安全的。
當然上述前提,是系統要用 .NET 開發,還在靠 ASP 或非 OOP 語言硬撐的舊系統就免談了。
----------------------------------------------
(10) 程式技巧 - 資料庫「事務 (Transaction)」
您是否知道 SQL Server 的預設「事務隔離等級 (Isolation Level)」,是「ReadCommitted」,當您在寫 ADO.NET 用了 SqlTransaction 時,預設是當某個人在修改某一筆記錄時,其它所有讀取這一筆記錄的人,都會被「鎖定 (lock)」住,造成其它全部使用者的瀏覽器都在等待中,無法做其它工作。
而 Oracle 事務的特性,是絕不會有類似無法讀取的情形,至少會用類似 SQL Server 2005 新增的「快照隔離 (Snapshot Isolation)」,讓使用者至少能先讀取到未 Commit 或 Rollback 的記錄,而不用呆坐在瀏覽器前面傻等。
不過 SQL Server 2005 的「快照隔離」預設未啟用。用 SQL Server 開發的系統,若怕使用者被鎖定的問題,可視 project 需求,改用最寬鬆的「ReadUncommitted」事務隔離等級,其特性為不會造成任何鎖定,但可能會造成 Dirty Read。SQL Server 有下列七種「事務隔離等級」,有興趣的網友可去查詢 ADO.NET 書籍或 MSDN Library:
Chaos
ReadCommitted // SQL Server 預設值
ReadUncommitted // 最寬鬆,會有 Dirty Read
RepeatableRead
Serializable // 最嚴,會有大量的鎖定
Snapshot
Unspecified
----------------------------------------------
(11) ASP.NET 分頁
GridView + SqlDataSource 的預設行為,就是每次換頁或排序時,不管資料庫有幾筆記錄都全部重撈一次;當資料庫有一百萬筆資料,就在每個使用者換頁時,都一百萬筆全部重撈出來,此舉消耗了大量的 Web server/ AP server 記憶體、資料庫系統資源、網路頻寬,結果網站效能可想而知。
很多企業內的小型網站,為了省錢,隨便外包給低價搶標的工作室,或沒經驗的學生和 SOHO 族,可能因此埋下了恐怖的後遺症。最可怕的是這些未爆彈,在開發期間和系統剛上線、資料量還很少時,都感覺不出來,有如癌症一樣,會在將來忽然爆發。
----------------------------------------------
(12) ASP.NET AJAX 的 UpdatePanel 控制項不是萬能的
以下引用自 MSDN Magazine:
不論好壞,UpdatePanel 控制項都是 ASP.NET AJAX 社區所喜愛的。我說“好”,是因為 UpdatePanel 使部分頁面呈現變得相當簡單,而說“壞”,是因為它的簡便和易用性是以效率和令人啼笑皆非的頻寬為代價的。
UpdatePanel 可以為一般的網頁帶來 AJAX 神奇的好處,但是它不能提供我們與 AJAX 正常關聯的高效性。例如,您是否知道,當 UpdatePanel 控制項對伺服器執行非同步 AJAX 回調以更新其內容時,這個請求包含了常規 ASP.NET 回傳所包含的一切,其中還包括 ViewState 呢?具有太多 ViewState 的頁面會降低效能,並且具有太多 ViewState 的頁面在 ASP.NET 應用程式中都太常見。
如果您準備使用 UpdatePanel 控制項,您需要清楚您在準備幹什麼。在許多情況下,從效能的角度而言,應用程式最好是不使用 UpdatePanel,而是使用對 WebMethods 或頁面方法的非同步呼叫。
…中間略…
當您使用 UpdatePanel 在一個頁面上執行無閃爍更新時,您可能會認為您在進行高效構建。畢竟,UpdatePanel 使用的是 AJAX,不是嗎?不幸的是,如果您在 UpdatePanel 更新時檢驗一下網路中的通訊,您會發現您根本就沒有儲存什麼東西,至少是在發送的時候沒有儲存。通常在回傳期間傳送到伺服器的 ViewState 資料(與其他資料)也會在 UpdatePanel 回調期間傳送。事實上,來自 UpdatePanel 的非同步 XML-HTTP 要求中所增長的資料,幾乎與在標準 ASP .NET 回傳中增長的資料相同。下面是有關 ASP.NET AJAX 不可告人的秘密:UpdatePanel 雖便於使用,但是通訊效率不高。
幾乎沒有什麼辦法可讓您提高 UpdatePanel 的效率,但是您可以放棄使用 UpdatePanel,並轉而使用 ASP.NET AJAX 的其他功能來更新頁面內容,它不僅同樣流暢,而且更加高效。它只需要多一點點力氣,但是最後的結果往往讓人覺得是值得付出的,因為您可以大大降低在用戶端與伺服器之間傳輸的資料量。
----------------------------------------------
(13) Design Patterns
雖然「設計模式」不是為解決效能問題而誕生的,但可適度防止沒經驗的新人做出蠢事。多學一些 .NET 技術敵營注重的系統架構、OOAD、Design Patterns 和相關的 Framework,對提升自己的身價和薪資也有協助。
----------------------------------------------