剛開始的時候沒有太在意,但是隨著系統的發布,這種初次請求,或者閒置若干時間後第一次請求的漫長等待使得App的體驗很差,很多時候App加載好半天數據都沒過來。如果前端沒處理好,還會導致App的假死。所以就花了點功夫研究下什麼原因導致。
剛開始的時候,還以為是WebService的框架出了問題。後面使用App,通過Fiddler看到了,某次請求db.竟然長達6s中,並且每次導致請求超時都出現在db.訪問這一塊,這顯然不正常。早期,我們訪問數據庫使用的是原始的ADO.NET 執行SQL語句,如果有參數的話,參數化防止SQL注入,然後基本的增刪查,通過批量生成存儲過程實現,並在底層框架層記錄了每次服務的訪問語句和次數,這樣便於調優。起初認為這樣性能是墜吼的,但是隨著規模的擴大,效率太低,所以就切換到了Entity Framework上,通過自動生成實體,然後通過LINQ的方式來實現增刪改查,這樣效率快很多。
意識到是數據訪問的問題之後,開始在查找Entity Framework第一次啟動比較慢的問題,然後在網上找了一下,發現很多人都遇到過同樣的問題。解決方案也比較成熟,比如Pre-Generated Mapping Views,NGen等等,下面就逐個來說明,其實這些在網上也有很多,我這裡記錄一下作為自己以後備查。
下面提到的方法,有很多需要Entity Framework版本的支持,所以我們在使用Entity Framework的時候,最好使用最新版本。
Pre-Generated Mapping Views
關於使用EntityFramework的注意事項在Performance Considerations for Entity Framework 這篇文章中有詳細介紹,其中生成視圖操作耗時比較多,在Entity Framework執行查詢或者對數據庫進行寫操作的時候,必須生成一些映射視圖來訪問數據庫,這些映射視圖是一系列對數據庫中對象的抽象聲明,這些數據同時也是app domain的緩存元數據的一部分,在同一應用程序作用域裡面,創建多個數據庫訪問上下文時可以重用這部分對象。因為在第一次查詢的時候,生成映射視圖是比較耗時的,所以關於這一點,具體的詳細操作可以查看msdn上的Pre-Generated Mapping Views,這篇文章提供了兩種預先生成映射視圖的方法,一種是在Visual Studio中,通過安裝EF Power Tools 插件來生成(以下圖片,來自msdn)。
這種方式不受EF的版本限制。
網站還提供了第二種通過代碼的方式來預先生成視圖,這必須要求EF是6.0及以上版本。這也是博客園這篇文章 來,給Entity Framework熱熱身所使用的方法。一般的,如果是Web站點,可以放在App_Start中來初始化:
protected void Application_Start(object sender, EventArgs e) { //預熱EntityFramework using (var dbcontext = new mcccEntities()) { var objectContext = ((IObjectContextAdapter)dbcontext).ObjectContext; var mappingCollection = (StorageMappingItemCollection)objectContext.MetadataWorkspace.GetItemCollection(DataSpace.CSSpace); mappingCollection.GenerateViews(new List<EdmSchemaError>()); } }
經過以上處理,大概程序初次查詢db從之前的6s下降到了3s左右。
除了以上優化之外,對於EF6.0及之後的版本,可以使用NGen處理來進一步提高速度。
相關優化可以查看這篇文章Improving Startup Performance with NGen (EF6 Onwards),下面這條方法,主要是針對EF6及以上版本的,因為低於這個版本的自帶該特性,在這篇文章裡說的很清楚“在6.0之前的EF中,EF的運行時核心類庫也是.NET框架的一部分,其本地映像在.NET 核心類庫加載時自動加載,在6.0及之後的版本,EF整個運行時已經被集成到EntityFramework NuGet包中,本地映像需要使用NGen工具來生成才能達到類似的效果”。
提到這裡,首先要說一下NGen這個工具的作用以及為什麼能夠加快應用程序的啟動性能。.NET 框架支持為托管應用或者程序集生成本地映像文件來幫助應用程序更快啟動和在一些情況下減少內存占用。在應用程序執行之前,通過將托管代碼程序集翻譯為包含本地機器指令的文件,能夠減少.NET JIT編譯器在應用程序啟動的時候,生成本地指令代碼這一過程,從而能夠加快應用程序啟動。
使用NGen也很簡單
1:以管理員身份啟動控制台cmd程序
2:切換到本機.NET 工具目錄下:
對於32位機器,通常在%WINDIR%\Microsoft.NET\Framework\v4.0.30319\下
對於64位機器,通常在 %WINDIR%\Microsoft.NET\Framework64\v4.0.30319\下
3:然後執行 ngen install 加上程序集的路徑和名稱,即可。
比如在我的機器上,可以看到如下:
經過這一操作,首次訪問db的速度終於控制到了500ms以內。
以上是EF的優化,解決了首次部署之後,第一次訪問數據庫的問題,對於應用程序放置一會兒,再次請求由於線程池回收導致再次訪問變慢的問題,通過設置IIS解決。
將服務或者站點部署到IIS上之後,在對應的線程池裡有兩個地方可以設置,如下圖:
這樣設置之後就可以解決第二個問題。
本文簡單介紹了優化EntityFramework初次啟動速度的方法,以及為防止IIS線程超時閒置,以及例常線程回收導致的初次運行時間過長的解決方法,希望對您解決上述問題有所幫助。