SQL Server EF Core有时需要花费大量时间来执行SELECT查询

nkhmeac6  于 2022-11-28  发布在  其他
关注(0)|答案(2)|浏览(236)

我使用的是EF Core 6与SQL Server DB。有时,SELECT查询的执行时间超过30秒,并且超时。
如果我执行EF Core生成的完全相同的SQL(使用完全相同的参数,在相同的数据库上,在超时后几秒),所需时间不到一秒。
在整个周期内,DB服务器的CPU保持在〈30%。
在SQL Server Management Studio上运行SQL查询,我可以看到执行计划是理想的(即,它使用索引等)。
因此,我担心可能会有一些锁定阻止DB返回查询结果。
是否有办法指定EF Core,例如查询的隔离级别,或者甚至某些并发/锁定策略?
在我的特定场景中,有脏的或不完全最新的读取是可以的,因为我们有适当的过程来在后续的一轮查询中检索干净的数据。
谢谢

gab6jxml

gab6jxml1#

SQL Server何时会因为Parameter Sniffing而减慢查询速度已不是新问题。可以通过将参数转换为常量或在查询末尾添加OPTION(RECOMPILE)来解决此问题。此答案将DbCommandInterceptor添加到DbContextOptions,并将OPTION(RECOMPILE)提示追加到特定查询。

配置数据库上下文

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 Server发送以下SQL语句:

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;
            }
        }
    }
}
3zwjbxry

3zwjbxry2#

不建议重新编译,这可能会损害良好运行的应用程序。对于大规模数据,我个人建议使用DBQuery或Simple ADO.net

相关问题