簡體   English   中英

EF Core 有時需要花費大量時間來執行 SELECT 查詢

[英]EF Core takes a lot of time, sometimes, to perform SELECT query

我正在使用 EF Core 6 與 SQL 服務器數據庫。 有時,SELECT 查詢的執行時間超過 30 秒,並且超時。

如果我執行由 EF Core 生成的完全相同的 SQL(使用完全相同的參數,在相同的數據庫上,超時后僅幾秒),它需要不到一秒的時間。

在整個期間,DB 服務器的 CPU 使用率 < 30%。

在 SQL Server Management Studio 上運行 SQL 查詢,我可以看到執行計划是理想的(即它使用索引等)。

所以我擔心可能會有一些鎖定阻止數據庫返回查詢結果。

有沒有辦法向 EF Core 指定查詢的隔離級別,甚至一些並發/鎖定策略?

在我的具體場景中,有臟讀或不完全最新的讀取是可以的,因為我們有適當的程序來在后續輪查詢中檢索干凈的數據。

謝謝

當 SQL Server 可能因為Parameter Sniffing而減慢查詢時,這不是一個新問題。 可以通過將參數轉換為常量或在查詢末尾添加OPTION(RECOMPILE)來解決問題。 此答案將DbCommandInterceptor添加到DbContextOptions並將OPTION(RECOMPILE)提示附加到特定查詢。

配置 DbContext

builder.UseSqlServer(connectionString)
    .UseRecompileExtensions(); // registering interceptor 

如何在查詢中使用:

var name = "SomeName";
var result = context.SomeItems
    .Where(x => x.Name == name)
    .WithRecompile() // it marks query as a query which need RECOMPILE query hint
    .ToList();

然后將以下 SQL 發送到 SQL Server:

SELECT [s].[Id], [s].[Name]
FROM [SomeItems] AS [s]
WHERE [s].[Name] = @__name_0
OPTION(RECOMPILE)

以及擴展的實現:

我已將所有內容放入一個靜態類中以簡化答案。 在 EF Core 6 上測試,但也適用於較低版本。

public static class RecompileExtensions
{
    private const string RecompileTag = "recompile_query_tag";
    private const string RecompileComment = "-- " + RecompileTag + "\r\n";

    public static DbContextOptionsBuilder UseRecompileExtensions(this DbContextOptionsBuilder builder)
    {
        return builder.AddInterceptors(RecompileInterceptor.Instance);
    }

    public static IQueryable<T> WithRecompile<T>(this IQueryable<T> query)
    {
        return query.TagWith(RecompileTag);
    }

    private class RecompileInterceptor : DbCommandInterceptor
    {
        public static RecompileInterceptor Instance = new();

        public override ValueTask<InterceptionResult<DbDataReader>> ReaderExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result,
            CancellationToken cancellationToken = new CancellationToken())
        {
            CorrectCommand(command);

            return base.ReaderExecutingAsync(command, eventData, result, cancellationToken);
        }

        public override InterceptionResult<DbDataReader> ReaderExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result)
        {
            CorrectCommand(command);

            return base.ReaderExecuting(command, eventData, result);
        }

        private static void CorrectCommand(DbCommand command)
        {
            var newQuery = command.CommandText.Replace(RecompileComment, "");

            // if query was changed, we have to append RECOMPILE option
            if (!ReferenceEquals(newQuery, command.CommandText))
            {
                // remove rest of the comment
                if (newQuery.StartsWith("\r\n"))
                    newQuery = newQuery.Substring(2);

                newQuery += "\r\nOPTION(RECOMPILE)";
                command.CommandText = newQuery;
            }
        }
    }
}

不建議重新編譯,它可能會損害正常運行的應用程序。 對於大規模數據,我個人推薦使用DBQuery或者Simple ADO.net

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM