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

问题描述 投票:0回答:2

我正在使用 EF Core 6 与 SQL Server 数据库。 有时,SELECT 查询的执行时间超过 30 秒,并超时。

如果我执行由 EF Core 生成的完全相同的 SQL(使用完全相同的参数,在相同的数据库上,超时后仅几秒),它需要不到一秒。

在整个期间,DB server 都在用CPU < 30%.

在 SQL Server Management Studio 上运行 SQL 查询,我可以看到执行计划是理想的(即它使用索引等)。

所以我担心可能会有一些锁定阻止数据库返回查询结果。

有没有办法指定 EF Core 例如查询的隔离级别,甚至一些并发/锁定策略?

在我的特定场景中,可以进行脏读或不完全最新的读取,因为我们有适当的程序来在后续轮查询中检索干净的数据。

谢谢

c# sql-server concurrency entity-framework-core locking
2个回答
4
投票

当 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 needs 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;
            }
        }
    }
}

-1
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.