Entity Framework Core 6.0 预览4 性能改进
起因
微軟在Build2021開發者大會上,發布Entity?Framework?Core 6.0(簡稱EFCore 6)預覽第四版,號稱是性能版本,性能提升主要對于Entity?Framework?Core 5.
性能改進:
EFCore 5.0和EFCore 6.0基準測試,提升了70%.
在查詢時,比EFCore5.0提升了31%.
內存改進了不少,減少43%.減少內存分配,就是減少GC回收的次數,降低GC回收的壓力.
運行時性能
在以往的幾年,EF?Core團隊主要以功能和穩定為中心,縮小與EF(Entity?Framework)的功能差距.當EF?Core團隊在給EF Core 6.0規劃時以性能(高性能和低延遲)為中心.EF?Core 6.0以Dapper(這里不給Dapper過多的介紹)為目標.
在發布EF?Core 6.0?pre 4的時候,EF Core 6與Dapper在TechEmpower基準測試中的差距由原來的55%,縮小到5% .
當然在TechEmpower基準測試是特定的環境(有針對性的優化,比如加大DbContext池的默認大小改為1024),基準測試是在高性能和低延遲的環境下執行的,EF Core也關閉了跟蹤查詢(關閉后,無法對實體進行修改),這些和線上生產環境不同.因為EF?Core在執行查詢時(在線上生產環境)的性能瓶頸,主要是在網絡和數據庫的IO上.
DbContext創建和回收
使用EF?Core的人都知道DbContext是操作數據的入口,通常直接實例化DbContext,或者通過依賴注入的方式獲得DbContext的實例,使用它進行一些數據庫操作(工作單元),使用結束后進行釋放.
通常直接實例化DbContext是一個很好的方式.在有些環境需要和容器(依賴注入)集成到一起,又需要高性能的時候,協調DbContext和容器一起工作是需要時間的.所以在EF?Core團隊加入DbContextPool,對DbContext進行池化管理.DbContext在使用完畢后放入到DbContextPool允許被重新使用.
通過DbContextPool(DbContext池)減少DbContext創建和回收,減少DbContext的創建,降低堆內存分配,也就減少GC回收的次數.減少GC的壓力.
在EF?Core 6.0調整池中的實例數量,從原來128個改為1024個.對于大多數程序128個DbContext實例是足夠使用的.但在TechEmpower?測試是有些不太夠,將實例數量改為1024,TechEmpower?基準吞吐率增加了23%.
DbContext不是萬能的,EF?Core在查詢時是要使用Ado.Net對象的,如DbConnection/DbCommand及DbDataReader,以及使用這些對象中的內部對象,但這些對象在內存使用比較高時,就需要對這些對象進行回收,這些對象回收是有序的.因此,每個DbContext都有專屬的自己實例(指DbConnection等),重用的時候也是使用這些對象.這種可重用的對象圖以DbContext為根節點延伸到Npgsql(PostgreSQL),這次優化在執行查詢時內存分配減少了23%.
日志調整
EF?Core有很多可以擴展的地方,允許用戶獲得關于查詢執行的各個階段的信息,并將其掛鉤到查詢執行的各個階段,比如說要對數據庫執行Sql查詢時,EF?Core調用DbCommand.ExcuteReaderAsync(可以記錄Sql執行前和運行的時間),也可以寫一個DiagnosticSource事件,調用用戶配置的攔截器,可以讓用戶操作之前的命令,EF Core雖然提供了一組靈活切強大的擴展,但這些擴展的開銷并不低.一次查詢有7個事件,每個事件都有2個擴展(執行前和執行后).一直在檢查是否啟用日志和注冊DiagnosticListener?事件,這些開銷都可以在性能分析中體現出.
在日志這一塊改進,是檢查是否開啟任何類型的日志或者攔截器,如果沒有啟用,默認就對該事件和日志禁用1秒.禁用后,基準吞吐率提升了7%,這為使用EF?Core的用戶,帶來了很好的收益,如果在程序的某一時間注冊DiagnosticListener,那么只需要等待一秒鐘的時間,就可以看到結果.
線程安全檢查
在EF?Core中DbContext并不是線程安全的,DbContext內部封裝了數據庫連接對象,數據庫連接對象本身也不允許并發使用.所以我們對DbContext的實例對象不應該在有并發的時候使用,但在EF?Core內部有一個線程安全檢測機制,但檢測到有跨線程使用,會拋出異常,這樣方便解決DbContext跨線程使用帶來的問題.
EF?Core在查詢時線程安全機制也是會進行檢測的,在異步查詢的時候,會使用AsyncLocal將狀態加鎖并傳遞給查詢線程,AsyncLocal會在堆上進行比較多的內存分配,有太多的堆內存分配,導致GC也會進行頻繁回收,最終導致EF?Core的吞吐率下降.
EF?Core不知道是什么時候檢測是否線程安全,也不知道什么時候不需要進行線程安全,在EF?Core 6.0加入了線程安全檢查機制的關閉參數,通過DbContextOptionsBuilder的EnableThreadSafetyChecks方法進行設置.本想在測試項目測試一下呢?發現在EF?Core 6.0?preview 4的具體數據庫的實現依賴.都還是EF?Core?5.0的.只有Npgsql.EntityFrameworkCore.PostgreSQL內部依賴有EF?Core 6.0的,SqlServer和MySQL暫時沒有更新.
qqio
在關閉線程安全檢測后,在TechEmpower?性能基準測試提升了6.7%.
總結
以上是生活随笔為你收集整理的Entity Framework Core 6.0 预览4 性能改进的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【视频回放】Best of Build
- 下一篇: 日志组件DotNetCommon.Fil