繁体   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