這兩天遇到一個奇怪的問題,通過 EF/EF Core 查詢數(shù)據(jù)庫速度奇慢,先是在傳統(tǒng)的 ASP.NET 項目中遇到(用的是EF6.0),后來將該項目遷移至
ASP.NET Core 也是同樣的問題(用的是EF Core 2.2.2)。
問題觸發(fā)的條件是所查詢的字段中存儲了很大的字符串(有400多萬個字符),查詢耗時竟然要40s左右(對,是40秒),CPU消耗也很高,2核CPU消耗50%-80%左右,而換成
Dapper 則沒這個問題。
通過 EF Core 的 Debug 日志跟蹤發(fā)現(xiàn),耗時發(fā)生在執(zhí)行完 DbCommand 與 dispose DbDataReader
<https://github.com/aspnet/EntityFrameworkCore/blob/release/2.2/src/EFCore.Relational/Storage/RelationalDataReader.cs#L138>
之間:
2019-02-23 15:46:27.026 [Information] Executed DbCommand ("4"ms)
[Parameters=[""], CommandType='Text', CommandTimeout='30']" 2019-02-23
15:47:06.859 [Debug] A data reader was disposed.
通過日志跟蹤信息看,很容易會懷疑耗時可能發(fā)生在 ADO.NET DataReader 讀取數(shù)據(jù)時,但這個懷疑與 Dapper 查詢正常矛盾,而且 CPU
消耗高也說明耗時不是出現(xiàn)在 IO 層面。
后來在 stackoverflow 上找到了線索 Poor performance when loading entity with large
string property using Entity Framework
<https://stackoverflow.com/questions/40496012/poor-performance-when-loading-entity-with-large-string-property-using-entity-fra>
I had the same issue yesterday. What I did find out is that async operations
with Entity Framework is broken or at least very slow. Try using the same
operations synchronously
當時看到了這個線索,有點不相信,異步竟然會引起這個問題,不是默認都使用異步嗎?只是抱著試試看的心理將代碼中的 ToListAsync() 改為
ToList() ,結果卻讓人大吃一驚,多次測試,查詢耗時在 100-500 ms 之間,快了 100 多倍。
更新
觸發(fā)這個問題有 3 個條件:
1)讀取的字符串很大
2)使用 DbCommand.ExecuteReaderAsync 異步方法讀取
3)調用 ExecuteReaderAsync 時沒有給 behavior 參數(shù)傳值 CommandBehavior.SequentialAccess
在 Dapper 中沒有出現(xiàn)問題是因為 Dapper 中設置了 CommandBehavior.SequentialAccess ,詳見 Dapper
的源代碼SqlMapper.Async.cs#L945
<https://github.com/StackExchange/Dapper/blob/4e62055c5775aced61b848282ed180de0ab62161/Dapper/SqlMapper.Async.cs#L945>
using (var reader = await ExecuteReaderWithFlagsFallbackAsync(cmd, wasClosed,
CommandBehavior.SequentialAccess | CommandBehavior.SingleResult,
command.CancellationToken).ConfigureAwait(false)) { //... }
EF Core 中會出現(xiàn)這個問題是因為 EF Core 調用的是 ExecuteReaderAsync(CancellationToken
cancellationToken) ,沒有設置 CommandBehavior ,詳見 EF Core 的源代碼
RelationalCommand.cs#L292
<https://github.com/aspnet/EntityFrameworkCore/blob/release/2.2/src/EFCore.Relational/Storage/Internal/RelationalCommand.cs#L292>
result = new RelationalDataReader( connection, dbCommand, await
dbCommand.ExecuteReaderAsync(cancellationToken), commandId, Logger);
關于 CommandBehavior.SequentialAccess 詳見微軟官方文檔
<https://docs.microsoft.com/en-us/dotnet/api/system.data.commandbehavior?redirectedfrom=MSDN&view=netcore-2.2>
Provides a way for the DataReader to handle rows that contain columns with
large binary values. Rather than loading the entire row, SequentialAccess
enables the DataReader to load data as a stream. You can then use the GetBytes
or GetChars method to specify a byte location to start the read operation, and
a limited buffer size for the data being returned.
熱門工具 換一換
感谢您访问我们的网站,您可能还对以下资源感兴趣:
调教肉文小说-国产成本人片免费av-空姐av种子无码-在线观看免费午夜视频-综合久久精品激情-国产成人丝袜视频在线观看软件-大芭区三区四区无码-啊啊好爽啊啊插啊用力啊啊-wanch视频网-国产精品成人a免费观看