我們注意到一些非常小的Web服務調用花費的時間比我們預期的要長得多。我們做了一些調查,並安排了一些定時器,並將其縮小到創建實體框架6 DbContext的實例。不是查詢本身,只是創建上下文。自從我做了一些日誌記錄之後,平均需要多長時間才能創建一個DbContext的實例,看起來大概是50ms。創建實體框架實例上下文環境下降
應用程序被預熱後,上下文創建並不慢。在應用程序回收後,它以2-4ms開始(這是我們在開發環境中看到的)。上下文創建似乎隨着時間的推移而減慢。在接下來的幾個小時內,它將爬升到50-80ms的範圍並保持平穩。
我們的上下文是一個相當大的代碼優先的上下文,有大約300個實體 - 包括一些實體之間的一些非常複雜的關係。我們正在運行EF 6.1.3。我們正在做「每個請求一個上下文」,但對於我們的大多數Web API調用,它只能執行一個或兩個查詢。創建一個60 + ms的上下文,然後執行一個1ms的查詢有點不滿意。我們每分鐘有大約10k個請求,所以我們不是一個輕微使用的站點。
下面是我們所看到的快照。時代在MS,大潮是一個可以回收應用領域的部署。每行是4個不同的Web服務器之一。注意它並不總是相同的服務器。
我沒有采取內存轉儲,試圖充實發生了什麼事情,這裏是堆統計:
00007ffadddd1d60 70821 2266272 System.Reflection.Emit.GenericFieldInfo
00007ffae02e88a8 29885 2390800 System.Linq.Enumerable+WhereSelectListIterator`2[[NewRelic.Agent.Core.WireModels.MetricDataWireModel, NewRelic.Agent.Core],[System.Single, mscorlib]]
00007ffadda7c1a0 1462 2654992 System.Collections.Concurrent.ConcurrentDictionary`2+Node[[System.Object, mscorlib],[System.Object, mscorlib]][]
00007ffadd4eccf8 83298 2715168 System.RuntimeType[]
00007ffadd4e37c8 24667 2762704 System.Reflection.Emit.DynamicMethod
00007ffadd573180 30013 3121352 System.Web.Caching.CacheEntry
00007ffadd2dc5b8 35089 3348512 System.String[]
00007ffadd6734b8 35233 3382368 System.RuntimeMethodInfoStub
00007ffadddbf0a0 24667 3749384 System.Reflection.Emit.DynamicILGenerator
00007ffae04491d8 67611 4327104 System.Data.Entity.Core.Metadata.Edm.MetadataProperty
00007ffadd4edaf0 57264 4581120 System.Signature
00007ffadd4dfa18 204161 4899864 System.RuntimeMethodHandle
00007ffadd4ee2c0 41900 5028000 System.Reflection.RuntimeParameterInfo
00007ffae0c9e990 21560 5346880 System.Data.SqlClient._SqlMetaData
00007ffae0442398 79504 5724288 System.Data.Entity.Core.Metadata.Edm.TypeUsage
00007ffadd432898 88807 8685476 System.Int32[]
00007ffadd433868 9985 9560880 System.Collections.Hashtable+bucket[]
00007ffadd4e3160 92105 10315760 System.Reflection.RuntimeMethodInfo
00007ffadd266668 493622 11846928 System.Object
00007ffadd2dc770 33965 16336068 System.Char[]
00007ffadd26bff8 121618 17335832 System.Object[]
00007ffadd2df8c0 168529 68677312 System.Byte[]
00007ffadd2d4d08 581057 127721734 System.String
0000019cf59e37d0 166894 143731666 Free
Total 5529765 objects
Fragmented blocks larger than 0.5 MB:
Addr Size Followed by
0000019ef63f2140 2.9MB 0000019ef66cfb40 Free
0000019f36614dc8 2.8MB 0000019f368d6670 System.Data.Entity.Core.Query.InternalTrees.SimpleColumnMap[]
0000019f764817f8 0.8MB 0000019f76550768 Free
0000019fb63a9ca8 0.6MB 0000019fb644eb38 System.Data.Entity.Core.Common.Utils.Set`1[[System.Data.Entity.Core.Metadata.Edm.EntitySet, EntityFramework]]
000001a0f6449328 0.7MB 000001a0f64f9b48 System.String
000001a0f65e35e8 0.5MB 000001a0f666e2a0 System.Collections.Hashtable+bucket[]
000001a1764e8ae0 0.7MB 000001a17659d050 System.RuntimeMethodHandle
000001a3b6430fd8 0.8MB 000001a3b6501aa0 Free
000001a4f62c05c8 0.7MB 000001a4f636e8a8 Free
000001a6762e2300 0.6MB 000001a676372c38 System.String
000001a7761b5650 0.6MB 000001a776259598 System.String
000001a8763c4bc0 2.3MB 000001a8766083a8 System.String
000001a876686f48 1.4MB 000001a8767f9178 System.String
000001a9f62adc90 0.7MB 000001a9f63653c0 System.String
000001aa362b8220 0.6MB 000001aa36358798 Free
這似乎是相當多的元數據和typeusage的。
的事情,我們已經試過:
- 創建一個簡單的測試工具來複制。它失敗了,我的猜測是因爲我們沒有改變流量,或者改變了查詢運行的類型。只是加載上下文並反覆執行一些查詢並不會導致時間增加。
- 我們已經明顯減少了上下文,它是500個實體,現在是300個。速度沒有變化。我的猜測是因爲我們根本沒有使用這200個實體。
- (編輯)我們使用SimpleInjector創建我們的「每個請求的上下文」。驗證它不是SimpleInjector我已經通過new'in up了一個Context的實例。創建時間同樣緩慢。
- (編輯)我們沒有EF。沒有產生任何影響。
我們可以進一步調查什麼?我瞭解EF使用的緩存非常廣泛,可以加快速度。在緩存中做更多事情,是否會減慢上下文的創建速度?有沒有一種方法可以確切地看到緩存中的內容,以充實那裏的任何奇怪的東西?有誰知道我們可以做些什麼來加速上下文創建?
更新 - 17年5月30日
我把EF6源和編譯我們自己的版本在某些時刻堅持。我們運行一個很受歡迎的網站,以便收集定時的大量信息是棘手的,我沒有得到儘可能我想要的,但基本上我們發現,所有的放緩從this method
public void ForceOSpaceLoadingForKnownEntityTypes()
{
if (!_oSpaceLoadingForced)
{
// Attempting to get o-space data for types that are not mapped is expensive so
// only try to do it once.
_oSpaceLoadingForced = true;
Initialize();
foreach (var set in _genericSets.Values.Union(_nonGenericSets.Values))
{
set.InternalSet.TryInitialize();
}
}
}
的每次迭代到來該foreach命中每個由DBSet在我們的上下文中定義的實體。每次迭代相對較短.1-3毫秒,但是當您添加254個實體時,我們將它累加起來。我們仍然沒有弄清楚它爲什麼一開始就很快並且放慢速度。
'DbContext'實例創建你的唯一減速? – mxmissile
我的直覺說,「每個請求的一個上下文」就是這裏的問題。爲什麼不是「每個會話一個背景」,至少? – Alex
這裏是討論DbContext生活時間的另一個線程http://stackoverflow.com/questions/10777630/questions-about-entity-framework-context-lifetime – Alex